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

 

概要

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

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

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


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

 

斜堆的介紹

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

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

 

斜堆的基本操做

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是斜堆對應的節點類。spa

 

2. 合併3d

/* 
 * 合併"斜堆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)是遞歸實現的。code

 

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
View Code

斜堆的實現文件(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 }
View Code

斜堆的測試程序(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 }
View Code

 

斜堆的C測試程序

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

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

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

== 合併ha和hb後的詳細信息: 
10 is root
11 is 10's   left child
12 is 11's   left child
15 is 12's   left child
21 is 15's   left child
24 is 12's  right child
36 is 24's   left child
13 is 11's  right child
17 is 13's   left child
23 is 17's   left child
19 is 13's  right child
16 is 10's  right child
20 is 16's   left child
30 is 20's   left child
40 is 30's   left child
相關文章
相關標籤/搜索