1. 程式人生 > >深度解析(十二)斜堆

深度解析(十二)斜堆

fin 內容 保存 source 測試 java版 AI root stat

斜堆(一)之 C語言的實現

概要

本章介紹斜堆。和以往一樣,本文會先對斜堆的理論知識進行簡單介紹,然後給出C語言的實現。後續再分別給出C++和Java版本的實現;實現的語言雖不同,但是原理如出一轍,選擇其中之一進行了解即可。若文章有錯誤或不足的地方,請不吝指出!

目錄
1. 斜堆的介紹
2. 斜堆的基本操作
3. 斜堆的C實現(完整源碼)
4. 斜堆的C測試程序

轉載請註明出處:http://www.cnblogs.com/skywang12345/p/3638493.html


更多內容:數據結構與算法系列 目錄

斜堆的介紹

斜堆(Skew heap)也叫自適應堆(self-adjusting heap),它是左傾堆的一個變種。和左傾堆一樣,它通常也用於實現優先隊列。它的合並操作的時間復雜度也是O(lg n)。

相比於左傾堆,斜堆的節點沒有"零距離"這個屬性。除此之外,它們斜堆的合並操作也不同。斜堆的合並操作算法如下:
(01) 如果一個空斜堆與一個非空斜堆合並,返回非空斜堆。
(02) 如果兩個斜堆都非空,那麽比較兩個根節點,取較小堆的根節點為新的根節點。將"較小堆的根節點的右孩子"和"較大堆"進行合並。
(03) 合並後,交換新堆根節點的左孩子和右孩子。
第(03)步是斜堆和左傾堆的合並操作差別的關鍵所在,如果是左傾堆,則合並後要比較左右孩子的零距離大小,若右孩子的零距離 > 左孩子的零距離,則交換左右孩子;最後,在設置根的零距離。

斜堆的基本操作

1. 頭文件

技術分享圖片
#ifndef _SKEW_HEAP_H_
#define _SKEW_HEAP_H_

typedef int Type;

typedef struct _SkewNode{
    Type   key;                // 關鍵字(鍵值)
    struct _SkewNode *left;    // 左孩子
    struct _SkewNode *right;   // 右孩子
}SkewNode, *SkewHeap;

// 前序遍歷"斜堆"
void preorder_skewheap(SkewHeap heap);
// 中序遍歷"斜堆"
void inorder_skewheap(SkewHeap heap);
// 後序遍歷"斜堆"
void postorder_skewheap(SkewHeap heap);

// 獲取最小值(保存到pval中),成功返回0,失敗返回-1。
int skewheap_minimum(SkewHeap heap, int *pval);
// 合並"斜堆x"和"斜堆y",並返回合並後的新樹
SkewNode* merge_skewheap(SkewHeap x, SkewHeap y);
// 將結點插入到斜堆中,並返回根節點
SkewNode* insert_skewheap(SkewHeap heap, Type key);
// 刪除結點(key為節點的值),並返回根節點
SkewNode* delete_skewheap(SkewHeap heap);

// 銷毀斜堆
void destroy_skewheap(SkewHeap heap);

// 打印斜堆
void print_skewheap(SkewHeap heap);

#endif
技術分享圖片

SkewNode是斜堆對應的節點類。

2. 合並

技術分享圖片
/* 
 * 合並"斜堆x"和"斜堆y"
 *
 * 返回值:
 *     合並得到的樹的根節點
 */
SkewNode* merge_skewheap(SkewHeap x, SkewHeap y)
{
    if(x == NULL)
        return y;
    if(y == NULL)
        return x;

    // 合並x和y時,將x作為合並後的樹的根;
    // 這裏的操作是保證: x的key < y的key
    if(x->key > y->key)
        swap_skewheap_node(x, y);

    // 將x的右孩子和y合並,
    // 合並後直接交換x的左右孩子,而不需要像左傾堆一樣考慮它們的npl。
    SkewNode *tmp = merge_skewheap(x->right, y);
    x->right = x->left;
    x->left  = tmp;

    return x;
}
技術分享圖片

