栈
栈就像一个一边封闭一边开口的圆筒,第一个进去的在最底部,也叫栈底,最后一个进去的在最上面,也叫栈顶;出栈的话,最上面的先出,最下面的后出,也就是先进后出。
入栈
因为栈底是封闭的,所以入栈的元素进去后就会成为新的栈顶。
出栈
同样出栈也只能从栈顶出,出来后,原来栈顶的前一个元素就会成为新的栈顶。
队列
队列可以想象为一个隧道,第一个进去的在队头,最后一个进去的注意不是队尾,而是最后一个元素的下一个是队尾,(如下图)出队的话,自然的就是第一个进去的先出来,最后一个进去的后出来,也就是先进先出。
入队
就是把新元素放入队列中,只允许在队尾的位置放入元素,新放入的元素的下一个位置自然地成为新的队尾。
出队
因为队列的先进先出,出队只能从队头先出,队头后面的元素自然的成为新的队头。
如果这样不断地出队,那队头左边的空间就失去作用,队列的空间就会越来越小,就像下面这样
用数组实现的队列采用循环队列的方式来维持队列容量的恒定,如下:
假如一个队列经过反复的入队和出队操作,还剩下两个元素,在数组不扩容的前提下,让队尾指针重新指回数组的首位,当有新的元素进来时,将其放入数组的首位,队尾指针继续后移即可,过程如下图:
由图可以看出,队尾指针指向的位置永远空出一位,所以队列的最大容量比数组长度小1。
哈希表
也叫散列表,这种数据结构提供了键(key)和值(value)的映射关系,只要给出一个key,就可以迅速查找出它所匹配的value。
那么是怎么通过key快速找到value呢?这里用到了哈希函数,类似一个中转站,通过这种方式,把key和数组下标进行转换。如下图:
先暂时不了解哈希函数的原理。
哈希表的写操作
在Java中,写操作就是在哈希表中插入新的键值对(JDK中叫做Entry),如调用hashMap.put("002931","张三"),意思就是插入一组key为002931,value为张三的键值对。
具体过程是:
1.通过哈希函数把002931转换成对应的下标,假如是5。
2.如果下标5对应的位置没有元素,就把Entry填入到下标为5的位置。
但是数组的长度是有限的,当填入的Entry越来越多时,不同的key通过哈希函数转换的下标可能是相同的,这种情况就是哈希冲突。有两种解决方案,一是开放寻址法,二是链表法。
一:
其实就是,以上面转换成下标5为例,如果下标5的位置有元素,那就向后移动一位,看下标为6的位置是否为空,若还为空,就再向后移动一位,看下标7是否为空,若不为空,就存入下标为7的位置。
二:
链表法被应用在Java的集合类HashMap中,HashMap数组的每一个元素不仅是Entry对象,还是一个链表的头结点,每一个Entry对象通过next指针指向下一个Entry节点,当冲突时,只需要插入到对应的链表中即可。如图:
哈希表的读操作
调用 hashMap.get("002936"),意思是查找Key为002936的Entry在散列 表中所对应的值。
第1步:通过哈希函数,把Key转化成数组下标2。
第2步:找到数组下标2所对应的元素,如果这个元素的Key是002936,那么就 找到了;如果这个Key不是002936也没关系,由于数组的每个元素都与一个链表对应,我们可以顺着链表慢慢往下找,看看能否找到与Key相匹配的节点。如图:
在上图中,首先查到的节点Entry6的Key是002947,和待查找的Key 002936不 符,接着定位到链表下一个节点Entry1,发现Entry1的Key 002936正是我们要寻 找的,所以返回Entry1的Value即可。
扩容
当经过多次元素插入,散列表达到一定饱和度时,Key映射位置发生冲突的概率 会逐渐提高。这样一来,大量元素拥挤在相同的数组下标位置,形成很长的链表, 对后续插入操作和查询操作的性能都有很大影响。
小结
栈是一种线性逻辑结构,可以用数组实现,也可以用链表实现,遵循先入后出规则。
队列也是一个种线性逻辑结构,可以用数组实现,也可以用链表实现,遵循先入先出规则。
哈希表也叫散列表,是存储Key-Value映射的集合,通过哈希函数实现Key和数组下标的 转换,通过开放寻址法和链表法来解决哈希冲突。
Ending:
OK,本篇文章就到此结束了,非常感谢你能看到这里,所以如果你觉得这篇文章对你有帮助的话,请点一个大大的赞,支持一下博主,若你觉得有什么问题或疑问,欢迎私信博主或在评论区指出~