9.1 最小-最大堆 node
雙端優先隊列是一種支持以下操做的數據結構: 算法
1. 插入一個具備任意關鍵字值的元素. 編程
2. 刪除關鍵字值最大的元素. 服務器
3. 刪除關鍵字值最小的元素. 數據結構
當僅支持插入和其中的一種刪除操做時,能夠採用最大堆或者最小堆.而最小-最大堆能夠同時支持上述所有操做. 數據結構和算法
定義:最小-最大堆是一個知足以下條件的徹底二叉樹:該二叉樹不爲空,其上的每一個元素都有一個稱爲關鍵字的域.二叉樹的各層交替爲最小層和最大層,且根結點位於最小層.設x是最小-最大堆的任意一個節點,若是x位於最小層,那麼x就是以x爲根結點的二叉樹中關鍵字最小的結點,稱該結點爲最小結點.相似地,若是x位於最大層,那麼x就是以x爲根結點的二叉樹中關鍵字值最大的結點,稱該結點爲最大結點. 函數
9.1.2 最小-最大堆插入 學習
#include <stdio.h> #include <math.h> #include <string.h> #define MAX_SIZE 100 //備註:這裏下標從1開始 void swap( int *px, int *py ); /*最小-最大堆的插入*/ void min_max_insert( int heap[], int len, int item ); /*判斷父結點是在最小堆仍是最大堆,如果最小堆返回0,最大堆返回1*/ int level( int parent ); /*從最大結點開始查找合適位置*/ void verify_max( int heap[], int i, int item ); /*從最小結點開始查找合適位置*/ void verify_min( int heap[], int i, int item ); int main( void ) { int arr[ MAX_SIZE ]; int len = 0; int i = 0; memset( arr, 0, sizeof( int ) * MAX_SIZE ); len += 1; min_max_insert( arr, len, 7 ); len += 1; min_max_insert( arr, len, 70 ); len += 1; min_max_insert( arr, len, 40 ); len += 1; min_max_insert( arr, len, 30 ); len += 1; min_max_insert( arr, len, 9 ); len += 1; min_max_insert( arr, len, 10 ); len += 1; min_max_insert( arr, len, 15 ); len += 1; min_max_insert( arr, len, 45 ); len += 1; min_max_insert( arr, len, 50 ); len += 1; min_max_insert( arr, len, 30 ); len += 1; min_max_insert( arr, len, 20 ); len += 1; min_max_insert( arr, len, 12 ); len += 1; min_max_insert( arr, len, 5 ); for ( i = 0; i <= len; i++ ){ printf("%d ", arr[ i ] ); if ( 0 == ( i + 1 ) % 5 ){ printf("\n"); } } printf("\n"); return 0; } void swap( int *px, int *py ) { int temp = *px; *px = *py; *py = temp; } void min_max_insert( int heap[], int len, int item ) { int parent; if ( MAX_SIZE == len ){ fprintf( stderr, "the heap is full\n" ); return; } parent = len / 2; if ( !parent ){ heap[ 1 ] = item; } else{ switch( level( parent ) ){ case 0: if ( item < heap[ parent ] ){ heap[ len ] = heap[ parent ]; verify_min( heap, parent, item ); } else{ verify_max( heap, len, item ); } break; case 1: if ( item > heap[ parent ] ){ heap[ len ] = heap[ parent ]; verify_max( heap, parent, item ); } else{ verify_min( heap, len, item ); } } } } int level( int parent ) { return (int)( log( parent ) / log( 2 ) ) % 2; } void verify_max( int heap[], int i, int item ) { int grandparent = i / 4; while ( grandparent ){ if ( item > heap[ grandparent ] ){ heap[ i ] = heap[ grandparent ]; i = grandparent; grandparent /= 4; } else{ break; } } heap[ i ] = item; } void verify_min( int heap[], int i, int item ) { int grandparent = i / 4; while ( grandparent ){ if ( item < heap[ grandparent ] ){ heap[ i ] = heap[ grandparent ]; i = grandparent; grandparent /= 4; } else{ break; } } heap[ i ] = item; }
程序輸出: 3d
這裏分析不太好弄.書上能夠經過畫圖來解釋.因此這裏簡單解釋一下代碼中各個函數的做用: 指針
1. level函數:判斷parent是在最小層仍是在最大層
2. verify_max:從最大層開始執行查找.
3. verify_min:從最小層開始執行查找.
因此,若是一個要插入的節點的父節點爲最小層,而自己又小於父節點,則須要執行如下操做:
這樣就能夠保證從最小層開始執行查找.
插入節點實現了雙端隊列的第一條性質:插入一個具備任意關鍵字值的元素.
如今咱們來實現剩餘的兩條性質:1. 刪除關鍵字值最大的元素和刪除關鍵字值最小的元素.
int delete_min( int heap[], int len ) { int i, last, k, parent; int x; if ( !len ){ fprintf( stderr, "the heap is empty\n" ); return INT_MAX; } heap[ 0 ] = heap[ 1 ]; x = heap[ len-- ]; for ( i = 1, last = len / 2; i <= last; ){ k = min_child_grandchild( heap, i, len ); /*新的根節點爲最小結點*/ if ( x <= heap[ k ] ){ break; } heap[ i ] = heap[ k ]; if ( k <= 2 * i + 1 ){ i = k; break; } parent = k / 2; if ( x > heap[ parent ] ){ swap( &heap[ parent ], &x ); } i = k; } heap[ i ] = x; return heap[ 0 ]; } int min_child_grandchild( int arr[], int parent, int len ) { int min_value = INT_MAX; int i = 2 * parent; int min_index = 0; for ( ; i <= len; i++ ){ if ( arr[ i ] < min_value ){ min_value = arr[ i ]; min_index = i; } } return min_index; }
刪除最大結點:
int delete_max( int arr[], int len ) { int max_index = 0; int i, x, last, k, parent; if ( !len ){ fprintf( stderr, "the heap is empty\n" ); return INT_MAX; } if ( arr[ 2 ] > arr[ 3 ] ){ arr[ 0 ] = arr[ 2 ]; max_index = 2; } else{ arr[ 0 ] = arr[ 3 ]; max_index = 3; } x = arr[ len-- ]; for ( i = max_index, last = len / 2; i <= last ; ){ k = max_child_grandchild( arr, i, len ); if ( x >= arr[ k ] ){ break; } arr[ i ] = arr[ k ]; if ( k <= 2 * i + 1 ){ i = k; break; } parent = k / 2; if ( x < arr[ parent ] ){ swap( &arr[ parent ], &x ); } i = k; } arr[ i ] = x; return arr[ 0 ]; } int max_child_grandchild( int arr[], int parent, int len ) { int max_value = INT_MIN; int i = 2 * parent; int max_index = 0; for ( ; i <= len; i++ ){ if ( arr[ i ] > max_value ){ max_value = arr[ i ]; max_index = i; } } return max_index; }
整個程序以下:
#include <stdio.h> #include <math.h> #include <string.h> #include <stdlib.h> #define MAX_SIZE 100 //備註:這裏下標從1開始 void swap( int *px, int *py ); /*最小-最大堆的插入*/ void min_max_insert( int heap[], int len, int item ); /*判斷父結點是在最小堆仍是最大堆,如果最小堆返回0,最大堆返回1*/ int level( int parent ); /*從最大結點開始查找合適位置*/ void verify_max( int heap[], int i, int item ); /*從最小結點開始查找合適位置*/ void verify_min( int heap[], int i, int item ); /*刪除最小關鍵字值結點*/ int delete_min( int heap[], int len ); /*肯定parent結點的全部兒子和後代結點中關鍵字值最小的結點*/ int min_child_grandchild( int arr[], int parent, int len ); /*刪除最大關鍵字值結點*/ int delete_max( int heap[], int len ); /*肯定parent結點的全部兒子和後代結點中關鍵字值最大的結點*/ int max_child_grandchild( int arr[], int parent, int len ); int main( void ) { int arr[ MAX_SIZE ]; int len = 0; int i = 0; int min_value, max_value; memset( arr, 0, sizeof( int ) * MAX_SIZE ); len += 1; min_max_insert( arr, len, 7 ); len += 1; min_max_insert( arr, len, 70 ); len += 1; min_max_insert( arr, len, 40 ); len += 1; min_max_insert( arr, len, 30 ); len += 1; min_max_insert( arr, len, 9 ); len += 1; min_max_insert( arr, len, 10 ); len += 1; min_max_insert( arr, len, 15 ); len += 1; min_max_insert( arr, len, 45 ); len += 1; min_max_insert( arr, len, 50 ); len += 1; min_max_insert( arr, len, 30 ); len += 1; min_max_insert( arr, len, 20 ); len += 1; min_max_insert( arr, len, 12 ); len += 1; min_max_insert( arr, len, 5 ); for ( i = 1; i <= len; i++ ){ printf("%d ", arr[ i ] ); if ( 0 == ( i + 1 ) % 5 ){ printf("\n"); } } min_value = delete_min( arr, len ); printf("\nthe min value is:%d\n", min_value ); len -= 1; for ( i = 1; i <= len; i++ ){ printf("%d ", arr[ i ] ); if ( 0 == ( i + 1 ) % 5 ){ printf("\n"); } } max_value = delete_max( arr, len ); printf("\nthe max value is:%d\n", max_value ); len -= 1; for ( i = 1; i <= len; i++ ){ printf("%d ", arr[ i ] ); if ( 0 == ( i + 1 ) % 5 ){ printf("\n"); } } printf("\n"); return 0; } void swap( int *px, int *py ) { int temp = *px; *px = *py; *py = temp; } void min_max_insert( int heap[], int len, int item ) { int parent; if ( MAX_SIZE == len ){ fprintf( stderr, "the heap is full\n" ); return; } parent = len / 2; if ( !parent ){ heap[ 1 ] = item; } else{ switch( level( parent ) ){ case 0: if ( item < heap[ parent ] ){ heap[ len ] = heap[ parent ]; verify_min( heap, parent, item ); } else{ verify_max( heap, len, item ); } break; case 1: if ( item > heap[ parent ] ){ heap[ len ] = heap[ parent ]; verify_max( heap, parent, item ); } else{ verify_min( heap, len, item ); } } } } int level( int parent ) { return (int)( log( parent ) / log( 2 ) ) % 2; } void verify_max( int heap[], int i, int item ) { int grandparent = i / 4; while ( grandparent ){ if ( item > heap[ grandparent ] ){ heap[ i ] = heap[ grandparent ]; i = grandparent; grandparent /= 4; } else{ break; } } heap[ i ] = item; } void verify_min( int heap[], int i, int item ) { int grandparent = i / 4; while ( grandparent ){ if ( item < heap[ grandparent ] ){ heap[ i ] = heap[ grandparent ]; i = grandparent; grandparent /= 4; } else{ break; } } heap[ i ] = item; } int delete_min( int heap[], int len ) { int i, last, k, parent; int x; if ( !len ){ fprintf( stderr, "the heap is empty\n" ); return INT_MAX; } heap[ 0 ] = heap[ 1 ]; x = heap[ len-- ]; for ( i = 1, last = len / 2; i <= last; ){ k = min_child_grandchild( heap, i, len ); /*新的根節點爲最小結點*/ if ( x <= heap[ k ] ){ break; } heap[ i ] = heap[ k ]; if ( k <= 2 * i + 1 ){ i = k; break; } parent = k / 2; if ( x > heap[ parent ] ){ swap( &heap[ parent ], &x ); } i = k; } heap[ i ] = x; return heap[ 0 ]; } int min_child_grandchild( int arr[], int parent, int len ) { int min_value = INT_MAX; int i = 2 * parent; int min_index = 0; for ( ; i <= len; i++ ){ if ( arr[ i ] < min_value ){ min_value = arr[ i ]; min_index = i; } } return min_index; } int delete_max( int arr[], int len ) { int max_index = 0; int i, x, last, k, parent; if ( !len ){ fprintf( stderr, "the heap is empty\n" ); return INT_MAX; } if ( arr[ 2 ] > arr[ 3 ] ){ arr[ 0 ] = arr[ 2 ]; max_index = 2; } else{ arr[ 0 ] = arr[ 3 ]; max_index = 3; } x = arr[ len-- ]; for ( i = max_index, last = len / 2; i <= last ; ){ k = max_child_grandchild( arr, i, len ); if ( x >= arr[ k ] ){ break; } arr[ i ] = arr[ k ]; if ( k <= 2 * i + 1 ){ i = k; break; } parent = k / 2; if ( x < arr[ parent ] ){ swap( &arr[ parent ], &x ); } i = k; } arr[ i ] = x; return arr[ 0 ]; } int max_child_grandchild( int arr[], int parent, int len ) { int max_value = INT_MIN; int i = 2 * parent; int max_index = 0; for ( ; i <= len; i++ ){ if ( arr[ i ] > max_value ){ max_value = arr[ i ]; max_index = i; } } return max_index; }
程序輸出:
具體請參考<數據結構(C語言版)>上面的內容.
9.2 雙端堆
定義:雙端堆是一顆徹底二叉樹,該徹底二叉樹要麼爲空,要麼同時知足下列性質:
根結點不包含元素.
左子樹是一個最小堆
右子樹是一個最大堆
若是右子樹不爲空,則令i是左子樹中任意一個結點,j是i在右子樹中的對應結點.若是i在右子樹中的對應結點j不存在,則令j爲i的父結點在右子樹中的對應結點.對於結點i和j,結點i的關鍵字值小於等於結點j的關鍵字值.
第一段程序:插入結點:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #define MAX_SIZE 100 /*雙端堆的插入操做*/ void deap_insert( int deap[], int len, int x ); /*若n位於雙端堆中的最大堆,則返回1,不然返回0*/ int max_heap( int n ); /*計算最小堆結點n的父節點對應的最大堆結點.*/ int max_partner( int n ); /*計算最大堆結點n對應的最小堆結點*/ int min_partner( int n ); /*將值x插入最小堆指定的位置*/ void min_insert( int deap[], int index, int x ); /*將值x插入最大堆指定的位置*/ void max_insert( int deap[], int index, int x ); /*交換兩個數*/ void swap( int *px, int *py ); int main( void ) { int len = 1; int i = 0; int deap[ MAX_SIZE ]; memset( deap, 0, sizeof( int ) * MAX_SIZE ); len += 1; deap_insert( deap, len, 5 ); len += 1; deap_insert( deap, len, 45 ); len += 1; deap_insert( deap, len, 10 ); len += 1; deap_insert( deap, len, 8 ); len += 1; deap_insert( deap, len, 25 ); len += 1; deap_insert( deap, len, 40 ); len += 1; deap_insert( deap, len, 15 ); len += 1; deap_insert( deap, len, 19 ); len += 1; deap_insert( deap, len, 9 ); len += 1; deap_insert( deap, len, 30 ); len += 1; deap_insert( deap, len, 20 ); len += 1; deap_insert( deap, len, 4 ); for ( i = 2; i <= len; i++ ){ printf("%d ", deap[ i ] ); if ( 0 == ( i % 6 ) ){ printf("\n"); } } return 0; } void deap_insert( int deap[], int len, int x ) { int i; if ( MAX_SIZE == len ){ fprintf(stderr, "the heap is full\n"); return; } if ( 2 == len ){ deap[ 2 ] = x; } else switch( max_heap( len ) ){ case 0: i = max_partner( len ); if ( x > deap[ i ] ){ deap[ len ] = deap[ i ]; max_insert( deap, i, x ); } else{ min_insert( deap, len, x ); } break; case 1: i = min_partner( len ); if ( x < deap[ i ] ){ deap[ len ] = deap[ i ]; min_insert( deap, i, x ); } else{ max_insert( deap, len, x ); } } } /*頭結點爲空結點,而且索引爲1.判斷索引n是位於最小堆仍是最大堆*/ int max_heap( int n ) { int exp = ( int )( log( n ) / log( 2 ) ); if ( ( pow( 2.0, exp ) <= n ) && ( n < pow( 2.0, exp ) + pow( 2.0, exp - 1 ) ) ){ return 0; } return 1; } /*計算最小堆結點n的父節點對應的最大堆結點.*/ int max_partner( int n ) { return ( int )( ( n + pow( 2.0, ( int )( log( n ) / log( 2 ) ) - 1 ) ) / 2 ); } /*計算最大堆結點n對應的最小堆結點*/ int min_partner( int n ) { return n - ( int )( pow( 2.0, ( int )( log( n ) / log( 2 ) ) - 1 ) ); } /*將值x插入最小堆指定的位置*/ void min_insert( int deap[], int index, int x ) { int parent = index / 2; deap[ index ] = x; if ( 1 == parent ){ deap[ index ] = x; } else{ while ( parent > 1 ){ if ( deap[ index ] < deap[ parent ] ){ swap( &deap[ index ], &deap[ parent ] ); } index /= 2; parent = index / 2; } } } /*將值x插入最大堆指定的位置*/ void max_insert( int deap[], int index, int x ) { int parent = index / 2; deap[ index ] = x; if ( 1 == parent ){ deap[ index ] = x; } else{ while ( parent > 1 ){ if ( deap[ index ] > deap[ parent ] ){ swap( &deap[ index ], &deap[ parent ] ); } index /= 2; parent = index / 2; } } } /*交換兩個數*/ void swap( int *px, int *py ) { int temp = *px; *px = *py; *py = temp; }
程序輸出:
根據書上一步步寫出來.剛開始學習C語言的時候就應該這樣,不急不躁.先學會如何編程,而後再想如何編好程序.
雙端堆的做用就是方便刪除最小最大值.代碼以下:
備註:在刪除最小元素或者最大元素的時候,存在一個很是隱蔽的BUG是:假設我要在最大堆中插入20,而在最小堆中對應的結點是9,那麼符合性質4.可是結點9的孩子一個是30,一個是40怎麼辦?他們的孩子結點大於20,不符合性質4,這時候就須要進行特殊的處理.代碼以下:
/*刪除最小元素,這裏len是已經刪除掉元素後的長度*/ int deap_delete_min( int deap[], int len ) { int temp, i, j, k; if ( len < 2 ){ fprintf(stderr, "the deap is empty\n"); return INT_MAX; } deap[ 0 ] = deap[ 2 ]; temp = deap[ len + 1 ]; for ( i = 2; i * 2 <= len; ){ j = i * 2; if ( j + 1 <= len ){ if ( deap[ j ] > deap[ j + 1 ] ){ j++; } } deap[ i ] = deap[ j ]; i = j; } deap[ i ] = temp; k = max_partner( i ); /*判斷最小堆的孩子結點小於最大堆要插入的結點*/ while ( k <= len ){ if ( deap[ i ] > deap[ k ] ){ deap[ i ] = deap[ k ]; max_insert( deap, k, temp ); break; } k *= 2; if ( k + 1 <= len ){ if ( deap[ k ] > deap[ k + 1 ] ){ k += 1; } } } return deap[ 0 ]; } /*刪除最大元素*/ int deap_delete_max( int deap[], int len ) { int temp, i, j, k; if ( len < 3 ){ fprintf(stderr, "the deap is empty\n"); return INT_MAX; } deap[ 0 ] = deap[ 3 ]; temp = deap[ len + 1 ]; for ( i = 3; i * 2 <= len; ){ j = i * 2; if ( j + 1 <= len ){ if ( deap[ j ] < deap[ j + 1 ] ){ j++; } } deap[ i ] = deap[ j ]; i = j; } deap[ i ] = temp; k = min_partner( i ); /*判斷最大堆的孩子結點大於最小堆要插入的結點*/ while ( k <= len ){ if ( deap[ i ] < deap[ k ] ){ deap[ i ] = deap[ k ]; min_insert( deap, k, temp ); break; } k *= 2; if ( k + 1 <= len ){ if ( deap[ k ] < deap[ k + 1 ] ){ k += 1; } } } return deap[ 0 ]; }
全部的代碼羅列以下:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #define MAX_SIZE 100 /*雙端堆的插入操做*/ void deap_insert( int deap[], int len, int x ); /*若n位於雙端堆中的最大堆,則返回1,不然返回0*/ int max_heap( int n ); /*計算最小堆結點n的父節點對應的最大堆結點.*/ int max_partner( int n ); /*計算最大堆結點n對應的最小堆結點*/ int min_partner( int n ); /*將值x插入最小堆指定的位置*/ void min_insert( int deap[], int index, int x ); /*將值x插入最大堆指定的位置*/ void max_insert( int deap[], int index, int x ); /*交換兩個數*/ void swap( int *px, int *py ); /*刪除最小元素*/ int deap_delete_min( int deap[], int len ); /*刪除最大元素*/ int deap_delete_max( int deap[], int len ); int main( void ) { int len = 1; int i = 0; int deap[ MAX_SIZE ]; int minValue = 0; int maxValue = 0; memset( deap, 0, sizeof( int ) * MAX_SIZE ); len += 1; deap_insert( deap, len, 5 ); len += 1; deap_insert( deap, len, 45 ); len += 1; deap_insert( deap, len, 10 ); len += 1; deap_insert( deap, len, 8 ); len += 1; deap_insert( deap, len, 25 ); len += 1; deap_insert( deap, len, 40 ); len += 1; deap_insert( deap, len, 15 ); len += 1; deap_insert( deap, len, 19 ); len += 1; deap_insert( deap, len, 9 ); len += 1; deap_insert( deap, len, 30 ); len += 1; deap_insert( deap, len, 20 ); len += 1; deap_insert( deap, len, 4 ); len -= 1; minValue = deap_delete_min( deap, len ); len -= 1; maxValue = deap_delete_max( deap, len ); printf("the min value is : %d\n", minValue ); printf("the max value is : %d\n", maxValue ); for ( i = 2; i <= len; i++ ){ printf("%3d ", deap[ i ] ); if ( 0 == ( i % 6 ) ){ printf("\n"); } } printf("\n"); return 0; } void deap_insert( int deap[], int len, int x ) { int i; if ( MAX_SIZE == len ){ fprintf(stderr, "the heap is full\n"); return; } if ( 2 == len ){ deap[ 2 ] = x; } else switch( max_heap( len ) ){ case 0: i = max_partner( len ); if ( x > deap[ i ] ){ deap[ len ] = deap[ i ]; max_insert( deap, i, x ); } else{ min_insert( deap, len, x ); } break; case 1: i = min_partner( len ); if ( x < deap[ i ] ){ deap[ len ] = deap[ i ]; min_insert( deap, i, x ); } else{ max_insert( deap, len, x ); } } } /*頭結點爲空結點,而且索引爲1.判斷索引n是位於最小堆仍是最大堆*/ int max_heap( int n ) { int exp = ( int )( log( n ) / log( 2 ) ); if ( ( pow( 2.0, exp ) <= n ) && ( n < pow( 2.0, exp ) + pow( 2.0, exp - 1 ) ) ){ return 0; } return 1; } /*計算最小堆結點n的父節點對應的最大堆結點.*/ int max_partner( int n ) { return ( int )( ( n + pow( 2.0, ( int )( log( n ) / log( 2 ) ) - 1 ) ) / 2 ); } /*計算最大堆結點n對應的最小堆結點*/ int min_partner( int n ) { return n - ( int )( pow( 2.0, ( int )( log( n ) / log( 2 ) ) - 1 ) ); } /*將值x插入最小堆指定的位置*/ void min_insert( int deap[], int index, int x ) { int parent = index / 2; deap[ index ] = x; if ( 1 == parent ){ deap[ index ] = x; } else{ while ( parent > 1 ){ if ( deap[ index ] < deap[ parent ] ){ swap( &deap[ index ], &deap[ parent ] ); } index /= 2; parent = index / 2; } } } /*將值x插入最大堆指定的位置*/ void max_insert( int deap[], int index, int x ) { int parent = index / 2; deap[ index ] = x; if ( 1 == parent ){ deap[ index ] = x; } else{ while ( parent > 1 ){ if ( deap[ index ] > deap[ parent ] ){ swap( &deap[ index ], &deap[ parent ] ); } index /= 2; parent = index / 2; } } } /*交換兩個數*/ void swap( int *px, int *py ) { int temp = *px; *px = *py; *py = temp; } /*刪除最小元素,這裏len是已經刪除掉元素後的長度*/ int deap_delete_min( int deap[], int len ) { int temp, i, j, k; if ( len < 2 ){ fprintf(stderr, "the deap is empty\n"); return INT_MAX; } deap[ 0 ] = deap[ 2 ]; temp = deap[ len + 1 ]; for ( i = 2; i * 2 <= len; ){ j = i * 2; if ( j + 1 <= len ){ if ( deap[ j ] > deap[ j + 1 ] ){ j++; } } deap[ i ] = deap[ j ]; i = j; } deap[ i ] = temp; k = max_partner( i ); /*判斷最小堆的孩子結點小於最大堆要插入的結點*/ while ( k <= len ){ if ( deap[ i ] > deap[ k ] ){ deap[ i ] = deap[ k ]; max_insert( deap, k, temp ); break; } k *= 2; if ( k + 1 <= len ){ if ( deap[ k ] > deap[ k + 1 ] ){ k += 1; } } } return deap[ 0 ]; } /*刪除最大元素*/ int deap_delete_max( int deap[], int len ) { int temp, i, j, k; if ( len < 3 ){ fprintf(stderr, "the deap is empty\n"); return INT_MAX; } deap[ 0 ] = deap[ 3 ]; temp = deap[ len + 1 ]; for ( i = 3; i * 2 <= len; ){ j = i * 2; if ( j + 1 <= len ){ if ( deap[ j ] < deap[ j + 1 ] ){ j++; } } deap[ i ] = deap[ j ]; i = j; } deap[ i ] = temp; k = min_partner( i ); /*判斷最大堆的孩子結點大於最小堆要插入的結點*/ while ( k <= len ){ if ( deap[ i ] < deap[ k ] ){ deap[ i ] = deap[ k ]; min_insert( deap, k, temp ); break; } k *= 2; if ( k + 1 <= len ){ if ( deap[ k ] < deap[ k + 1 ] ){ k += 1; } } } return deap[ 0 ]; }
程序輸出:
9.3 左高樹
備註:在描述概念上書上有圖展現,可是我不知道怎麼畫好一幅圖(或者這樣會形成臃腫),因此闡述概念的時候不會很清晰,請參考<數據結構(C語言版)>這本書.
左高樹的一個應用是合併操做,應用場景是:當某個優先隊列的服務器關閉時,就須要將其與另外一個正在運行服務器的優先隊列合併.若是兩個隊列的元素總數爲n,則通常的堆結構的複雜度爲n,可是左高樹能夠達到log(n).
左高樹的簡單定義就是:左子樹比對應的右子樹高.它的數據結構中會有一項shortest,表示其結點到葉子節點的長度.用於判斷左子樹高於右子樹,即left.shortest >= right.shortest.
定義:最小(最大)左高樹是一棵左高樹,其中每一個內部結點的關鍵字值都不大於(不小於)該結點兒子結點(若是有的話)的關鍵字值.換句話說,一棵最小(最大)左高樹既是一棵左高樹,同時又是一棵最小(最大)樹.
程序用層序遍歷輸出.可是一個層序遍歷沒法推測出一棵樹的本來模樣,因此咱們提供了中序和前序輸出用於比較:
#include <stdio.h> #include <stdlib.h> /*宏定義相似於C++的模板技術,能夠不考慮交換的數據類型*/ #define SWAP( x, y, t ) ( (t) = (x),(x) = (y), (y) = (t) ) #define MAX_SIZE 100 typedef struct LEFTISTTREE{ struct LEFTISTTREE *leftChild; struct LEFTISTTREE *rightChild; int data; int shortest; } Leftisttree; Leftisttree *stack[ MAX_SIZE ]; int top = 0; int rear = 0; /*合併兩棵左高樹*/ void min_combine( Leftisttree **a, Leftisttree **b ); /*合併兩棵最小左高樹*/ void min_union( Leftisttree **a, Leftisttree **b ); /*初始化一個左高樹結點*/ void init_node( Leftisttree **b, int data ); /*打印一棵左高樹:層序輸出*/ void show_tree( Leftisttree *a ); /*打印一棵左高樹:中序輸出*/ void show_mid_tree( Leftisttree *a ); void show_pre_tree( Leftisttree *a ); int main( void ) { Leftisttree *a = NULL; Leftisttree *b = NULL; Leftisttree *c = NULL; init_node( &b, 2 ); min_combine( &a, &b ); init_node( &b, 7 ); min_combine( &a, &b ); init_node( &b, 50 ); min_combine( &a, &b ); init_node( &b, 11 ); min_combine( &a, &b ); init_node( &b, 80 ); min_combine( &a, &b ); init_node( &b, 13 ); min_combine( &a, &b ); printf("tree a is:\n"); show_tree( a ); printf("->NULL\n"); init_node( &b, 5 ); min_combine( &c, &b ); init_node( &b, 9 ); min_combine( &c, &b ); init_node( &b, 8 ); min_combine( &c, &b ); init_node( &b, 12 ); min_combine( &c, &b ); init_node( &b, 10 ); min_combine( &c, &b ); init_node( &b, 20 ); min_combine( &c, &b ); init_node( &b, 18 ); min_combine( &c, &b ); init_node( &b, 15 ); min_combine( &c, &b ); printf("\ntree c is:\n"); show_tree( c ); printf("->NULL\n"); min_combine( &a, &c ); printf("\nnew tree a is:\n"); show_tree( a ); printf("->NULL\n"); show_mid_tree( a ); printf("->NULL\n"); show_pre_tree( a ); printf("->NULL\n"); return 0; } void init_node( Leftisttree **b, int data ) { if ( !*b ){ *b = ( Leftisttree *)malloc( sizeof( Leftisttree ) ); } ( *b )->data = data; ( *b )->leftChild = ( *b )->rightChild = NULL; ( *b )->shortest = 1; } void show_tree( Leftisttree *tree ) { top = 0; rear = 0; stack[ rear++ ] = tree; while ( 1 ){ tree = stack[ top++ ]; if ( tree ){ printf("%d->", tree->data ); if ( tree->leftChild ){ stack[ rear++ ] = tree->leftChild; } if ( tree->rightChild ){ stack[ rear++ ] = tree->rightChild; } } else{ break; } } } void show_mid_tree( Leftisttree *a ) { if ( a ){ printf("%d->", a->data ); show_mid_tree( a->leftChild ); show_mid_tree( a->rightChild ); } } void show_pre_tree( Leftisttree *a ) { if ( a ){ show_pre_tree( a->leftChild ); printf("%d->", a->data ); show_pre_tree( a->rightChild ); } } void min_combine( Leftisttree **a, Leftisttree **b ) { if ( !*a ){ *a = *b; } else if ( *b ){ min_union( a, b ); } *b = NULL; } void min_union( Leftisttree **a, Leftisttree **b ) { Leftisttree *temp; if ( ( *a )->data > ( *b )->data ){ SWAP( *a, *b, temp ); } if ( !( *a )->rightChild ){ ( *a )->rightChild = *b; } else{ min_union( &( *a )->rightChild, b ); } if ( !( *a )->leftChild ){ ( *a )->leftChild = ( *a )->rightChild; ( *a )->rightChild = NULL; } else if ( ( *a )->leftChild->shortest < ( *a )->rightChild->shortest ){ SWAP( ( *a )->leftChild, ( *a )->rightChild, temp ); } ( *a )->shortest = ( !( *a )->rightChild ) ? 1 : ( *a )->rightChild->shortest + 1; }
程序輸出:
PS:若是你想刪除最小元素,則讓最小元素(根節點)的左子樹和右子樹合併便可.並且這裏用到的是指針的指針,能夠保證正確的修改.還記得嗎?C語言只支持傳值的.
關於二項堆和菲波那契堆,書上只有簡單描述,沒有具體的代碼,故略過.等看<數據結構和算法分析(C語言描述)>或者<算法導論>時候深刻了解.