第十章 哈希表和二叉树

文章目录

  • 第十章 哈希表和二叉树
  • 一、哈希表
    • 1.介绍
    • 2.代码实现
  • 二、二叉树
    • 1.介绍
    • 2.遍历二叉树
    • 3.查找二叉树
    • 4.二叉树删除节点
    • 5.二叉树综合实例

一、哈希表

1.介绍

散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表

给定表 M,存在函数 f (key),对任意给定的关键值 key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表 M 为哈希(Hash)表,函数 f (key) 为哈希(Hash) 函数

基本概念

  • 若关键字为 k,则其值存放在 f(k) 的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系 f 为散列函数,按这个思想建立的表为散列表
  • 对不同的关键字可能得到同一散列地址,即 k1 ≠ k2,而 f(k1) = f(k2),这种现象称为冲突(Collision)。具有相同函数值的关键字对该散列函数来说称作同义词。综上所述,根据散列函数 f(k) 和处理冲突的方法将一组关键字映射到一个有限的连续的地址集(区间)上,并以关键字在地址集中的 ”像“ 作为记录在表中的存储位置,这种表便称为散列表,这一映射过程称为散列造表或散列,所得的存储位置称散列地址
  • 若对于关键字集合中的任一个关键字,经散列函数映像到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数(Uniform Hash function),这就是使关键字经过散列函数得到一个 ”随机的地址“,从而减少冲突

哈希表可以通过数组 + 链表实现,也可以通过数组 + 二叉树实现

提出问题:
有一个公司,当有新员工来报到时,要求将该员工的信息加入(id,性别,年龄,住址…),当输入该员工的 id 时,要求查找到该员工的所有信息,要求使用哈希表

数据结构设计:

class HashTab{EmplinkedList[];   //链表empLinkedListArr;   //链表数组add();list();find();散列函数();   //决定 id 对应到哪个链表
}class EmpLinkedList{Emp head = null;  //头指针,指向当前链表的第一个雇员add();list();find();
}class Emp{id;name;address;
}

2.代码实现

package com.sisyphus.hashtab;import java.util.Scanner;/*** @Description: 哈希表$* @Param: $* @return: $* @Author: Sisyphus* @Date: 7/22$*/
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;//初始化 empLinkedListArrayempLinkedListArray = 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);}//遍历所有的链表,遍历 hashtabpublic 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 默认为空public Emp(int id, String name) {super();this.id = id;this.name = name;}
}//创建 EmpLinkedList,表示链表
class EmpLinkedList{//头指针,执行第一个 Emp,因此我们这个链表的 head 是直接指向第一个 Empprivate 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,如果没有找到,就返回 nullpublic 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;}
}

二、二叉树

1.介绍

二叉树(binary tree)是指树中节点的度不大于 2 的有序树,它是一种最简单且最重要的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树

相关术语:

  1. 结点:包含一个数据元素及若干指向子树分支的信息
  2. 结点的度:一个结点拥有子树的数目称为结点的度
  3. 叶子结点:也称为终端结点,没有子树的结点或者度为零的结点
  4. 分支结点:也称为非终端结点,度不为零的结点称为非终端结点
  5. 树的度:树中所有结点的度的最大值
  6. 结点的层次:从根结点开始,假设根结点为第 1 层,根结点的子结点为第 2 层,依此类推,如果某一个结点位于第 L 层,则其子结点位于 L + 1 层
  7. 树的深度:也称为树的高度,树中所有结点的层次最大值称为树的深度
  8. 有序树:如果树中各棵子树的次序是有先后次序,则称该树为有序树
  9. 无序树:如果树中各棵子树的次序没有先后次序,则称该树为无序树
    10.森林:由 m (m ≥ 0) 棵互不相交的树构成一片森林。如果把一棵非空的树的根结点删除,则该树就变成了一片森林,森林中的树由原来根结点的各棵子树构成

