美文网首页
数据结构与算法分析三 (哈希表和树)

数据结构与算法分析三 (哈希表和树)

作者: Teemo_fca4 | 来源:发表于2019-07-29 11:11 被阅读0次

哈希表


image.png
public class HashTabDemo {

    public static void main(String[] args) {
        
        //创建哈希表
        HashTab hashTab = new HashTab(7);
        
        //写一个简单的菜单
        String key = "";
        Scanner scanner = new Scanner(System.in);
        while(true) {
            System.out.println("add:  添加雇员");
            System.out.println("list: 显示雇员");
            System.out.println("find: 查找雇员");
            System.out.println("exit: 退出系统");
            
            key = scanner.next();
            switch (key) {
            case "add":
                System.out.println("输入id");
                int id = scanner.nextInt();
                System.out.println("输入名字");
                String name = scanner.next();
                //创建 雇员
                Emp emp = new Emp(id, name);
                hashTab.add(emp);
                break;
            case "list":
                hashTab.list();
                break;
            case "find":
                System.out.println("请输入要查找的id");
                id = scanner.nextInt();
                hashTab.findEmpById(id);
                break;
            case "exit":
                scanner.close();
                System.exit(0);
            default:
                break;
            }
        }
        
    }

}

//创建HashTab 管理多条链表
class HashTab {
    private EmpLinkedList[] empLinkedListArray;
    private int size; //表示有多少条链表
    
    //构造器
    public HashTab(int size) {
        this.size = size;
        //初始化empLinkedListArray
        empLinkedListArray = new EmpLinkedList[size];
        //?留一个坑, 这时不要分别初始化每个链表
        for(int i = 0; i < size; i++) {
            empLinkedListArray[i] = new EmpLinkedList();
        }
    }
    
    //添加雇员
    public void add(Emp emp) {
        //根据员工的id ,得到该员工应当添加到哪条链表
        int empLinkedListNO = hashFun(emp.id);
        //将emp 添加到对应的链表中
        empLinkedListArray[empLinkedListNO].add(emp);
        
    }
    //遍历所有的链表,遍历hashtab
    public void list() {
        for(int i = 0; i < size; i++) {
            empLinkedListArray[i].list(i);
        }
    }
    
    //根据输入的id,查找雇员
    public void findEmpById(int id) {
        //使用散列函数确定到哪条链表查找
        int empLinkedListNO = hashFun(id);
        Emp emp = empLinkedListArray[empLinkedListNO].findEmpById(id);
        if(emp != null) {//找到
            System.out.printf("在第%d条链表中找到 雇员 id = %d\n", (empLinkedListNO + 1), id);
        }else{
            System.out.println("在哈希表中,没有找到该雇员~");
        }
    }
    
    //编写散列函数, 使用一个简单取模法
    public int hashFun(int id) {
        return id % size;
    }
    
    
}

//表示一个雇员
class Emp {
    public int id;
    public String name;
    public Emp next; //next 默认为 null
    public Emp(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
}

//创建EmpLinkedList ,表示链表
class EmpLinkedList {
    //头指针,执行第一个Emp,因此我们这个链表的head 是直接指向第一个Emp
    private Emp head; //默认null
    
    //添加雇员到链表
    //说明
    //1. 假定,当添加雇员时,id 是自增长,即id的分配总是从小到大
    //   因此我们将该雇员直接加入到本链表的最后即可
    public void add(Emp emp) {
        //如果是添加第一个雇员
        if(head == null) {
            head = emp;
            return;
        }
        //如果不是第一个雇员,则使用一个辅助的指针,帮助定位到最后
        Emp curEmp = head;
        while(true) {
            if(curEmp.next == null) {//说明到链表最后
                break;
            }
            curEmp = curEmp.next; //后移
        }
        //退出时直接将emp 加入链表
        curEmp.next = emp;
    }
    
    //遍历链表的雇员信息
    public void list(int no) {
        if(head == null) { //说明链表为空
            System.out.println("第 "+(no+1)+" 链表为空");
            return;
        }
        System.out.print("第 "+(no+1)+" 链表的信息为");
        Emp curEmp = head; //辅助指针
        while(true) {
            System.out.printf(" => id=%d name=%s\t", curEmp.id, curEmp.name);
            if(curEmp.next == null) {//说明curEmp已经是最后结点
                break;
            }
            curEmp = curEmp.next; //后移,遍历
        }
        System.out.println();
    }
    
