數據結構(C語言版)第九章:堆結構

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 雙端堆

定義:雙端堆是一顆徹底二叉樹,該徹底二叉樹要麼爲空,要麼同時知足下列性質:

  1. 根結點不包含元素.

  2. 左子樹是一個最小堆

  3. 右子樹是一個最大堆

  4. 若是右子樹不爲空,則令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語言描述)>或者<算法導論>時候深刻了解.

相關文章
相關標籤/搜索