特殊类型:

  • 满二叉树:指的是深度为 k 且含有 2^k - 1 个结点的二叉树
  • 完全二叉树:设二叉树的深度为 h,除第 h 层外,其他各层的结点数都达到最大个数,且第 h 层所有的结点都连续集中在最左边。也就是说,树中所含的 n 个结点和满二叉树中编号为 1 至 n 的结点一一对应
    • 第 n 个元素的左子结点为 2n
    • 第 n 个元素的右子结点为 2n + 1
    • 第 n 个元素的父结点为 (n - 1)/ 2

性质:

  1. 二叉树的第 i 层上至多有 2^(i - 1) 个点(i ≥ 1)
  2. 深度为 h 的二叉树中至多含有 2^h - 1 个结点
  3. 若在任意一棵二叉树中,有 n0 个叶子结点,有 n2 个度为 2 的结点,则必有 n0 = n2 + 1
  4. 具有 n 个 结点的完全二叉树深度为 log2(x + 1) ,其中 x 表示不大于 n 的最大整数
  5. 若对一棵有 n 个结点的完全二叉树进行顺序编号(1 ≤ i ≤ n),那么,对于编号为 i (i ≥ 1)的结点:
    当 i = 1 时,该结点为根,它无双亲结点
    当 i > 1 时,该结点的双亲结点的编号为 i / 2
    若 2i < n,则有编号为 2i 的左结点,否则没有左结点
    若 2i + 1 ≤ n,则有编号为 2i + 1 的右节点,否则没有右节点

2.遍历二叉树

前序遍历二叉树

思路:
访问到一个结点后就打印该结点,并继续遍历其左右子树

遍历顺序:
GDAFEMHZ

代码实现:

//前序遍历
public void preOrder(){//打印父结点System.out.println(this);//递归向左子树前序遍历if (this.left != null){this.left.preOrder();}//递归向右子树前序遍历if (this.right != null){this.right.preOrder();}
}

中序遍历二叉树

思路:
访问到一个结点后将其暂存,遍历完左子树后,再打印该结点的值,然后遍历右子树

遍历顺序:
ADEFGHMZ

代码实现:

//中序遍历
public void infixOrder(){//递归向左子树中序遍历if (this.left != null){this.left.infixOrder();}//打印父结点System.out.println(this);//递归向右子树中序遍历if (this.right != null){this.right.infixOrder();}
}

后续遍历二叉树

思路:
访问到一个结点后将其暂存,遍历完左右子树后,再打印该结点的值

遍历顺序:
AEFDHZMG

代码实现:

//后序遍历
public  void postOrder(){//递归向左子树后序遍历if (this.left != null){this.left.postOrder();}//递归向右子树后序遍历if (this.right != null){this.right.postOrder();}//打印父结点System.out.println(this);
}

层次遍历二叉树

思路:
建立一个循环队列,先将二叉树根结点入队列,然后出队列,访问根结点,如果它有左子树,则将左子树的根结点入队:如果它有右子树,则将右子树的根结点入队。然后出队列,对出队结点访问,如此反复,直到队列为空为止

遍历顺序:
ADMAFHZ

代码实现:

//层次遍历public void levelOrder(){ArrayDeque<HeroNode> queue = new ArrayDeque<>(20);//首先将根结点加入队列中queue.add(this);//遍历二叉树while(!queue.isEmpty()){HeroNode tempNode = queue.poll();System.out.println(tempNode);if (tempNode.left != null){queue.add(tempNode.left);}if (tempNode.right != null){queue.add(tempNode.right);}}}

3.查找二叉树

前序查找二叉树

思路:

  1. 首先拿根结点进行比较,如果相等,直接返回,否则左递归前序查找
  2. 如果左递归找到,直接返回,否则右递归前序查找
  3. 如果右递归找到返回,否则返回空

查到 H 结点
比较次数:
7

代码实现:

//前序遍历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;}

中序查找二叉树

思路:

  1. 首先左递归查找,如果找到,直接返回,否则和根结点比较
  2. 如果比较相等,直接返回,否则右递归中序查找
  3. 如果右递归找到则返回,否则返回空

查找 H 结点
比较次数:
6

代码实现:

//中序遍历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;}

后序查找二叉树

思路:

  1. 首先左递归查找,如果找到,直接返回,否则右递归中序查找
  2. 如果右递归找到,直接返回,否则和根结点比较
  3. 如果比较不相等则返回空