    //根据id查找雇员
    //如果查找到,就返回Emp, 如果没有找到,就返回null
    public Emp findEmpById(int id) {
        //判断链表是否为空
        if(head == null) {
            System.out.println("链表为空");
            return null;
        }
        //辅助指针
        Emp curEmp = head;
        while(true) {
            if(curEmp.id == id) {//找到
                break;//这时curEmp就指向要查找的雇员
            }
            //退出
            if(curEmp.next == null) {//说明遍历当前链表没有找到该雇员
                curEmp = null;
                break;
            }
            curEmp = curEmp.next;//以后
        }
        return curEmp;
    }

}

树的常用术语,节点 根节点 父节点 子节点 层 等等

image.png
二叉树的概念
image.png
满二叉树 与 完全二叉树 image.png
前序遍历 ,中序遍历 ,与后序遍历
image.png
三种遍历思路的实现
image.png
前序 后序 与 中序 查找
image.png
二叉树的节点删除 :如果节点下有节点数据 暂时当前删除其子节点 , 后面不会这样
image.png image.png
   public static void main(String[] args) {
       //先需要创建一颗二叉树
       BinaryTree binaryTree = new BinaryTree();
       //创建需要的结点
       HeroNode root = new HeroNode(1, "宋江");
       HeroNode node2 = new HeroNode(2, "吴用");
       HeroNode node3 = new HeroNode(3, "卢俊义");
       HeroNode node4 = new HeroNode(4, "林冲");
       HeroNode node5 = new HeroNode(5, "关胜");
       
       //说明,我们先手动创建该二叉树,后面我们学习递归的方式创建二叉树
       root.setLeft(node2);
       root.setRight(node3);
       node3.setRight(node4);
       node3.setLeft(node5);
       binaryTree.setRoot(root);
       
       //测试
       System.out.println("前序遍历"); // 1,2,3,5,4
       binaryTree.preOrder();
       
       //测试 
       System.out.println("中序遍历");
       binaryTree.infixOrder(); // 2,1,5,3,4
       
       System.out.println("后序遍历");
       binaryTree.postOrder(); // 2,5,4,3,1
       
       //前序遍历
       //前序遍历的次数 :4 
//      System.out.println("前序遍历方式~~~");
//      HeroNode resNode = binaryTree.preOrderSearch(5);
//      if (resNode != null) {
//          System.out.printf("找到了,信息为 no=%d name=%s", resNode.getNo(), resNode.getName());
//      } else {
//          System.out.printf("没有找到 no = %d 的英雄", 5);
//      }
       
       //中序遍历查找
       //中序遍历3次
//      System.out.println("中序遍历方式~~~");
//      HeroNode resNode = binaryTree.infixOrderSearch(5);
//      if (resNode != null) {
//          System.out.printf("找到了,信息为 no=%d name=%s", resNode.getNo(), resNode.getName());
//      } else {
//          System.out.printf("没有找到 no = %d 的英雄", 5);
//      }
       
       //后序遍历查找
       //后序遍历查找的次数  2次
//      System.out.println("后序遍历方式~~~");
//      HeroNode resNode = binaryTree.postOrderSearch(5);
//      if (resNode != null) {
//          System.out.printf("找到了,信息为 no=%d name=%s", resNode.getNo(), resNode.getName());
//      } else {
//          System.out.printf("没有找到 no = %d 的英雄", 5);
//      }
       
       //测试一把删除结点
       
//      System.out.println("删除前,前序遍历");
//      binaryTree.preOrder(); //  1,2,3,5,4
//      binaryTree.delNode(5);
//      //binaryTree.delNode(3);
//      System.out.println("删除后,前序遍历");
//      binaryTree.preOrder(); // 1,2,3,4
       
       
       
   }

}

//定义BinaryTree 二叉树
class BinaryTree {
   private HeroNode root;

   public void setRoot(HeroNode root) {
       this.root = root;
   }
   
