链接:https://www.zhihu.com/question/19729973/answer/433057897
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
在数据结构中经常有人说堆栈,堆栈之间有什么区别了?这些数据结构在算法思想中有哪些良好的运用了?
首先我们平常端盘子,都是知道盘子一般都是一个个堆叠起来的,而我们把盘子比作数据,而堆起来的盘子就很像栈这种数据结构。
![](https://pic3.zhimg.com/80/v2-da302cfeb040bc6a373aa28e14242890_720w.jpg)
先进后出的这种结构,就是栈的数据结构,我们存数据时先存a,b,c,d,e 之后取e,d,c,b,a。
同样的堆也是一类特殊的数据结构,和栈有所不同的是,堆的内存是由所在语言系统环境管理的(比如python虚拟机)而栈申请内存时是由系统自动分配的,而且系统分配也是大小限制的,超过这个大小后就会内存溢出报错,类似如下定义的字符溢出。
![](https://pic3.zhimg.com/80/v2-2dd88f859b7fca1975494196b1e6a09c_720w.jpg)
而堆内存是一般是由语言运行环境分配管理,如Python虚拟机控制内存和运行环境:
![](https://pic3.zhimg.com/80/v2-709e3590be1fd4b7cca08cfe7f6a4050_720w.jpg)
所以内存上可以组合关联起来,这样就不容易受系统限制
![](https://pic3.zhimg.com/80/v2-4239988f52839c5b0091ca5b133f8bce_720w.jpg)
堆在数据存取上和栈有所不同,栈是先进后出,而堆是一种优先的队列(并不是队列,堆通常是一个可以被看做一棵树的数组对象。),所谓优先队列是按照元素的优先级取出元素。举个例子,一般在饭桌上,无论你是先来后到,应该先是爷爷奶奶辈先动筷子,后面是父母,之后是你,像这种:
![](https://pic3.zhimg.com/80/v2-68bd16cac1b71d0e53b610d459f603d3_720w.jpg)
有这样的优先级享受的,其严格的逻辑数据结构定义如下:
![](https://pic3.zhimg.com/80/v2-1cb63bd5ef774120cba198c481f89e2a_720w.jpg)
有了这定义我们来尝试一下用堆这种数据结构做一种排序叫堆排序,我们先得到需要排序的一组元素:
16,7,3,20,17,8
先构建一颗树:
![](https://pic2.zhimg.com/80/v2-0dab3a25948788add3b0132a77947fb3_720w.jpg)
按照堆数据结构的定义,即任何一非叶节点的数据不大于或者不小于其左右孩子节点的数据
,调整如下:
![](https://pic4.zhimg.com/80/v2-578353267d9e23cffd6d7f72c24998fe_720w.jpg)
![](https://pic2.zhimg.com/80/v2-034a91cd40a68840d896557f4f7d3554_720w.jpg)
![](https://pic3.zhimg.com/80/v2-b983904c06d8b1b550d3560fc69fa362_720w.jpg)
![](https://pic3.zhimg.com/80/v2-75d161f4a01db37926382ef84ad98698_720w.jpg)
直到调整如上,就表示最大的在最上面,下面每个子节点都比父节点小,堆就完成了初始化。现在要对堆进行排序(注意),我们希望从左到右,按照从小到大的顺序排列,依照堆的性质,我们可以这样排:
首先我们将最大的和最后一个位置进行交换
![](https://pic3.zhimg.com/80/v2-f064ed746bc618ff97f8801d88219204_720w.jpg)
除了红色标记的元素,其他数据按照堆的性质从新排列好。
![](https://pic1.zhimg.com/80/v2-8fd2652607a84e9790099c4b518a3141_720w.jpg)
继续将除红色标记外的最大元素移到最后面
![](https://pic2.zhimg.com/80/v2-7dc367c45a4e177dabd0b1e4285dc562_720w.jpg)
![](https://pic3.zhimg.com/80/v2-0c05c6f5a22876e8fe43279f99437d54_720w.jpg)
![](https://pic2.zhimg.com/80/v2-eaad09178130391f660e0415c3e46b11_720w.jpg)
![](https://pic4.zhimg.com/80/v2-46486b3bd4e2e60bbb7fce694ea64ac7_720w.jpg)
![](https://pic1.zhimg.com/80/v2-9887169108a57c28ad59bb990a3c0e04_720w.jpg)
![](https://pic2.zhimg.com/80/v2-b08b78fc99fe3df071d4e2ef309d022e_720w.jpg)
![](https://pic1.zhimg.com/80/v2-c1a703f2bbfb57577ee45a9126b15ecc_720w.jpg)
![](https://pic2.zhimg.com/80/v2-040559acd552cee6cbea762d96e142a7_720w.jpg)
一直到只剩根节点时,这个时候排序就已经排好了,以上就是利用数据结构堆做的排序算法。我们用Python代码实现如下:
def heap_sort(lst):
def sift_down(start, end):
"""最大堆调整"""
root = start
while True:
child = 2 * root + 1
if child > end:
break
if child + 1 <= end and lst[child] < lst[child + 1]:
child += 1
if lst[root] < lst[child]:
lst[root], lst[child] = lst[child], lst[root]
root = child
else:
break
# 创建最大堆
for start in range((len(lst) - 2) // 2, -1, -1):
sift_down(start, len(lst) - 1)
# 堆排序
for end in range(len(lst) - 1, 0, -1):
lst[0], lst[end] = lst[end], lst[0]
sift_down(0, end - 1)
return lst
if __name__ == "__main__":
list = [16,7,3,20,17,8]
heap_sort(list)
print(list)
运行结果:
![](https://pic3.zhimg.com/80/v2-156b5282a8810e8d7573c742a480742c_720w.jpg)
计算复杂度后我们发现堆排序也是比较优秀的一种排序,那栈了,其实这里突然想起一件事,就是学维特比算法时有个马尔科夫链,其中就包含了回溯算法,网上搜索发现栈这种数据结构正好对应这种算法思想,我们写个实际的例子来看看,首先我们看看,什么是回溯了,我举个例子。
如下图,现在有一只小蚂蚁P要在迷宫中找食物T,找的方式如下,我们把每个岔路口当成一个节点,记录下来,假设蚂蚁从节点1出发,沿着2、3、4都是死路,于是返回节点1,这个返回的过程就是回溯,当到达节点5时,往上碰壁返回来(回溯),左右碰壁回来(回溯),只有往下才找到食物,这个过程就是回溯算法体现。
![](https://pic2.zhimg.com/80/v2-0a0d66024dd2460b0518d5f17e5fe4d6_720w.jpg)
为了方便代码实现,我们用1表示墙,0表示可以走的地方,666表示食物,矩阵表示整个迷宫:
![](https://pic3.zhimg.com/80/v2-6b24cbb0e5daefbba617352acb3c428f_720w.jpg)
想法和思路是这样:
1、将小蚂蚁走过的分岔路口记录下来
2、将小蚂蚁走过的路径记录记录下来(用栈这种数据结构)
3、当小蚂蚁发现这条路不通时,或者遇到相同的岔路口时,延原路返回(路径数据取出栈的数据)当回到岔路口时,将岔路口那个方向路不通的数据标记,延没有标记的方向继续前进(如果都标记了,继续延原路返回),直到找到食物。
具体的实现代码我就不贴出来了,这就是栈这种数据结构在回溯算法上的应用。
哈哈哈,折腾了一天希望有大佬能指出错误,共同进步。
参考书籍:
算法导论原书第三版
Python数据结构