merge_skewheap(x, y)的作用是合並x和y這兩個斜堆,並返回得到的新堆。merge_skewheap(x, y)是遞歸實現的。

3. 添加

技術分享圖片
/* 
 * 新建結點(key),並將其插入到斜堆中
 *
 * 參數說明:
 *     heap 斜堆的根結點
 *     key 插入結點的鍵值
 * 返回值:
 *     根節點
 */
SkewNode* insert_skewheap(SkewHeap heap, Type key)
{
    SkewNode *node;    // 新建結點

    // 如果新建結點失敗,則返回。
    if ((node = (SkewNode *)malloc(sizeof(SkewNode))) == NULL)
        return heap;
    node->key = key;
    node->left = node->right = NULL;

    return merge_skewheap(heap, node);
}
技術分享圖片

insert_skewheap(heap, key)的作用是新建鍵值為key的結點,並將其插入到斜堆中,並返回堆的根節點。

4. 刪除

技術分享圖片
/* 
 * 取出根節點
 *
 * 返回值:
 *     取出根節點後的新樹的根節點
 */
SkewNode* delete_skewheap(SkewHeap heap)
{
    SkewNode *l = heap->left;
    SkewNode *r = heap->right;

    // 刪除根節點
    free(heap);

    return merge_skewheap(l, r); // 返回左右子樹合並後的新樹
}
技術分享圖片

delete_skewheap(heap)的作用是刪除斜堆的最小節點,並返回刪除節點後的斜堆根節點。

註意關於斜堆的"前序遍歷"、"中序遍歷"、"後序遍歷"、"打印"、"銷毀"等接口就不再單獨介紹了。後文的源碼中有給出它們的實現代碼,Please RTFSC(Read The Fucking Source Code)!

斜堆的C實現(完整源碼)

斜堆的頭文件(skewheap.h)

 1 #ifndef _SKEW_HEAP_H_
 2 #define _SKEW_HEAP_H_
 3 
 4 typedef int Type;
 5 
 6 typedef struct _SkewNode{
 7     Type   key;                    // 關鍵字(鍵值)
 8     struct _SkewNode *left;    // 左孩子
 9     struct _SkewNode *right;    // 右孩子
10 }SkewNode, *SkewHeap;
11 
12 // 前序遍歷"斜堆"
13 void preorder_skewheap(SkewHeap heap);
14 // 中序遍歷"斜堆"
15 void inorder_skewheap(SkewHeap heap);
16 // 後序遍歷"斜堆"
17 void postorder_skewheap(SkewHeap heap);
18 
19 // 獲取最小值(保存到pval中),成功返回0,失敗返回-1。
20 int skewheap_minimum(SkewHeap heap, int *pval);
21 // 合並"斜堆x"和"斜堆y",並返回合並後的新樹
22 SkewNode* merge_skewheap(SkewHeap x, SkewHeap y);
23 // 將結點插入到斜堆中,並返回根節點
24 SkewNode* insert_skewheap(SkewHeap heap, Type key);
25 // 刪除結點(key為節點的值),並返回根節點
26 SkewNode* delete_skewheap(SkewHeap heap);
27 
28 // 銷毀斜堆
29 void destroy_skewheap(SkewHeap heap);
30 
31 // 打印斜堆
32 void print_skewheap(SkewHeap heap);
33 
34 #endif

