要想完全懂堆排序,首先,你得知道堆这个结构,其实就是一个完全二叉树。
图片:
这就是一个完全二叉树。
其次就是大根堆和小跟堆得概念
大根堆:就是每个父节点的数都比他的左孩子和右孩子的数大
小根堆:就是每个父节点的数都比他的左孩子和右孩子的数小
注意:堆的所有操作都可以在数组中来进行操作
完全二叉树一个节点(i)的左孩子的下标是:i*2+1 右孩子的小标是:i*2+2 其根节点的下标是:(i-1)/2
还有,你还得知道堆的几种基本操作
1:当往堆中加入一个数后,把这个堆调成大根堆或小根堆,加堆操作
2:当堆中一个数变小后,调整为大根堆
:3:弹出堆顶的的数后,重新调整为大根堆,减堆操作
首先讲第一种操作(以大根堆为例):
思路,每次比较i位置上的数和它父节点上的数的大小,如果i位置上的数大于其父节点上的数,那么就交换这两个位置上的数,然后重复上述过程,直到其父节点为0时结束。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int a[100000];
int n;
void swap(int i,int j)
{
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
void heapinsert(int i)//将数组中第i个数放入大根堆中
{
int index=i;//用index表示当前位置,每次都会更新index
while (a[index]>a[(index-1)/2])//当前数大于他的父节点的数
{
swap(index,(index-1)/2);//交换这两个位置上的数
index=(index-1)/2;//把index设置为父节点的位置,继续执行上述过程
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
heapinsert(i);//每次加入一个数,都会进行一次heapisert操作,调整为大根堆
}
for(int i=0;i<n;i++)
cout<<a[i]<<" ";
}
第二种操作(大根堆为例)
基本思路:先比较i位置(数变小位置)的左孩子和右孩子上的数的大小,然后左孩子,右孩子中大的数再和i位置上的数比较,如果i位置上的还是最大的,那么就不用换了,直接结束就行,如果i位置上的数小于左孩子右孩子位置中大的那个数,交换。然后继续执行上述操作。其实相当于把数变小位置上的数往下沉。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n;
int a[10000];
int b;//表示哪个位置上的值需要改变
int number;//表示b位置上的数改成number
void swap(int i,int j)
{
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
void heapify(int i,int heapsize)//i表示哪个位置上的数发生了改变。R表示该大根堆的长度
{
int index=i*2+1;//index表示改变的左孩子
while (index<heapsize)//所有操作必须在左孩子小于堆的大小的条件下进行操作
{
int xmax=a[index]<a[index+1]&&((index+1)<heapsize)?index+1:index;//找出左孩子和右孩子中哪个大(注意,这个地方必须是这样,不能是int xmax=a[index]>a[index+1]&&((index+1)<R)?index:index+1,因为如果这样的话,当右孩子不存在时,就不对了)
xmax=a[xmax]>a[i]?xmax:i;//将左孩子和右孩子中较大的那个和改变的那个比较,找出大的
if(xmax==i)//当左孩子,右孩子和改变的那个中最大是改变的那个,直接结束(1:说明改变后它还是最大的,不需要换。2:已经把改变的那个往下换完了,不需要再换了)
break;
swap(xmax,i);
i=xmax;//这时改变的那个已经换到下面了,更新坐标,继续执行上述过程
index=i*2+1;
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
cin>>b;
cin>>number;
a[b]=number;
heapify(b,n);
for(int i=0;i<n;i++)
cout<<a[i]<<" ";
}
第三种操作(大根堆)
思路:先将大根堆顶的数和大根堆的最后一个数进行交换,然后进行2操作即可(因为交换后堆顶的数变为小数了,相当于堆顶的数变小了,此时进行2操作来调整成为大根堆)
代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int a[1000];
void swap(int i,int j)
{
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
void heapify(int i,int heapsize)//i表示哪个位置上的数需要往下沉。R表示该大根堆的长度
{
int index=i*2+1;//index表示改变的左孩子
while (index<heapsize)//所有操作必须在左孩子小于堆的大小的条件下进行操作
{
int xmax=a[index]<a[index+1]&&((index+1)<heapsize)?index+1:index;//找出左孩子和右孩子中哪个大
xmax=a[xmax]>a[i]?xmax:i;//将左孩子和右孩子中较大的那个和改变的那个比较,找出大的
if(xmax==i)//当左孩子,右孩子和改变的那个中最大是改变的那个,直接结束(1:说明改变后它还是最大的,不需要换。2:已经把改变的那个换完了,不需要再换了)
break;
swap(xmax,i);
i=xmax;//这时改变的那个已经换到下面了,更新坐标,继续执行上述过程
index=i*2+1;
}
}
void heapiput(int size)
{
swap(0,size-1);//将堆顶的数和最后一个数交换,执行heapify让堆顶的数往下沉(注意,这时堆得长度减一)
heapify(0,size-1);
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
heapiput(n);
for(int i=0;i<n-1;i++)
cout<<a[i]<<" ";
}
最后,开始讲堆排序(大根堆)(由小到大排序)
数组大小为n 0~~n-1
思路:首先,把数组调整成为大根堆,然后把数组中第一个数(堆顶的数,也是数组中最大的数)和数组第n-1位置上的数(堆底的数)交换,堆的大小减一,然后进行2操作。然后再进行上述操作,只不过这次是把数组中第一个数(堆顶的数,也是数组中最大的数)和数组的第n-2位置上的数交换。直到堆的大小为0或1时,结束
代码:
#include<bits/stdc++.h>
using namespace std;
int n;//需要排序的数组的大小
int heapsize;//堆的大小
int a[1500000];
void swap(int i,int j)
{
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
void heapify(int i,int heapsize)//大根堆数变小后如何调成大根堆 i表示哪个位置上的数发生了改变。heapsize表示该大根堆的长度
{
int index=i*2+1;//index表示改变的左孩子
while (index<heapsize)
{
int xmax=a[index]<a[index+1]&&((index+1)<heapsize)?index+1:index;//找出左孩子和右孩子中哪个大(注意,这个地方必须是这样,不能是int xmax=a[index]>a[index+1]&&((index+1)<R)?index:index+1,因为如果这样的话,当右孩子不存在时,就不对了)
xmax=a[xmax]>a[i]?xmax:i;//将左孩子和右孩子中较大的那个和改变的那个比较,找出大的
if(xmax==i)//当左孩子,右孩子和改变的那个中最大是改变的那个,直接结束(1:说明改变后它还是最大的,不需要换。2:已经把改变的那个换完了,不需要再换了)
break;
swap(xmax,i);
i=xmax;//这时改变的那个已经换到下面了,更新坐标,继续执行上述过程
index=i*2+1;
}
}
void heapinsert(int i)//将数组中第i个数放入大根堆中
{
int index=i;
while (a[index]>a[(index-1)/2])//当前数大于他的父节点的数
{
swap(index,(index-1)/2);//交换这两个位置上的数
index=(index-1)/2;//把index设置为父节点的位置,继续执行上述过程
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
heapinsert(i);
}
heapsize=n-1;//此时大根堆长度为数组的长度
while (heapsize)//当大根堆的长度不为0时,说明还有数字没有被排序 继续执行 其实每次执行完一次while循环后,就找到了数组中最大的一个
{
swap(0,heapsize);
heapify(0,--heapsize);
}
for(int i=0;i<n;i++)
cout<<a[i]<<" ";
cout<<endl;
}