Fork me on GitHub

数据结构与算法——堆 特点与算法实现(二)

1. 建立堆算法实现

 堆数据结构的定义

1 #define DEFAULT_CAPCITY 128
2 
3 typedef struct _Heap
4 {
5     int *arr; //存储堆元素的数组
6     int size; //当前已存储的元素个数
7     int capacity; //当前存储的容量
8 }Heap;

 

建(最大)堆

 1 bool initHeap(Heap &heap, int *orginal, int size);
 2 static void buildHeap(Heap &heap);
 3 static void adjustDown(Heap &heap, int index);
 4 
 5 /*初始化堆*/
 6 bool initHeap(Heap &heap, int *orginal, int size)
 7 {
 8     int capacity = DEFAULT_CAPCITY>size? DEFAULT_CAPCITY:size;
 9 
10     heap.arr = new int[capacity];
11     if(!heap.arr) return false;
12 
13     heap.capacity = capacity;
14     heap.size = 0;
15 
16     //如果存在原始数据则构建堆
17     if(size>0)
18     {
19         memcpy(heap.arr, orginal, size*sizeof(int));
20         heap.size = size;
21         buildHeap(heap);
22     }
23     else
24     {
25         heap.size = 0;
26     }
27     return true;
28 }
29 
30 /*将当前的节点和子节点调整成最大堆*/
31 void adjustDown(Heap &heap, int index)
32 {
33     int cur=heap.arr[index];    //当前待调整的节点
34     int parent,child;
35 
36     /*判断否存在大于当前节点子节点,如果不存在 ,则堆本身是平衡的,不需要调整;
37     如果存在,则将最大的子节点与之交换,交换后,如果这个子节点还有子节点,
38     则要继续按照同样的步骤对这个子节点进行调整*/
39     for(parent=index; (parent*2+1)<heap.size; parent=child)
40     {
41         child=parent*2+1;
42         //取两个子节点中的最大的节点
43         if(((child+1)<heap.size)&&(heap.arr[child]<heap.arr[child+1]))
44         {
45             child++;
46         }
47         //判断最大的节点是否大于当前的父节点
48         if(cur>=heap.arr[child])
49         {
50             //不大于,则不需要调整,跳出循环
51             break;
52         }
53         else
54         {
55             //大于当前的父节点,进行交换,然后从子节点位置继续向下调整
56             heap.arr[parent]=heap.arr[child];
57             heap.arr[child]=cur;
58         }
59     }
60 }
61 
62 /* 从最后一个父节点(size/2-1 的位置)逐个往前调整所有父节点(直到根节点),
63 确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆 */
64 void buildHeap(Heap &heap)
65 {
66     int i;
67     for(i=heap.size/2-1; i>=0; i--)
68     {
69         adjustDown(heap, i);
70     }
71 }

 

 

2. 插入新元素

将数字 99 插入到上面大顶堆中的过程如下:

1) 原始的堆,如图 a,对应的数组:{95, 93, 87, 92, 86, 82}

 

 2) 将新进的元素插入到大顶堆的尾部,如下图 b 所示:  

  对应的数组:{95, 93, 87, 92, 86, 82, 99}

 

 

