1. 踩坑记录
今天天气好,我高高兴兴地打算用 priority_queue
实现一个哈夫曼树,我是这么做的:
1 | struct Node |
这代码一旦运行,就会看见满屏的输出,永无止境。我一点一点的 debug ,发现是因为简历起来的树成了图了,自己指向自己...???
什么情况,我又仔细地 debug 一边,最后我发现了一个很鬼畜的事实:
1 | Node a = heap.top(); heap.pop(); |
没错!就是这两行代码,每次运行的时候都返回一毛一样的地址啊!不仅每次循环是一样的,就连 a
和 b
的地址也是一样的!
2. 实验+解释
2.1 实验
为了研究一下到底是哪里出的问题,我做了一个小实验。
1 | void TestPriorityQueue(int n = 5) |
运行这段代码,会得到以下结果:
测试 priority_queue 的地址问题
1
x 的地址: 0x62fee0
2
x 的地址: 0x62fee0
3
x 的地址: 0x62fee0
4
x 的地址: 0x62fee0
5
x 的地址: 0x62fee0
堆顶元素: 5, 地址: 0x712138 0x712138; 指针 last: 0x712138, 5
堆顶元素: 4, 地址: 0x712138 0x712138; 指针 last: 0x712138, 4
堆顶元素: 3, 地址: 0x712138 0x712138; 指针 last: 0x712138, 3
堆顶元素: 2, 地址: 0x712138 0x712138; 指针 last: 0x712138, 2
堆顶元素: 1, 地址: 0x712138 0x712138; 指针 last: 0x712138, 1
为啥 x
的地址都一样我以前解释过了,这里我们主要关心最后 5 行,我们会发现:
- 每次
heap.top()
的地址都是一样的 - 尽管我们没有改变
last
,但是它的值却始终保持和top()
一致
莫非是我对 STL 的理解有问题?我换了个容器试了试:
1 | void TestQueue(int n = 5) |
结果如下:
测试 queue 的地址问题
1 2 3 4 5
队首元素: 1, 地址为0xfa16b0 0xfa16b0; 队尾元素: 5, 地址为0xfa16c0 ; 指针 last: 0xfa16b0, 1
队首元素: 2, 地址为0xfa16b4 0xfa16b4; 队尾元素: 5, 地址为0xfa16c0 ; 指针 last: 0xfa16b0, 1
队首元素: 3, 地址为0xfa16b8 0xfa16b8; 队尾元素: 5, 地址为0xfa16c0 ; 指针 last: 0xfa16b0, 1
队首元素: 4, 地址为0xfa16bc 0xfa16bc; 队尾元素: 5, 地址为0xfa16c0 ; 指针 last: 0xfa16b0, 1
队首元素: 5, 地址为0xfa16c0 0xfa16c0; 队尾元素: 5, 地址为0xfa16c0 ; 指针 last: 0xfa16b0, 1
这里就没有问题了,可见是 priority_queue
自身的特性导致的。
2.2 解释
其实这个问题很简单,只是我一开始没有去思考而已。
请问:优先队列是什么?是堆,具体而言是二叉堆。那二叉堆是用什么实现的?用数组,因为二叉堆是一个完全二叉树,所以很方便用数组来实现。
这不就结了嘛!为啥每次地址都一样?不就是因为堆顶元素始终都放在二叉树的根节点位置吗?根节点始终都在数组的首位元素,所以每次返回的地址都一样啊。
所以说学好数据结构还是很重要的,不然到时候除了 bug 都不知道是怎么回事。
3. 修改我的哈夫曼
既然这样的话,我们就需要把原来的代码改一下了,我的做法是不在存储 Node
,而直接存储 Node*
,代码如下:
1 |
|