   //删除结点
   public void delNode(int no) {
       if(root != null) {
           //如果只有一个root结点, 这里立即判断root是不是就是要删除结点
           if(root.getNo() == no) {
               root = null;
           } else {
               //递归删除
               root.delNode(no);
           }
       }else{
           System.out.println("空树,不能删除~");
       }
   }
   //前序遍历
   public void preOrder() {
       if(this.root != null) {
           this.root.preOrder();
       }else {
           System.out.println("二叉树为空,无法遍历");
       }
   }
   
   //中序遍历
   public void infixOrder() {
       if(this.root != null) {
           this.root.infixOrder();
       }else {
           System.out.println("二叉树为空,无法遍历");
       }
   }
   //后序遍历
   public void postOrder() {
       if(this.root != null) {
           this.root.postOrder();
       }else {
           System.out.println("二叉树为空,无法遍历");
       }
   }
   
   //前序遍历
   public HeroNode preOrderSearch(int no) {
       if(root != null) {
           return root.preOrderSearch(no);
       } else {
           return null;
       }
   }
   //中序遍历
   public HeroNode infixOrderSearch(int no) {
       if(root != null) {
           return root.infixOrderSearch(no);
       }else {
           return null;
       }
   }
   //后序遍历
   public HeroNode postOrderSearch(int no) {
       if(root != null) {
           return this.root.postOrderSearch(no);
       }else {
           return null;
       }
   }
}

//先创建HeroNode 结点
class HeroNode {
   private int no;
   private String name;
   private HeroNode left; //默认null
   private HeroNode right; //默认null
   public HeroNode(int no, String name) {
       this.no = no;
       this.name = name;
   }
   public int getNo() {
       return no;
   }
   public void setNo(int no) {
       this.no = no;
   }
   public String getName() {
       return name;
   }
   public void setName(String name) {
       this.name = name;
   }
   public HeroNode getLeft() {
       return left;
   }
   public void setLeft(HeroNode left) {
       this.left = left;
   }
   public HeroNode getRight() {
       return right;
   }
   public void setRight(HeroNode right) {
       this.right = right;
   }
   @Override
   public String toString() {
       return "HeroNode [no=" + no + ", name=" + name + "]";
   }
   
   //递归删除结点
   //1.如果删除的节点是叶子节点,则删除该节点
   //2.如果删除的节点是非叶子节点,则删除该子树
   public void delNode(int no) {
       
       //思路
       /*
        *  1. 因为我们的二叉树是单向的,所以我们是判断当前结点的子结点是否需要删除结点,而不能去判断当前这个结点是不是需要删除结点.
           2. 如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)
           3. 如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)
           4. 如果第2和第3步没有删除结点,那么我们就需要向左子树进行递归删除
           5.  如果第4步也没有删除结点,则应当向右子树进行递归删除.

        */
       //2. 如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)
       if(this.left != null && this.left.no == no) {
           this.left = null;
           return;
       }
       //3.如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)
       if(this.right != null && this.right.no == no) {
           this.right = null;
           return;
       }
       //4.我们就需要向左子树进行递归删除
       if(this.left != null) {
           this.left.delNode(no);
       }
       //5.则应当向右子树进行递归删除
       if(this.right != null) {
           this.right.delNode(no);
       }
   }
   
   //编写前序遍历的方法
   public void preOrder() {
       System.out.println(this); //先输出父结点
       //递归向左子树前序遍历
       if(this.left != null) {
           this.left.preOrder();
       }
       //递归向右子树前序遍历
       if(this.right != null) {
           this.right.preOrder();
       }
   }
   //中序遍历
   public void infixOrder() {
       
       //递归向左子树中序遍历
       if(this.left != null) {
           this.left.infixOrder();
       }
       //输出父结点
       System.out.println(this);
       //递归向右子树中序遍历
       if(this.right != null) {
           this.right.infixOrder();
       }
   }
   //后序遍历
   public void postOrder() {
       if(this.left != null) {
           this.left.postOrder();
       }
       if(this.right != null) {
           this.right.postOrder();
       }
       System.out.println(this);
   }
   
