美文网首页
哈希表与树的入门

哈希表与树的入门

作者: 光锥外 | 来源:发表于2020-05-13 13:38 被阅读0次

哈希表:

特点:

数组(顺序表):寻址容易

链表:插入与删除容易

哈希表:寻址容易,插入删除也容易的数据结构,也就是综合了上述两种数据结构的优点于一身。

Hash table:

定义:

哈希表(Hash table,也叫散列表)

是根据关键码值(Key value)而直接进行访问的数据结构,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。

关键码值(Key value)也可以当成是key的hash值,这个映射函数叫做散列函数,是需要我们自己来设计的。

存放记录的数组叫做散列表。

例子:

上面的定义有点抽象,下面用一个例子来对它进行说明,比如有如下数据:

Key : {14, 19, 5, 7, 21, 1, 13, 0, 18}

散列表: 大小为13 的数组 a[13];

散列函数: f(x) = x mod 13;

所以根据散列函数我们可以算出每个Key对应的数组的位置,如下:

image.png

第一个值算出来它的key位置为1,所以:

则就将14存放在位置为1的散列表处,以此类推,再放第二个、第三个、第四个、第五个,如下:

对应散列表的分布为:

好,到了一个关键的值了:

此时就属于hash冲突了,那如何解决hash冲突则需要我们自己来制定规则,假如我们这样来定制冲突解决办法为:将hash+1,如果还是冲突则再+1,以此类推,所以此时的存放就为:

接下来再看一个更加极端的:

此时hash为5已经冲突了,所以hash+1=6:

此时还是冲突了,继续hash+1=7,发现也冲突了,再hash+1=8,还是冲突,那就再hash+1=9,嗯,此时终于不冲突了则直接存放到这个位置:

另外为了避免hash碰撞,需要引入装填因子这个概念,为什么需要这个值?因为数据越接近数组最大值,可能产生冲突的情况就越多,啥叫装填因子呢?比如说我们定义0.8的装填因子,还是拿上面的13个散列表的情况来说,当填到第9个位置时因为到达这个80%的空间了,所以接下来会给这个散列表进行扩容,具体扩大多少自己可以定义,如下:

另外HashMap的装填因子是:0.75 阈值:8

HashMap中初始化大小为什么是16? 为什么链表的长度为8是变成红黑树?为什么为6时又变成链表?

https://blog.csdn.net/qq_27409289/article/details/92759730

缺点:

虽说它是综合了数组和链表的优点,但是它扩容需要消费大量的空间和性能,因为散列函数也需要发生变化:

image.png

散列函数一变,则存放到散列表中的下标都得重新进行计算,所以性能会大大受到影响。

应用:

电话号码,字典,点歌系统,QQ,微信的好友等,都有一个上限,就是为了避免散列表扩容导致性能有影响。

JDK中HashMap的散列表应用:

对于jdk1.8以前的HashMap是采用这种结构:

image.png

它是采用数组+链表来进行存放的,就不像之前我们解决Hash冲突时简单的去让hash+1了。我们来看一下通过这种结构想要查387这个数字其速度有多好,首先f(387)=3,则直接在3的位置来找到它的链表,然后第一个就找到了。

但是到了JDK1.8时,由于处于大数据的时代了,所以此时的HashMap的结构也发生了变化了,比如有1000万的数据,这样很可机率会出现某一个散列表位置的链表有几十万的数据,比如:

image.png

所以JDK1.8开始HashMap中当链表长度超过阈值,就转换成红黑树:

image.png

这里先有个概念,之外会专门去学红黑树的,反正就是数据量越大其查询的速度会变成级数级增长,效率是非常之高的。

关于HashMap的源码解读由于目前看还为时尚早,咱们先来贴一下它的resize()方法感受一下其难度:

image.png

,很明显目前基础还没打牢,所以这里只先对Hash表做一个入门,在未来会将HashMap的源码实现给看透滴。

树:

什么树?

image.png

其中提到了“互不相交”,啥意思?下面看一下各种树:

image.png

其中第一张图和最后一张图都有相交的情况,所以它就不属于树了,其它的都是属于树。

image.png image.png

其实这俩是图的数据结构了,而非树。

