堆 最小堆 最大堆 堆排序(小到大,大到小)

導航

1.瞭解什麼是堆
2.如何創建最小堆,最大堆
3.新增值在堆中如何進行
4.完整的堆排序,升序和降序(兩種方式)

————————————————————————————————————————

1.瞭解什麼是堆

樹型結構與圖型結構的差別:

看是否有迴路,無迴路的是樹型,有迴路的是圖型

在這裏插入圖片描述

滿二叉樹可以用數組進行存儲,層次遍歷順序存儲
設置其中父節點爲k,兒子節點(左結點,右節點) 其中左節點 爲2*k右節點爲 2 * k+1
高度爲log2^N

其中最典型的應用就是(也就是完全二叉樹)
在這裏插入圖片描述
完全二叉樹
在這裏插入圖片描述

————————————————————————————————————————

2.如何將堆轉化爲最小堆,最大堆

有兩層的直接用父結點來比較
三層的應該有倒數一層的右邊節點開始依次往下
在這裏插入圖片描述

代碼如下:(創建最小堆)

//這裏用的是數組的方式存儲堆
void siftdown(int i)  //輸入的是從i點往下找
{
	int t,flag=0;  //flag用來表示無需再進行往下查找 
	//從i位置開始,判斷是否有左孩子,並且沒有找到最佳位置 
	while(i*2<=n&&flag==0)
	{
		if(h[i]>h[i*2]) //比較父節點與左節點的大小 
			t = i*2;
		else
			t = i;
		if(i*2+1<=n){   //判斷是否存在右孩子 
			if(h[t]>h[i*2+1])  //比較上一次父節點與左節點的最小值與右孩子大小 
				t = i*2+1;
		}
		if(t!=i){  //判斷得到最小值是否爲父節點,是的話flag標誌1,不是的話交換,繼續往下找 
			swap(t,i);
			i=t;
		}
		else
			flag = 1;
	}
}

創建最大堆:只需要將父節點與左右孩子節點的判大小符號換一下就可以了

————————————————————————————————————————

3.新增值在堆中如何進行

先插入到堆中最後一個位置,在進行與父節點依次比對,找到最合適的位置
在這裏插入圖片描述
整個都用數組保存
代碼:

//這裏弄得是最小堆 
void siftup(int i)
{
	int i,flag=0;   //flag等於0表示當前子節點比父節點小 
	if(i==1) return;   //如果爲根節點直接返回 
	while(i!=1&&flag==0)   
	{ 
		if(h[i]<h[i/2])
			swap(i,i/2);
		else
			flag=1;
		i=i/2;    //讓子節點等於父節點的位置 
	}
} 

————————————————————————————————————————

4.完整的堆排序,升序和降序

輸入提示:
14
99 5 36 7 22 17 46 12 2 19 25 28 1 92

升序:

#include <stdio.h>
int h[101]; //用來存放堆的數組
int n;  //存放堆的個數

void swap(int x,int y)
{
	int t;
	t = h[x];
	h[x] = h[y];
	h[y] = t;
} 

void siftdown(int i)
{
	int t,flag=0;  //flag用來表示無需再進行往下查找 
	//從i位置開始,判斷是否有左孩子,並且沒有找到最佳位置 
	while(i*2<=n&&flag==0)
	{
		if(h[i]>h[i*2]) //比較父節點與左節點的大小 
			t = i*2;
		else
			t = i;
		if(i*2+1<=n){   //判斷是否存在右孩子 
			if(h[t]>h[i*2+1])  //比較上一次父節點與左節點的最小值與右孩子大小 
				t = i*2+1;
		}
		if(t!=i){  //判斷得到最小值是否爲父節點,是的話flag標誌1,不是的話交換,繼續往下找 
			swap(t,i);
			i=t;
		}
		else
			flag = 1;
	}
}

void create()
{
	int i;
	for(i=n/2;i>=1;i--)
		siftdown(i);
}

//用於返回最小值
int deletemax()
{
	int t;
	t=h[1];
	h[1]=h[n];
	n--;   //刪除一位
	siftdown(1);  //進行最小堆
	return t;
}
 
int main()
{
	int i,num;
	scanf("%d",&num);
	for(i=1;i<=num;i++)
		scanf("%d",&h[i]);
	n = num;
	
	//建立最小堆
	create();
	
	//刪除頂部元素,接着將最後一個元素放入第一個頂點中,輸出刪除的元素
	for(i=1;i<=num;i++)
		printf("%d ",deletemax()); 
	return 0;
} 

其中deletemax()函數作用是每次返回最小值,並且將最後一個值移入到根節點再次進行最小堆,這樣結束時數組中數據已不再是原來的。
若想用這樣的來求降序也是可以的,只需要建立最大堆就行了,其餘都一樣

另一種方式來求解升序和降序

//之前創建的是最大堆 
void headsort()
{
	while(n>1)
	{
		swap(1,n);     //將第一個與最後一個進行交換,此時最後一個點爲最大值 
		n--;     //下次建立最大堆不帶上最後一個值 
		siftdown(1);  //進行最大堆 
	}
	
}

求解升序另一種方法完整

#include <stdio.h>
int h[101]; //用來存放堆的數組
int n;  //存放堆的個數

void swap(int x,int y)
{
	int t;
	t = h[x];
	h[x] = h[y];
	h[y] = t;
} 

void siftdown(int i)
{
	int t,flag=0;  //flag用來表示無需再進行往下查找 
	//從i位置開始,判斷是否有左孩子,並且沒有找到最佳位置 
	while(i*2<=n&&flag==0)
	{
		if(h[i]<h[i*2]) //比較父節點與左節點的大小 
			t = i*2;
		else
			t = i;
		if(i*2+1<=n){   //判斷是否存在右孩子 
			if(h[t]<h[i*2+1])  //比較上一次父節點與左節點的最小值與右孩子大小 
				t = i*2+1;
		}
		if(t!=i){  //判斷得到最小值是否爲父節點,是的話flag標誌1,不是的話交換,繼續往下找 
			swap(t,i);
			i=t;
		}
		else
			flag = 1;
	}
}

//開始創建堆 
void create()
{
	int i;
	for(i=n/2;i>=1;i--)
		siftdown(i);
}

//之前創建的是最大堆 
void headsort()
{
	while(n>1)
	{
		swap(1,n);     //將第一個與最後一個進行交換,此時最後一個點爲最大值 
		n--;     //下次建立最大堆不帶上最後一個值 
		siftdown(1);  //進行最大堆 
	}
	
}
 
int main()
{
	int i,num;
	scanf("%d",&num);
	for(i=1;i<=num;i++)
		scanf("%d",&h[i]);
	n = num;
	
	//建立最小堆
	create();
	
	headsort(); 
	
	//刪除頂部元素,接着將最後一個元素放入第一個頂點中,輸出刪除的元素
	for(i=1;i<=num;i++)
		printf("%d ",h[i]); 
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章