查找 H 结点
比较次数:
5

代码实现:

//后序遍历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;}

4.二叉树删除节点

思路:

  • 如果删除的结点就是 root结点,则等价将二叉树置空
  • 如果删除的结点是非叶子结点,则删除该子树
  • 如果删除的结点是叶子结点,则删除该结点
  • 因为我们的二叉树是单向的,所以只能判断当前结点的子结点是不是需要删除结点
  1. 如果当前结点的左子结点不为空,并且左子结点就是要删除的结点,执行 this.left = null,并 return
  2. 如果当前结点的右子结点不为空,并且左子结点就是要删除的结点,执行 this.right = null,并 return
  3. 如果第 1 步和第 2 步没有 return,那么我们就需要向左子树进行递归删除
  4. 如果第 3 步仍没有 return,则向右子树进行递归删除

5.二叉树综合实例

代码:

package com.sisyphus.tree;import java.util.ArrayDeque;/*** @Description: 二叉树$* @Param: $* @return: $* @Author: Sisyphus* @Date: 7/23$*/
public class BinaryTreeDemo {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("前序遍历"); //12354binaryTree.preOrder();System.out.println("中序遍历"); //21534binaryTree.infixOrder();System.out.println("后序遍历"); //25431binaryTree.postOrder();//前序遍历System.out.println("前序遍历");HeroNode resNode = binaryTree.preOrderSearch(5);if (resNode != null){System.out.printf("找到了,信息为 no=%d name=%s",resNode.getNo(),resNode.getName());System.out.println();}else{System.out.printf("没有找到 no=%d 的英雄",5);System.out.println();}//中序遍历System.out.println("中序遍历");resNode = binaryTree.infixOrderSearch(5);if (resNode != null){System.out.printf("找到了,信息为 no=%d name=%s",resNode.getNo(),resNode.getName());System.out.println();}else{System.out.printf("没有找到 no=%d 的英雄",5);System.out.println();}//后序遍历System.out.println("后序遍历");resNode = binaryTree.postOrderSearch(5);if (resNode != null){System.out.printf("找到了,信息为 no=%d name=%s",resNode.getNo(),resNode.getName());System.out.println();}else{System.out.printf("没有找到 no=%d 的英雄",5);System.out.println();}System.out.println("===================================================");//测试删除结点System.out.println("删除前,前序遍历");binaryTree.preOrder();binaryTree.delNode(5);System.out.println("删除后,前序遍历");binaryTree.preOrder();System.out.println("======================================================");System.out.println("层次遍历");binaryTree.levelOrder();}
}//定义 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 void levelOrder(){if (this.root != null){this.root.levelOrder();}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 root.postOrderSearch(no);}else{return null;}}
}//先创建 HeroNode 结点
class HeroNode{private int no;private String name;private HeroNode left;private HeroNode right;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;}@Overridepublic String toString() {return "HeroNode{" +"no=" + no +", name='" + name + '\'' +'}';}//递归删除结点public void delNode(int no){if (this.left != null && this.left.no == no){this.left = null;return;}if (this.right != null && this.right.no == no){this.right = null;return;}if (this.left != null){this.left.delNode(no);}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);}//层次遍历public void levelOrder(){ArrayDeque<HeroNode> queue = new ArrayDeque<>(20);//首先将根结点加入队列中queue.add(this);//遍历二叉树while(!queue.isEmpty()){HeroNode tempNode = queue.poll();System.out.println(tempNode);if (tempNode.left != null){queue.add(tempNode.left);}if (tempNode.right != null){queue.add(tempNode.right);}}}//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//查找//前序遍历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;}
}

