Java进阶——数据结构与算法之哈希表与树的入门小结(四)

news2024/11/15 5:01:46

文章大纲

  • 引言
  • 一、哈希表
    • 1、哈希表概述
    • 2、哈希表的基本设计思想
    • 3、JDK中的哈希表的设计思想概述
  • 二、树
    • 1、树的概述
    • 2、树的特点
    • 3、树的相关术语
    • 4、树的存储结构
      • 4.1、双亲表示法
      • 4.2、孩子兄弟表示法:
      • 4.3、孩子表示法:
      • 4.4、双亲孩子表示法
  • 三、二叉树
    • 1、二叉树的性质
    • 3、二叉树的类型
    • 4、二叉树的存储结构
    • 5、二叉树的遍历
      • 5.1、先序遍历(根结点 ---> 左子树 ---> 右子树)
      • 5.2、中序遍历(左子树 ---> 根结点 ---> 右子树)
      • 5.3、后序遍历(左子树 ---> 右子树 ---> 根结点)
    • 6、二叉树的链式实现

引言

前面介绍了线性表结构中的顺序存储结构寻址容易但是插入删除性能不好,而链式结构插入删除性能较好寻址却欠佳,那么有没有“鱼和熊掌兼可得”的结构呢?

一、哈希表

1、哈希表概述

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

2、哈希表的基本设计思想

以一个实例简单分析下哈希表的设计思想,首先我们有一组数据{14,19,5,7,21,1,13,0,18}需要存储,暂且设计散列表长度为预存储数组的长度,最后再设计一个映射公式即散列函数表达式f(x)= x mod 13,经过映射之后无论多大的数据都能确保经过散列函数计算之后在散列表下标范围内(当然我们用的hashCode要比这复杂得多不过核心思想是一样的)
在这里插入图片描述
当然以上是一种简单的哈希表基本设计思想,适用于特定的场景,比如说通讯录、QQ好友列表、微信好友列表、字典等有上限的且重复不多的数据存储。

3、JDK中的哈希表的设计思想概述

JDK中采用的是所谓的拉链法,JDK1.7之前采用的是数组+单链表的结构,而在之后改成了数组+单链表+红黑树的结构,基本思想是一致的,区别在于解决哈希冲突的方案,JDK1.7之前散列表中存储的元素上一个单链表,当发生哈希冲突时,直接把值添加到链表尾部,这样就解决了哈希冲突,但是为了避免单链表长度过长,在JDK1.8之后设置来一个阈值,当链表长度超过这个阈值时则自动转为红黑树进行存储。
在这里插入图片描述

二、树

1、树的概述

树是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,不过它是根朝上,而叶朝下的,当n>0时根结点是唯一的,不可能存在多个根结点,数据结构中的树只能有一个根结点;.m>0时,子树的个数没有限制,但它们一定是互不相交的。单个结点是一棵树,树根就是该结点本身。

2、树的特点

  • 每个结点有零个或多个子结点
  • 没有父结点的结点称为根结点
  • 每一个非根结点有且只有一个父结点
  • 除了根结点外,每个子结点可以分为多个不相交的子树

3、树的相关术语

  • 结点的度——一个结点含有的子树的个数称为该结点的度;
  • 叶结点或终端结点——度为0的结点称为叶结点;
  • 非终端结点或分支结点——度不为0的结点;
  • 双亲结点或父结点——若一个结点含有子结点,则这个结点称为其子结点的父结点
  • 孩子结点或子结点——一个结点含有的子树的根结点称为该结点的子结点
  • 兄弟结点——具有相同父结点的结点互称为兄弟结点;
  • 树的度——一棵树中,最大的结点的度称为树的度;
  • 结点的层次——从根开始定义起,根为第1层,根的子结点为第2层,以此类推;
  • 树的高度或深度——树中结点的最大层次
  • 堂兄弟结点——双亲在同一层的结点互为堂兄弟;
  • 结点的祖先——从根到该结点所经分支上的所有结点
  • 子孙——以某结点为根的子树中任一结点都称为该结点的子孙。
  • 森林——由m(m>=0)棵互不相交的树的集合称为森林;

在这里插入图片描述

4、树的存储结构

树的存储结构有有四种:双亲表示法孩子兄弟表示法孩子表示法双亲孩子表示法

4.1、双亲表示法

把所有节点都村存在一组连续空间中,同时在每个结点中,附设一个指示器指示其双亲结点到链表中的位置。
节点结构为

data(数据域)parent(指针域)
存储结点的数据信息存储该结点的双亲所在数组中的下标
在这里插入图片描述

根节点的指针域为-1,根据结点的parent指针很容易找到它的双亲结点。所用时间复杂度为O(1),直到parent为-1时,表示找到了树结点的根。

4.2、孩子兄弟表示法:

任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此我们设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟,结点结构:

data(数据域)firstchild(指针域)rightsib(指针域)
data是数据域存储该结点的第一个孩子结点的存储地址存储该结点的右兄弟结点的存储地址
在这里插入图片描述

4.3、孩子表示法:

把每个结点的孩子结点排列起来,以单链表作存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空,然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中。为此设计两种结点结构:一种是孩子链表的孩子结点:

child(数据域)next(指针域)
存储某个结点在表头数组中的下标存储指向某结点的下一个孩子结点的指针
另一种是表头数组的表头结点:
data(数据域)firstchild(头指针域)
存储某个结点的数据信息存储该结点的孩子链表的头指针

在这里插入图片描述

4.4、双亲孩子表示法

在这里插入图片描述

三、二叉树

二叉树是**每个结点最多有两个子树的树结构,**通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树不是树的一种特殊情形,尽管其与树有许多相似之处,但树和二叉树有两个主要差别,树中结点的最大度数没有限制,而二叉树结点的最大度数为2, 树的结点无左、右之分,而二叉树的结点有左、右之分

1、二叉树的性质

  • 在非空二叉树中,第i层的结点总数不超过2的(i-1)次方 , i>=1
  • 深度为h的二叉树最多有 2的h次方减1个结点(h>=1),最少有h个结点
  • 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1
  • 具有n个结点的完全二叉树的深度为 [log2 N]+1 (注:[ ]表示向下取整)
  • 有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:若I为结点编号则 如果I>1,则其父结点的编号为I/2;如果2I<=N,则其左孩子(即左子树的根结点)的编号为2I;若2I>N,则无左孩子;如果2I+1<=N,则其右孩子的结点编号为2I+1;若2I+1>N,则无右孩子。
  • 给定N个节点,能构成h(N)种不同的二叉树。h(N)为卡特兰数的第N项。h(n)=C(2*n,n)/(n+1)。
  • 设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和J=I+2i [2]

3、二叉树的类型

  • 完全二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
  • 满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
  • 平衡二叉树——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

4、二叉树的存储结构

在这里插入图片描述

5、二叉树的遍历

遍历是对树的一种最基本的运算,所谓遍历二叉树就是按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。假设L、D、R分别表示遍历左子树、访问根结点和遍历右子树, 则对一棵二叉树的遍历有三种情况:DLR(称为先根次序遍历),LDR(称为中根次序遍历),LRD (称为后根次序遍历)。
在这里插入图片描述

5.1、先序遍历(根结点 —> 左子树 —> 右子树)

首先访问根,再先序遍历左子树,最后先序遍历右子树。

public void postOrderTraversal(TreeNode root) {
	if (root != null) {
		return;
	}
	postOrderTraversa1(root.left);
	postOrderTraversa1(root.right);
	System.out.print(root.val+"  ");
}

非递归版本

public void preOrderTraversval2(TreeNode root) {
		LinkedList<TreeNode> stack = new LinkedList<>();
		TreeNode pNode = root;
		while (pNode != null || !stack.isEmpty()) {
			if (pNode != null) {
				System.out.print(pNode.val+"  ");
				stack.push(pNode);
				pNode = pNode.left;
			} else { //pNode == null && !stack.isEmpty()
				TreeNode node = stack.pop();
				pNode = node.right;
			}
		}
	}

5.2、中序遍历(左子树 —> 根结点 —> 右子树)

首先中序遍历左子树,再访问根,最后中序遍历右子树

public void postOrderTraversal(TreeNode root) {
	if (root != null) {
		return;
	}
	postOrderTraversa1(root.left);
	postOrderTraversa1(root.right);
	System.out.print(root.val+"  ");
}

5.3、后序遍历(左子树 —> 右子树 —> 根结点)

首先后序遍历左子树,再后序遍历右子树,最后访问根,即

public void postOrderTraversal(TreeNode root) {
	if (root != null) {
		return;
	}
	postOrderTraversa1(root.left);
	postOrderTraversa1(root.right);
	System.out.print(root.val+"  ");
}

6、二叉树的链式实现

package com.crazymo.ndk.tree;

public class BinaryTree<E> {

    public TreeNode<E> root;

    public BinaryTree(E data){
        root=new TreeNode<>(data,null,null);
    }