斜堆的實現文件(skewheap.c)

  1 /**
  2  * C語言實現的斜堆
  3  *
  4  * @author skywang
  5  * @date 2014/03/31
  6  */
  7 
  8 #include <stdio.h>
  9 #include <stdlib.h>
 10 #include "skewheap.h"
 11 
 12 /*
 13  * 前序遍歷"斜堆"
 14  */
 15 void preorder_skewheap(SkewHeap heap)
 16 {
 17     if(heap != NULL)
 18     {
 19         printf("%d ", heap->key);
 20         preorder_skewheap(heap->left);
 21         preorder_skewheap(heap->right);
 22     }
 23 }
 24 
 25 /*
 26  * 中序遍歷"斜堆"
 27  */
 28 void inorder_skewheap(SkewHeap heap)
 29 {
 30     if(heap != NULL)
 31     {
 32         inorder_skewheap(heap->left);
 33         printf("%d ", heap->key);
 34         inorder_skewheap(heap->right);
 35     }
 36 }
 37 
 38 /*
 39  * 後序遍歷"斜堆"
 40  */
 41 void postorder_skewheap(SkewHeap heap)
 42 {
 43     if(heap != NULL)
 44     {
 45         postorder_skewheap(heap->left);
 46         postorder_skewheap(heap->right);
 47         printf("%d ", heap->key);
 48     }
 49 }
 50 
 51 /*
 52  * 交換兩個節點的內容
 53  */
 54 static void swap_skewheap_node(SkewNode *x, SkewNode *y)
 55 {
 56     SkewNode tmp = *x;
 57     *x = *y;
 58     *y = tmp;
 59 }
 60 
 61 /* 
 62  * 獲取最小值
 63  *
 64  * 返回值:
 65  *    成功返回0,失敗返回-1
 66  */
 67 int skewheap_minimum(SkewHeap heap, int *pval)
 68 {
 69     if (heap == NULL)
 70         return -1;
 71 
 72     *pval = heap->key;
 73 
 74     return 0;
 75 }
 76  
 77 /* 
 78  * 合並"斜堆x"和"斜堆y"
 79  *
 80  * 返回值:
 81  *     合並得到的樹的根節點
 82  */
 83 SkewNode* merge_skewheap(SkewHeap x, SkewHeap y)
 84 {
 85     if(x == NULL)
 86         return y;
 87     if(y == NULL)
 88         return x;
 89 
 90     // 合並x和y時,將x作為合並後的樹的根;
 91     // 這裏的操作是保證: x的key < y的key
 92     if(x->key > y->key)
 93         swap_skewheap_node(x, y);
 94 
 95     // 將x的右孩子和y合並,
 96     // 合並後直接交換x的左右孩子,而不需要像左傾堆一樣考慮它們的npl。
 97     SkewNode *tmp = merge_skewheap(x->right, y);
 98     x->right = x->left;
 99     x->left  = tmp;
100 
101     return x;
102 }
103 
104 /* 
105  * 新建結點(key),並將其插入到斜堆中
106  *
107  * 參數說明:
108  *     heap 斜堆的根結點
109  *     key 插入結點的鍵值
110  * 返回值:
111  *     根節點
112  */
113 SkewNode* insert_skewheap(SkewHeap heap, Type key)
114 {
115     SkewNode *node;    // 新建結點
116 
117     // 如果新建結點失敗,則返回。
118     if ((node = (SkewNode *)malloc(sizeof(SkewNode))) == NULL)
119         return heap;
120     node->key = key;
121     node->left = node->right = NULL;
122 
123     return merge_skewheap(heap, node);
124 }
125 
126 /* 
127  * 取出根節點
128  *
129  * 返回值:
130  *     取出根節點後的新樹的根節點
131  */
132 SkewNode* delete_skewheap(SkewHeap heap)
133 {
134     SkewNode *l = heap->left;
135     SkewNode *r = heap->right;
136 
137     // 刪除根節點
138     free(heap);
139 
140     return merge_skewheap(l, r); // 返回左右子樹合並後的新樹
141 }
142 
143 /*
144  * 銷毀斜堆
145  */
146 void destroy_skewheap(SkewHeap heap)
147 {
148     if (heap==NULL)
149         return ;
150 
151     if (heap->left != NULL)
152         destroy_skewheap(heap->left);
153     if (heap->right != NULL)
154         destroy_skewheap(heap->right);
155 
156     free(heap);
157 }
158 
159 /*
160  * 打印"斜堆"
161  *
162  * heap       -- 斜堆的節點
163  * key        -- 節點的鍵值 
164  * direction  --  0,表示該節點是根節點;
165  *               -1,表示該節點是它的父結點的左孩子;
166  *                1,表示該節點是它的父結點的右孩子。
167  */
168 static void skewheap_print(SkewHeap heap, Type key, int direction)
169 {
170     if(heap != NULL)
171     {
172         if(direction==0)    // heap是根節點
173             printf("%2d is root\n", heap->key);
174         else                // heap是分支節點
175             printf("%2d is %2d‘s %6s child\n", heap->key, key, direction==1?"right" : "left");
176 
177         skewheap_print(heap->left, heap->key, -1);
178         skewheap_print(heap->right,heap->key,  1);
179     }
180 }
181 
182 void print_skewheap(SkewHeap heap)
183 {
184     if (heap != NULL)
185         skewheap_print(heap, heap->key,  0);
186 }