树的概念【了解】:

节点与树的度:

结点拥有的子树数称为结点的度。

度为0的结点称为叶子结点或终端结点,度不为0的结点称为非终端结点或分支结点。

除根结点以外,分支结点也称为内部结点。

树的度是树内各结点的度的最大值。

用图来表示下:

image.png

层次和深度:

image.png image.png

森林:

image.png

树的存储结构:

双亲表示法【在未来启发示寻路算法会用到它】:

image.png

孩子表示法【二叉树典型的表示法】:

image.png

前面第一张比较浪费空间,而第二张则是优化版本。

双亲孩子表示法【用得少,了解】:

image.png

把每个结点的孩子结点排列起来,以单链表作为存储结构,

则n个结点有n个孩子链表,如果是叶子结点则此单链表为空,

然后n个头指针又组成一个线性表,采用顺序存储结构,存放在一个一维数组中

孩子兄弟表示法【用得少,了解】:

image.png

孩子兄弟表示法为每个节点设计三个域:

一个数据域,一个该节点的第一个孩子节点域,一个该节点的下一个节点的兄弟指针域

二叉树:

概念:

image.png image.png

斜树:

image.png

满二叉树:

一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。

image.png

完全二叉树:

若设二叉树的深度为k,除第k层外,其他各层(1~(k-1)层)的节点数都达到最大值,且第k层所有的节点都连续集中在最左边,这样的树就是完全二叉树。如下图:

image.png

那判定一下下面三个树哪些是完全二叉树:

image.png

其实简单判断的办法就是看编号是否是连续的,由于树1缺了10之后,编号就不连续了,同样的树2少了6、7也不连续了,同样的树3也是少了10,11也不连续了,所以都不是完全二叉树了。

二叉树的存储结构:

顺序存储【用得少】:

image.png

链式存储【用得多】:

image.png

其实它表示的树的形态就为:

image.png

下面咱们用代码来构建一下二叉树,为下面的遍历做准备:

先定义树的结构体:

image.png

然后定义一个根结点:

image.png

接下来咱们来生成这样一棵树:

image.png

所以在里面定义一个生成树的方法:

image.png

接下来咱们来调用生成树,可以如下:

image.png

那只调用了,木有输出呀,此时就需要往下看它的遍历方法了。

二叉树的遍历:

前序(DLR):

规则是若二叉树为空,则空操作返回,否则先访问跟结点,然后前序遍历左子树,再前序遍历右子树。

image.png

利用递归很容易实现,如下:

/**

  • 前序访问树的所有节点 Arrays.sort();

*/

public void preOrderTraverse(Node root){

if(root==null){

return;

}

System.out.println("pre:"+root.data);

preOrderTraverse(root.leftChild);

preOrderTraverse(root.rightChild);

}

调用一下:

image.png

中序(LDR):

规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),

中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。

image.png

咱们用代码来写一下:

/**

  • 中序访问树的所有节点

*/

public void midOrderTraverse(Node root){//逻辑

if(root==null){

    return;

}

midOrderTraverse(root.leftChild);//逻辑

System.out.println("mid:"+root.data);//输出

midOrderTraverse(root.rightChild);//逻辑

}

调用一下:

image.png

汉诺塔的小算法,其中提到了它其实就是二叉树的中序遍历,回忆一下:

image.png

后序(LRD):

规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。

image.png

依葫芦画瓢:

/**

  • 后序访问树的所有节点

*/

public void postOrderTraverse(Node root){

if(root==null){

    return;

}

postOrderTraverse(root.leftChild);

postOrderTraverse(root.rightChild);

System.out.println("post:"+root.data);

}

<embed style="cursor:pointer;" type="application/octet-stream" src="26D21C78-1D03-4437-992E-8703B2D52533.java">

<embed style="cursor:pointer;" type="application/octet-stream" src="0835F8C7-391C-4131-9BF2-66C5C3AAB788.java">

<embed style="cursor:pointer;" type="application/octet-stream" src="A0805B43-173F-4354-8423-D6628CB04A4F.mmap">

https://www.cnblogs.com/webor2006/p/11517957.html

相关文章

网友评论

      本文标题:哈希表与树的入门

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