   //前序遍历查找
   /**
    * 
    * @param no 查找no
    * @return 如果找到就返回该Node ,如果没有找到返回 null
    */
   public HeroNode preOrderSearch(int no) {
       System.out.println("进入前序遍历");
       //比较当前结点是不是
       if(this.no == no) {
           return this;
       }
       //1.则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找
       //2.如果左递归前序查找,找到结点,则返回
       HeroNode resNode = null;
       if(this.left != null) {
           resNode = this.left.preOrderSearch(no);
       }
       if(resNode != null) {//说明我们左子树找到
           return resNode;
       }
       //1.左递归前序查找,找到结点,则返回,否继续判断,
       //2.当前的结点的右子节点是否为空,如果不空,则继续向右递归前序查找
       if(this.right != null) {
           resNode = this.right.preOrderSearch(no);
       }
       return resNode;
   }
   
   //中序遍历查找
   public HeroNode infixOrderSearch(int no) {
       //判断当前结点的左子节点是否为空,如果不为空,则递归中序查找
       HeroNode resNode = null;
       if(this.left != null) {
           resNode = this.left.infixOrderSearch(no);
       }
       if(resNode != null) {
           return resNode;
       }
       System.out.println("进入中序查找");
       //如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前结点
       if(this.no == no) { 
           return this;
       }
       //否则继续进行右递归的中序查找
       if(this.right != null) {
           resNode = this.right.infixOrderSearch(no);
       }
       return resNode;
       
   }
   
   //后序遍历查找
   public HeroNode postOrderSearch(int no) {
       
       //判断当前结点的左子节点是否为空,如果不为空,则递归后序查找
       HeroNode resNode = null;
       if(this.left != null) {
           resNode = this.left.postOrderSearch(no);
       }
       if(resNode != null) {//说明在左子树找到
           return resNode;
       }
       
       //如果左子树没有找到,则向右子树递归进行后序遍历查找
       if(this.right != null) {
           resNode = this.right.postOrderSearch(no);
       }
       if(resNode != null) {
           return resNode;
       }
       System.out.println("进入后序查找");
       //如果左右子树都没有找到,就比较当前结点是不是
       if(this.no == no) {
           return this;
       }
       return resNode;
   }
}

相关文章

  • 数据结构与算法分析三 (哈希表和树)

    哈希表 树 树的常用术语,节点 根节点 父节点 子节点 层 等等

  • 排序算法

    什么是算法 书籍推荐 《数据结构与算法分析》 表、栈和队列 树 散列(hash) 优先队列(堆) 排序 定义 问题...

  • leetcode和牛客网刷题

    在上学时学过《数据结构和算法》这门课,当时学习了数组、链表、哈希表、二叉树、图等数据结构,还有排序算法、二分查找、...

  • 【MySQL学习】No.3 SQL索引

    索引的出现是为了提高查询效率,常用的主要包含三种数据结构:哈希表、有序数组和搜索树。 哈希表 哈希表是一种以键 -...

  • 「Redis源码解读」—数据结构(二)哈希表

    Redis的字典使用哈希表作为底层实现 知识点 1.数据结构 哈希节点 哈希表 类型处理函数 2.哈希 哈希算法 ...

  • iOS标准库中常用数据结构和算法之哈希表

    上一篇: iOS标准库中常用数据结构和算法之二叉排序树 ?哈希表 系统提供一个全局的key为字符串的哈希表。并提供...

  • MySQL索引简述--哈希索引

    哈希算法 哈希算法时间复杂度为O(1),且不只存在于索引中,每个数据库应用中都存在该数据结构。 哈希表 哈希表也为...

  • 树、二叉树

        前面几节分析了表的数据结构以及算法,包括顺序表、链表、hash表以及栈和队列。后面的几节我们讲开始分析树。...

  • MYSQL学习笔记3 索引

    三种常见的数据结构: 哈希表、有序数组和搜索树。 哈希表这种结构适用于只有等值查询的场景,通过key 算出位置(可...

  • 九、哈希表

    这一节记录一下哈希表的学习 持续更新中...数据结构与算法系列博客:一、数据结构与算法概述二、数组及LeetCod...

网友评论

      本文标题:数据结构与算法分析三 (哈希表和树)

      本文链接:https://www.haomeiwen.com/subject/ascmkctx.html