    public void createTree(){
        TreeNode<String> nodeB=new TreeNode<String>("B",null,null);
        TreeNode<String> nodeC=new TreeNode<String>("C",null,null);
        TreeNode<String> nodeD=new TreeNode<String>("D",null,null);
        TreeNode<String> nodeE=new TreeNode<String>("E",null,null);
        TreeNode<String> nodeF=new TreeNode<String>("F",null,null);
        TreeNode<String> nodeG=new TreeNode<String>("G",null,null);
        TreeNode<String> nodeH=new TreeNode<String>("H",null,null);
        TreeNode<String> nodeJ=new TreeNode<String>("J",null,null);
        TreeNode<String> nodeI=new TreeNode<String>("I",null,null);
        root.leftChild= (TreeNode<E>) nodeB;
        root.rightChild= (TreeNode<E>) nodeC;
        nodeB.leftChild=nodeD;
        nodeC.leftChild=nodeE;
        nodeC.rightChild=nodeF;
        nodeD.leftChild=nodeG;
        nodeD.rightChild=nodeH;
        nodeE.rightChild=nodeJ;
        nodeH.leftChild=nodeI;

    }

    /**
     * 中序访问树的所有节点
     */
    public void midOrderTraverse(TreeNode<E> root){//逻辑
        if(root==null){
            return;
        }
        midOrderTraverse(root.leftChild);//逻辑
        System.out.print("mid:"+root.data+"\t");//输出
        midOrderTraverse(root.rightChild);//逻辑
    }

    /**
     * 前序访问树的所有节点  Arrays.sort();
     */
    public void preOrderTraverse(TreeNode<E> root){
        if(root==null){
            return;
        }
        System.out.print("pre:"+root.data+"\t");
        preOrderTraverse(root.leftChild);
        preOrderTraverse(root.rightChild);
    }

    /**
     * 后序访问树的所有节点
     */
    public void postOrderTraverse(TreeNode<E> root){
        if(root==null){
            return;
        }
        postOrderTraverse(root.leftChild);
        postOrderTraverse(root.rightChild);
        System.out.print("post:"+root.data+"\t");
    }

    /**
     *节点的数据结构
     * @param <E>
     */
    public class TreeNode<E> {
        E data;
        TreeNode<E> leftChild;
        TreeNode<E> rightChild;

        public TreeNode(E data, TreeNode<E> leftChild, TreeNode<E> rightChild) {
            this.data = data;
            this.leftChild = leftChild;
            this.rightChild = rightChild;
        }
    }
}

树的遍历

 BinaryTree binarayTree=new BinaryTree("A");//构造简单二叉树
 binarayTree.createTree();
 binarayTree.midOrderTraverse(binarayTree.root);
 System.out.println();
 binarayTree.preOrderTraverse(binarayTree.root);
 System.out.println();
 binarayTree.postOrderTraverse(binarayTree.root);

在这里插入图片描述在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/833257.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ThreadPoolExecutor详解(上)

为什么会有线程池&#xff1f; 如果客户端发一个请求&#xff0c;服务端就创建一个线程接收请求&#xff0c;线程资源是有限的&#xff0c;而且创建一个线程和执行结束之后都要调用操作系统资源销毁线程&#xff0c;这样频繁操作肯定非常占用cpu和内存资源&#xff0c;线程池的…

性能测试 —— “问题分析”

性能测试大致分以下几个步骤&#xff1a; 需求分析 脚本准备 测试执行 结果整理 问题分析 今天要说的是最后一个步骤——“问题分析”&#xff1b; 需求描述 有一个服务&#xff0c;启动时会加载一个1G的词表文件到内存&#xff0c;请求来了之后&#xff0c;会把请求词去…

构建稳健的PostgreSQL数据库:备份、恢复与灾难恢复策略

在当今数字化时代&#xff0c;数据成为企业最宝贵的资产之一。而数据库是存储、管理和保护这些数据的核心。PostgreSQL&#xff0c;作为一个强大的开源关系型数据库管理系统&#xff0c;被广泛用于各种企业和应用场景。然而&#xff0c;即使使用了最强大的数据库系统&#xff0…

LeetCode 25题:K个一组翻转链表

题目&#xff1a; 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯…

嵌入式:C高级 Day2

一、递归实现&#xff0c;输入一个数&#xff0c;输出这个数的每一位 二、递归实现&#xff0c;输入一个数字&#xff0c;输出这个数的二进制 三、写一个脚本&#xff0c;包含以下内容 1.显示/etc/group文件中第五行的内容 2.创建目录/home/ubuntu/copy 3.切换工作路径到此目录…

《命运》阅读笔记

《命运》阅读笔记 2023年5月17号在杭州的小屋读完&#xff0c;我读完后&#xff0c;脑海里经常把余华的《活着》和这本《命运》的故事情节搞混淆&#xff0c;几乎都是讲着生活的苦难。全文以阿太&#xff08;外婆的妈妈&#xff09;的视角&#xff0c;在她九十九岁的人生里&…

在excel中整理sql语句