3) 此时最大堆已经被破坏,需要重新调整, 因加入的节点比父节点大,则新节点跟父节点调换即可,如图 c 所示;调整后,新节点如果比新的父节点小,则已经调整到位,如果比新的父节点大,则需要和父节点重新进 行交换,如图 d, 至此,最大堆调整完成。

 

 3. 插入元素代码实现

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 
  5 #define DEFAULT_CAPCITY 128
  6 
  7 typedef struct _Heap 
  8 {
  9     int *arr; //存储堆元素的数组
 10     int size; //当前已存储的元素个数
 11     int capacity; //当前存储的容量
 12 }Heap;
 13 
 14 bool initHeap(Heap &heap, int *orginal, int size);
 15 bool insert(Heap &heap, int value);
 16 static void buildHeap(Heap &heap);
 17 static void adjustDown(Heap &heap, int index);
 18 static void adjustUp(Heap &heap, int index);
 19 
 20 /*初始化堆*/
 21 bool initHeap(Heap &heap, int *orginal, int size) 
 22 {
 23     int capacity = DEFAULT_CAPCITY>size ? DEFAULT_CAPCITY : size;
 24     heap.arr = new int[capacity];
 25     if (!heap.arr) return false;
 26     heap.capacity = capacity;
 27     heap.size = 0;
 28     
 29     //如果存在原始数据则构建堆
 30     if(size > 0)
 31     {
 32         /*方式一: 直接调整所有元素
 33         memcpy(heap.arr, orginal, size*sizeof(int));
 34         heap.size = size;
 35         //建堆
 36         buildHeap(heap);
 37         */
 38         //方式二: 一次插入一个
 39         for(int i=0; i<size; i++)
 40         {
 41             insert(heap, orginal[i]);
 42         }
 43     }
 44     return true;
 45 }
 46 
 47 /* 从最后一个父节点(size/2-1 的位置)逐个往前调整所有父节点(直到根节
 48 点),确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆*/
 49 void buildHeap(Heap &heap) 
 50 {
 51     int i;
 52     for (i = heap.size / 2 - 1; i >= 0; i--) 
 53     {
 54         adjustDown(heap, i);
 55     }
 56 }
 57 
 58 /*将当前的节点和子节点调整成最大堆*/
 59 void adjustDown(Heap &heap, int index)
 60 {
 61     int cur = heap.arr[index];//当前待调整的节点
 62     int parent, child;
 63     
 64     /*判断否存在大于当前节点子节点,如果不存在 ,则堆本身是平衡的,不需要调整;
 65     如果存在,则将最大的子节点与之交换,交换后,如果这个子节点还有子节点,则要继续
 66     按照同样的步骤对这个子节点进行调整*/
 67     for (parent = index; (parent * 2 + 1)<heap.size; parent = child) 
 68     {
 69         child = parent * 2 + 1;
 70         //取两个子节点中的最大的节点
 71         if (((child + 1)<heap.size) && (heap.arr[child]<heap.arr[child + 1])) 
 72         {
 73             child++;
 74         }
 75         //判断最大的节点是否大于当前的父节点
 76         if (cur >= heap.arr[child]) 
 77         {
 78             //不大于,则不需要调整,跳出循环
 79             break;
 80         }
 81         else 
 82         {
 83             //大于当前的父节点,进行交换,然后从子节点位置继续向下调整
 84             heap.arr[parent] = heap.arr[child];
 85             heap.arr[child] = cur;
 86         }
 87     }
 88 }
 89 
 90 /*将当前的节点和父节点调整成最大堆*/
 91 void adjustUp(Heap &heap, int index) 
 92 {
 93     if(index<0 || index >= heap.size)
 94     {
 95         //大于堆的最大值直接 return
 96         return;
 97     }
 98     while(index>0)
 99     {
100         int temp = heap.arr[index];
101         int parent = (index - 1) / 2;
102         if(parent >= 0)
103         {
104             //如果索引没有出界就执行想要的操作
105             if(temp > heap.arr[parent])
106             {
107                 heap.arr[index] = heap.arr[parent];
108                 heap.arr[parent] = temp;
109                 index = parent;
110             }
111             else 
112             {
113                 //如果已经比父亲小 直接结束循环
114                 break;
115             }
116         }
117         else 
118         {
119             //越界结束循环
120             break;
121         }
122     }
123 }
124 
125 /*最大堆尾部插入节点,同时保证最大堆的特性*/
126 bool insert(Heap &heap, int value) 
127 {
128     if (heap.size == heap.capacity) 
129     {
130         fprintf(stderr, "栈空间耗尽!\n");
131         return false;
132     }
133     int index = heap.size;
134     heap.arr[heap.size++] = value;
135     adjustUp(heap, index);
136     return true;
137 }
138 
139 int main(void) 
140 {
141     Heap hp;
142     int origVals[] = { 1, 2, 3, 87, 93, 82, 92, 86, 95 };
143     int i = 0;
144     if(!initHeap(hp, origVals, sizeof(origVals)/sizeof(origVals[0])))
145     {
146         fprintf(stderr, "初始化堆失败!\n");
147         exit(-1);
148     }
149     for (i = 0; i<hp.size; i++) 
150     {
151         printf("the %dth element:%d\n", i, hp.arr[i]);
152     }
153     insert(hp, 99);
154     printf("在堆中插入新的元素 99,插入结果:\n");
155     for (i = 0; i<hp.size; i++) 
156     {
157         printf("the %dth element:%d\n", i, hp.arr[i]);
158     }
159     
160     system("pause");
161     return 0;
162 }

 

 4. 堆顶元素出列

如果我们将堆顶的元素删除,那么顶部有一个空的节点,怎么处理?

当插入节点的时候,我们将新的值插入数组的尾部。现在我们来做相反的事情:我们取出数组中的最后一个元 素,将它放到堆的顶部,然后再修复堆属性。

 

 

5. 堆顶元素出列算法实现

 1 bool initHeap(Heap &heap, int *orginal, int size);
 2 static void buildHeap(Heap &heap);
 3 static void adjustDown(Heap &heap, int index);
 4 
 5 /*初始化堆*/
 6 bool initHeap(Heap &heap, int *orginal, int size)
 7 {
 8     int capacity = DEFAULT_CAPCITY>size? DEFAULT_CAPCITY:size;
 9     heap.arr = new int[capacity];
10     if(!heap.arr) return false;
11     heap.capacity = capacity;
12     heap.size = 0;
13     
14     //如果存在原始数据则构建堆
15     if(size>0)
16     {
17         memcpy(heap.arr, orginal, size*sizeof(int));
18         heap.size = size;
19         buildHeap(heap);
20     }
21     else 
22     {
23         heap.size = 0;
24     }
25     return true;
26 }
27 
28 /*将当前的节点和子节点调整成最大堆*/
29 void adjustDown(Heap &heap, int index)
30 {
31     int cur=heap.arr[index];    //当前待调整的节点
32     int parent,child;
33     /*判断否存在大于当前节点子节点,如果不存在 ,则堆本身是平衡的,不需要调整;
34     如果存在,则将最大的子节点与之交换,交换后,如果这个子节点还有子节点,
35     则要继续按照同样的步骤对这个子节点进行调整*/
36     for(parent=index; (parent*2+1)<heap.size; parent=child)
37     {
38         child=parent*2+1;
39         //取两个子节点中的最大的节点
40         if(((child+1)<heap.size)&&(heap.arr[child]<heap.arr[child+1]))
41         {
42             child++;
43         }
44         //判断最大的节点是否大于当前的父节点
45         if(cur>=heap.arr[child])
46         {
47             //不大于,则不需要调整,跳出循环
48             break;
49         }
50         else
51         {
52             //大于当前的父节点,进行交换,然后从子节点位置继续向下调整
53             heap.arr[parent]=heap.arr[child];
54             heap.arr[child]=cur;
55         }
56     }
57 }
58 /* 从最后一个父节点(size/2-1 的位置)逐个往前调整所有父节点(直到根节点),确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆 */
59 void buildHeap(Heap &heap)
60 {
61     int i;
62     for(i=heap.size/2-1; i>=0; i--)
63     {
64         adjustDown(heap, i);
65     }
66 }

 

posted @ 2020-10-18 19:46  索智源  阅读(135)  评论(0编辑  收藏  举报