天天看點

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

導航

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;
} 
           

繼續閱讀