斜堆的測試程序(skewheap_test.c)

 1 /**
 2  * C語言實現的斜堆
 3  *
 4  * @author skywang
 5  * @date 2014/03/31
 6  */
 7 
 8 #include <stdio.h>
 9 #include "skewheap.h"
10 
11 #define LENGTH(a) ( (sizeof(a)) / (sizeof(a[0])) )
12 
13 void main()
14 {
15     int i;
16     int a[]= {10,40,24,30,36,20,12,16};
17     int b[]= {17,13,11,15,19,21,23};
18     int alen=LENGTH(a);
19     int blen=LENGTH(b);
20     SkewHeap ha,hb;
21 
22     ha=hb=NULL;
23 
24     printf("== 斜堆(ha)中依次添加: ");
25     for(i=0; i<alen; i++)
26     {
27         printf("%d ", a[i]);
28         ha = insert_skewheap(ha, a[i]);
29     }
30     printf("\n== 斜堆(ha)的詳細信息: \n");
31     print_skewheap(ha);
32 
33 
34     printf("\n== 斜堆(hb)中依次添加: ");
35     for(i=0; i<blen; i++)
36     {
37         printf("%d ", b[i]);
38         hb = insert_skewheap(hb, b[i]);
39     }
40     printf("\n== 斜堆(hb)的詳細信息: \n");
41     print_skewheap(hb);
42 
43     // 將"斜堆hb"合並到"斜堆ha"中。
44     ha = merge_skewheap(ha, hb);
45     printf("\n== 合並ha和hb後的詳細信息: \n");
46     print_skewheap(ha);
47 
48 
49     // 銷毀斜堆
50     destroy_skewheap(ha);
51 }

斜堆的C測試程序

斜堆的測試程序已經包含在它的實現文件(skewheap_test.c)中了,這裏僅給出它的運行結果:

技術分享圖片
== 斜堆(ha)中依次添加: 10 40 24 30 36 20 12 16 
== 斜堆(ha)的詳細信息: 
10 is root
16 is 10s   left child
20 is 16s   left child
30 is 20s   left child
40 is 30s   left child
12 is 10s  right child
24 is 12s   left child
36 is 24s   left child

== 斜堆(hb)中依次添加: 17 13 11 15 19 21 23 
== 斜堆(hb)的詳細信息: 
11 is root
13 is 11s   left child
17 is 13s   left child
23 is 17s   left child
19 is 13s  right child
15 is 11s  right child
21 is 15s   left child

== 合並ha和hb後的詳細信息: 
10 is root
11 is 10s   left child
12 is 11s   left child
15 is 12s   left child
21 is 15s   left child
24 is 12s  right child
36 is 24s   left child
13 is 11s  right child
17 is 13s   left child
23 is 17s   left child
19 is 13s  right child
16 is 10s  right child
20 is 16s   left child
30 is 20s   left child
40 is 30s   left child
技術分享圖片

深度解析(十二)斜堆