赞
踩
目录
数据结构中的二叉树:
但如果是这样那他就不是完全二叉树
意思就是除了最后一层,之前的必须是是满二叉树,最后一层可以不是满的但是最后一层从左到右必须是连续的,
接下来用该性质练练手
1. 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为()A 不存在这样的二叉树B 200C 198D 199答:根据上述性质 n0=n2+1;n0=199+1=200;
2. 在具有 2 n 个结点的完全二叉树中,叶子结点个数为()A nB n + 1C n - 1D n / 2答:设度为0的结点为n0,度为1的为n1,度为2的为n2n0+n1+n2=2nn0+n1+n0-1=2n;(n0=n2+1)2n0-1+n1=2n;(完全二叉树中度为1的节点最多只有一个,所以n1=1)2n0=2n;n0=选A;
3. 一棵完全二叉树的节点数位为 531 个,那么这棵树的高度为()A 11B 10C 8D 12答:设高度为h2^h-1-x=531;(x为最后一层缺的结点的个数)x的范围[0,2^(h-1)-1];(完全二叉树)所以将答案往上述两个公示套,发现符合求的是B
4. 一个具有 767 个节点的完全二叉树,其叶子节点个数为()A 383B 384C 385D 386答:n2+n1+n0=767;n0-1+n1+n0=767;2n0+n1=768;(完全二叉树中度为1的节点最多只有一个,所以n1=0,如果n1=1,则n0为小数)n0=38 选B;
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
父亲的下标是parentleftchild=parent*2+1;rightchild=parent*2+2;parent=(child-1)/2;
思路:1. 选出左右孩子中小的那一个数
2.小的这个孩子与父亲比
a.如果小的孩子比父亲小,则跟父亲交换位置,并且吧原来孩子的位置当成父亲继续往下调整,直到走到叶子节点
b.如果小的孩子比父亲大,则不需要处理,调整完成,整个树已经是小堆
对这样一个数组{27,15,19,18,28,34,65,49,25,37};调整思路如图
接下来通过思路实现代码:
- void AdjustDown(int* a, int n, int parent)
- {
- //找出左右孩子小的那个
- int child = parent * 2 + 1; //左孩子
- while (child < n)
- {
- //找出左右孩子小的那一个
- if (a[child + 1] < a[child])
- {
- //默认左孩子小,如果右孩子小就++,加到右孩子
- ++child;
- }
- if (a[child] < a[parent])
- {
- Swap(&a[parent], &a[child]);
- parent = child; //孩子成为父亲
- child = parent * 2 + 1;
- }
- else //b情况
- {
- break;
- }
-
- }
- }
注意看这个代码对上述例子是不会有任何问题的,但是他还存在一个潜在的风险
如果将上面的例子改动一个数字 如图:将28改为16
那么当他进行到第三步时
a[child+1]就会发生越界的问题,所以要对a[child+1]进行限制让他小于n,改进后如下
- void AdjustDown(int* a, int n, int parent)
- {
- //找出左右孩子小的那个
- int child = parent * 2 + 1; //左孩子
- while (child < n)
- {
- //找出左右孩子小的那一个
- if (child + 1 < n && a[child + 1] < a[child])
- {
- //默认做孩子小,如果右孩子小就++,加到右孩子
- ++child;
- }
- if (a[child] < a[parent])
- {
- Swap(&a[parent], &a[child]);
- parent = child;
- child = parent * 2 + 1;
- }
- else //b情况
- {
- break;
- }
-
- }
- }
完整代码如下:
- #include <stdio.h>
-
- void Swap(int* p1, int* p2)
- {
- int tmp = *p1;
- *p1 = *p2;
- *p2 = tmp;
- }
-
- void AdjustDown(int* a, int n, int parent)
- {
- //找出左右孩子小的那个
- int child = parent * 2 + 1; //左孩子
- while (child < n)
- {
- //找出左右孩子小的那一个
- if (child + 1 < n && a[child + 1] < a[child])
- {
- //默认做孩子小,如果右孩子小就++,加到右孩子
- ++child;
- }
- if (a[child] < a[parent])
- {
- Swap(&a[parent], &a[child]);
- parent = child;
- child = parent * 2 + 1;
- }
- else //b情况
- {
- break;
- }
-
- }
- }
-
- int main()
- {
- //前提左右字树是小堆
- int a[] = { 27,15,19,18,28,34,65,49,25,37 };
- int n = sizeof(a) / sizeof(a[0]);
- AdjustDown(a, n, 0);
-
- return 0;
- }
运行后发现和我们预期的结果相同
特殊情况我们可以用向下调整算法,接下来由特殊转为一般,如果左右字树不是小堆,该如何将其调整为堆
eg: 对这样一个数组{15,18,28,34,65,19,49,25,37,27}进行调整
思路:从倒数的第一个非叶子节点(最后一个节点的父亲),从后往前,按照编号,依次作为子树去向下调整
以下就是建堆算法,根据parent=(child-1)/2;最后一个结点的父亲就是 [(n-1)-1 ] / 2;n代表着数组中元素的个数。而除了最后一个结点的父亲之外,其余父亲结点的编号都是连续的直接减1即可,该建堆算法的时间复杂度是O(N);
- for (int i = (n - 1 - 1) / 2;i >= 0;i--)
- {
- AdjustDown(a, n, i);
- }
完整代码:
- #include <stdio.h>
-
- void Swap(int* p1, int* p2)
- {
- int tmp = *p1;
- *p1 = *p2;
- *p2 = tmp;
- }
-
- void AdjustDown(int* a, int n, int parent)
- {
- //找出左右孩子小的那个
- int child = parent * 2 + 1; //左孩子
- while (child < n)
- {
- //找出左右孩子小的那一个
- if (child + 1 < n && a[child + 1] < a[child])
- {
- //默认做孩子小,如果右孩子小就++,加到右孩子
- ++child;
- }
- if (a[child] < a[parent])
- {
- Swap(&a[parent], &a[child]);
- parent = child;
- child = parent * 2 + 1;
- }
- else //b情况
- {
- break;
- }
-
- }
- }
-
- int main()
- {
- //前提左右字树是小堆
- int a[] = { 15,18,28,34,65,19,49,25,37,27 };
- int n = sizeof(a) / sizeof(a[0]);
-
- for (int i = (n - 1 - 1) / 2;i >= 0;i--)
- {
- AdjustDown(a, n, i);
- }
- return 0;
- }
效果演示
以上代码是构造小堆,构造大堆也很简单,只需要将小于改成大于即可,其余不做改变,如下
- if (child + 1 < n && a[child + 1] > a[child])
- {
-
- ++child;
- }
- if (a[child] > a[parent])
- {
- Swap(&a[parent], &a[child]);
- parent = child;
- child = parent * 2 + 1;
- }
以上例子构造大堆如下
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。