数据准备 CREATE TABLE t_test (id varchar(32) NOT NULL,title varchar(255) DEFAULT NULL,date datetime DEFAULT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO t_test VALUES (87896cf20b5a4043b841351c2fd9271f,张三1,2023/6/8 14:06); INSERT INTO t_test …

Electron + Vue3 + Vite + TS 构建桌面应用

之前是使用React、Electron、TS和webpack来构建桌面应用的。虽然功能齐全,但是打包等等开发的体验不太理想,总感觉太慢了。作为一个开发者,我们总是希望,执行构建命令后,可以快速打包或者启动本地应用,且通过更少的配置,来完成开发体验。 现在的vite已经得到广泛的应用…

如今音视频开发还有前途吗?

下面我从两个角度来跟大家进行分析&#xff1a; 市场角度薪资角度 这两角度分析下来&#xff0c;估计大家心里就会有答案了&#xff01;&#xff01;&#xff01; 1. 市场角度 目前市场中使用音视频技术的公司太多了&#xff0c;大到全民观看短视频&#xff0c;小到直播带货…

IT 基础架构自动化

什么是 IT 基础架构自动化 IT 基础架构自动化是通过使用技术来控制和管理构成 IT 基础架构的软件、硬件、存储和其他网络组件来减少人为干预的过程&#xff0c;目标是构建高效、可靠的 IT 环境。 为什么要自动化 IT 基础架构 为客户和员工提供无缝的数字体验已成为企业的当务…

【从零开始学习JAVA | 第三十八篇】应用多线程

目录 前言&#xff1a; 多线程的实现方式&#xff1a; Thread常见的成员方法&#xff1a; 总结&#xff1a; 前言&#xff1a; 多线程的引入不仅仅是提高计算机处理能力的技术手段&#xff0c;更是适应当前时代对效率和性能要求的必然选择。在本文中&#xff0c;我们将深入…

百度网盘加速下载

下载网页插件可搜索各种奇葩工具 Tampermonkey 在线解析连接&#xff1a;https://api.94speed.com/web/ 工具下载&#xff1a;https://motrix.app/zh-CN/download 解析完成后发送到&#xff1a;motrix

LeetCode 626. 换座位

题目链接&#xff1a;LeetCode 626. 换座位 题目描述 表名&#xff1a;Seat 编写SQL查询来交换每两个连续的学生的座位号。如果学生的数量是奇数&#xff0c;则最后一个学生的id不交换。 按 id 升序 返回结果表。 查询结果格式如下所示。 示例1&#xff1a; 题目分析 如…

Ubuntu20.04安装MySQL8

Ubuntu20.04安装MySQL8 MySQL8下载 点击MySQL download下载官网&#xff0c;按照自己所需要的版本下载对应的MySQL版本&#xff0c;如下图 点击下载后在进行解压&#xff0c;大致文件如下所示 然后需要一次安装对应的.deb文件。普通.deb程序安装命令&#xff1a; dpkg -i …

前端:地图篇(一)

1、前言 在很多的出行程序中&#xff0c;都会使用到地图这一个功能&#xff0c;在实际的开发中我们也不会去开发一个自己的地图模型。如果自己开发一个地图模型&#xff0c;那么需要投入的成本、人力都是非常巨大的。所以我们很多网站和APP中使用的都是第三方的接口和JS&#…

本地部署 audiocraft

本地部署 audiocraft 1. 什么是 audiocraft2. Github 地址3. 安装 Miniconda34. 创建虚拟环境5. 部署 audiocraft6. 启动 MusicGen7. 访问 MusicGen 1. 什么是 audiocraft Audiocraft 是一个通过深度学习进行音频处理和生成的库。它具有最先进的 EnCodec 音频压缩器/分词器&am…

Java Selenium WebDriver 网页填报

一、windows环境安装配置 1.安装chrome浏览器 在“关于chrome”界面&#xff0c;查看浏览器版本号 2.下载chromeDriver 在https://registry.npmmirror.com/binary.html?pathchromedriver/下载对应版本的驱动&#xff08;如果浏览器版本过新&#xff0c;建议下载最接近的版…

day20-101. 对称二叉树

101. 对称二叉树 力扣题目链接 给定一个二叉树&#xff0c;检查它是否是镜像对称的。 思路 镜像对称必要的条件就是根节点的左右子树互相对称 左子树的左孩子 右子树的右孩子左子树的右孩子 右子树的左孩子 递归 使用递归前要确定递归的顺序&#xff0c;是前序、后序还…

N个实现水平垂直居中的方法

1 行内元素的水平垂直居中 1.1 单行文本 要实现行内元素的水平居中&#xff0c;只需把行内元素包裹在块级父层元素中&#xff0c;并且在父层元素CSS设置如下&#xff1a; <div class"box"><p>center</p> </div>.box{background-color: aq…

Docker Compose编排部署LNMP服务

目录 安装docker-ce 阿里云镜像加速器 文件 启动 安装docker-ce [rootlocalhost ~]# wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo --2023-08-03 18:34:32-- http://mirrors.aliyun.com/repo/Centos-7.repo 正在解析主机 m…