堆排过程:
先将待排序的数视为完全二叉树(按层次遍历顺序进行编号,从0开始)
完全二叉树的最后一个非叶子节点,也就是最后一个节点的父结点。最后一个节点的索引为数组长度len-1,那么最后一个非叶子节点的索引应该是为
(len-1)/2.也就是从索引为2的节点开始,如果其子节点的值大于其本身的值,则把他和较大子节点进行交换,即将索引2处节点和索引5处元素交换。交换后的结果如图:
建堆从最后一个非叶子节点开始即可
向前处理前一个节点,也就是处理索引为0的节点,此时9<79,9<49,因此需要交换。应该拿索引为0的节点与索引为1的节点交换,因为79>49,如图
如果某个节点和它的某个子节点交换后,该子节点又有子节点,系统还需要再次对孩子节点进行判断。如图因为1处,3处,4处中,1处的值大于3,4处的值,所以还需要交换
注:将每次堆排序得到的最大元素与当前规模数组最后一个元素交换
非递归实现:
//初始排序为大根堆
void hell(int *arr,int len)
{
for(int start = len/2 -1;start>=0;start--) //每个大根堆的根
{
int tmp = arr[start];
int i=0;
for(i=2*start + 1;i<len;i=2*i+1)//其根与孩子的关系
{
if(i+1 < len && arr[i] < arr[i+1]) //判断是否有右孩子
i++;
if(arr[i] < tmp) //孩子都小于tmp
break;
else if(arr[i]>tmp) // 大的数值换到根上
arr[start] = arr[i];
else
;
start = i;//start移动到i处
}
arr[start] = tmp;//循环结束将tmp放到合适的根上
}
}
void hell_adjust(int *arr,int len)
{
hell(arr,len);
}
void hell_sort(int *arr,int len)
{
int tmp = 0;
hell(arr,len);
for(int i=len-1;i>0;--i)
{
tmp = arr[i];
arr[i] = arr[0];
arr[0] = tmp;
hell_adjust(arr,i-1);
}
}
递归实现:
void adjust(int *arr,int len,int index)
{
int left = 2*index + 1;
int right = 2*index + 2;
int maxidx = index;
if(left < len && arr[left] > arr[maxidx])
maxidx = left;
if(right < len && arr[right] > arr[maxidx])
maxidx = right;
if(maxidx != index)
{
swap(arr[maxidx],arr[index]);
adjust(arr,len,maxidx); //递归调整其它不满足堆性质的部分
}
}
void heapdort(int arr[],int size)
{
for(int i=size/2 - 1;i>=0;i--) // 对每一个非叶结点进行堆调整
adjust(arr,size,i);
for(int i=size-1;i>=1;i--)
{
swap(arr[0],arr[i]); //将当前最大的位置放到数组末尾
adjust(arr,i,0); //将未完成排序的部分继续进行堆排
}
}