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

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;

//初始化 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 默认为空

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;

}

}

在这里插入图片描述

二、二叉树

====================================================================

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 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(“前序遍历”); //12354

binaryTree.preOrder();

System.out.println(“中序遍历”); //21534

binaryTree.infixOrder();

System.out.println(“后序遍历”); //25431

binaryTree.postOrder();

//前序遍历

System.out.println(“前序遍历”);

HeroNode resNode = binaryTree.preOrderSearch(5);

if (resNode != null){

System.out.printf(“找到了,信息为 no=%d name=%s”,resNode.getNo(),resNode.getName());

最后

总而言之,面试官问来问去,问的那些Redis知识点也就这么多吧,复习的不够到位,知识点掌握不够熟练,所以面试才会卡壳。将这些Redis面试知识解析以及我整理的一些学习笔记分享出来给大家参考学习

还有更多学习笔记面试资料也分享如下:

都是“Redis惹的祸”,害我差点挂在美团三面,真是“虚惊一场”

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(“前序遍历”); //12354

binaryTree.preOrder();

System.out.println(“中序遍历”); //21534

binaryTree.infixOrder();

System.out.println(“后序遍历”); //25431

binaryTree.postOrder();

//前序遍历

System.out.println(“前序遍历”);

HeroNode resNode = binaryTree.preOrderSearch(5);

if (resNode != null){

System.out.printf(“找到了,信息为 no=%d name=%s”,resNode.getNo(),resNode.getName());

最后

总而言之,面试官问来问去,问的那些Redis知识点也就这么多吧,复习的不够到位,知识点掌握不够熟练,所以面试才会卡壳。将这些Redis面试知识解析以及我整理的一些学习笔记分享出来给大家参考学习

还有更多学习笔记面试资料也分享如下:

[外链图片转存中…(img-ogdlJUE8-1714510145629)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值