【Java数据结构与算法】第十章 哈希表和二叉树相关推荐

  1. 数据结构与算法五:哈希表-哈希函数设计原则-哈希冲突解决方案

    一.哈希表的定义: 二.哈希表举例: 哈希函数就是映射关系 三.哈希表应用举例: Leetcode上第387题: 思路:通过s.charAt(i)-'a'将字符串中的字符映射成hash表,出现一次,在 ...

  2. 数据结构与算法——深入理解哈希表

    文章目录 哈希表 优点与缺点 哈希化 冲突 基于线性探测的开放地址法 插入 查找 删除 性能问题 基于二次探测的开放地址法 基于再哈希法的开放地址法 链地址法 哈希表实现方法的选择 参考 哈希表 哈希 ...

  3. 【数据结构与算法篇】 哈希表原理、底层实现剖析

    一个在校大二学生,在CSDN记录自我成长!!!最近在自学数据结构和算法时,学到了哈希表,有很多地方都不明白.如何使用哈希表?原理是什么?如何工作的?我们如何设计哈希表?等等,所以就在网络上查了相关博客 ...

  4. 数据结构与算法笔记:哈希表——力扣389

    原题: 给定两个字符串 s 和 t ,它们只包含小写字母.字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母.请找出在 t 中被添加的字母. 思路: 首先咱们抛开编程知识,就当它是咱们日常 ...

  5. python【数据结构与算法】深入浅出哈希表

    哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列 ...

  6. 【JS数据结构与算法】认识哈希表

    目录 一.什么是哈希表? 二.哈希表的优势. 三.哈希表与数组相比较. 四.数据的存储. 方法一:ASCII编码之和 方法二:幂的连乘 五.方法二改进--哈希化 六.解决冲突 一:链地址法(拉链法). ...

  7. C++(数据结构与算法):30---散列(哈希)表的介绍(散列函数、散列冲突、散列溢出)

    一.散列(哈希)介绍 散列使用一个散列函数(也称为哈希函数)把字典的数对映射到一个散列表(也称为哈希表)的具体位置 散列的存储与查找: 查找:如果数对p的关键字是k,散列函数为f,那么在理想的情况下, ...

  8. 数据结构和算法(第九章哈希表)

    文章目录 前言 一.哈希表的基本介绍 二.哈希表的实际应用场景 三.使用哈希表来管理雇员信息 1.分析 2.表示一个雇员的代码 3.EmpLinkedList,表示链表(链表里面存放数据)的代码 4. ...

  9. Java数据结构与算法(25) - ch11哈希(双重哈希)

    {TODO} 转载于:https://www.cnblogs.com/thlzhf/p/4089027.html

最新文章

  1. 计算机等级考试属于什么培训,计算机等级是什么
  2. 正则表达式—leetcode10
  3. 沈阳招聘.NET(C#)高级软件工程师
  4. Video.js实现rtmp视频播放
  5. vue 写bean_vue+jsp+删除一个bean
  6. 我经历的IT公司面试及离职感受(转)
  7. 利用锁机制解决商品表和库存表并发问题
  8. 【题解专栏】南华大学19级软卓选拔赛题解
  9. sql盲注 解决_解决SQL盲注和跨站脚本攻击
  10. PDF文件转化成mobi格式,亲测kindle或者iReader可用!
  11. 淘宝付邮试用Chrome桌面提醒插件(含源码)
  12. macOS Monterey系列原版光盘镜像文件制作
  13. 新概念英语第三册51-60课(转)
  14. Nginx负载均衡配置
  15. OI-wiki 算法基础 模拟 NOIP2014 生活大爆炸版石头剪刀布 python
  16. 红米4 android 8,【红米4(标准版) 安卓6.0.1线刷包】MIUI V8.1.4.0.MCECNDI稳定版 可解账号锁...
  17. Leetcode 143 链表对折
  18. 微软裁员1万人,遣散费约54亿元,人均获赔54万!
  19. 知识普及篇——动手做foc无刷电机电子调速器
  20. 运动小插件(有氧运动)

热门文章

  1. webbrowser 百度列表点击_百度信息流推广后台完整的实操流程分享
  2. 回声检测仿真信号matlab,杭州oracle培训入门
  3. poj2793 素数和
  4. dispatch_after中时间的计算
  5. [xPlugins] 开发中常用富文本编辑器介绍
  6. win7 远程桌面 复制粘贴
  7. MSSOAP与WebService
  8. google+stackoverflow_解决stackOverflow打开慢的问题
  9. _Linux后台开发6大常用的开源库,让你在同行中脱颖而出
  10. php 数组 键值 初始化,PHP 自定义键值数组