堆排序的特点是:在排序过程中,将待排序数组看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中父结点和子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的记录。
基本思想 :将待排序数组调整为一个大根堆。大根堆的堆顶元素就是这个堆中最大的元素。重复操作,直到无序区消失为止。
初始时,整个数组为无序区,每一次交换,都是将大根堆的堆顶元素换入有序区,以保证有序区是有序的。
class Program
{
static void Main(string[] args)
{
int[] array = { 4, 6, 34, 75, 43, 7, 356, 2 };
heapSort (array);
for (int i = 0; i < array.Length; i++)
{
Console.Write(array[i] + " ");
}
Console.ReadKey();
}
//堆排序
public static void heapSort(int[] arr)
{
//构造大根堆
heapInsert(arr);
int size = arr.Length ;
while (size > 0)
{
//固定最大值
swap(arr, 0, size - 1);
size--;
//构造大根堆
heapify(arr, 0, size);
}
}
//构造大根堆(通过新插入的数上升)
public static void heapInsert(int[] arr)
{
for (int i = 0; i < arr.Length; i++)
{
//当前插入的索引
int currentIndex = i;
//父结点索引
int fatherIndex = (currentIndex - 1) / 2;
//如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点
//然后继续和上面的父结点值比较,直到不大于父结点,则退出循环
while (arr[currentIndex] > arr[fatherIndex])
{
//交换当前结点与父结点的值
swap(arr, currentIndex, fatherIndex);
//将当前索引指向父索引
currentIndex = fatherIndex;
//重新计算当前索引的父索引
fatherIndex = (currentIndex - 1) / 2;
}
}
}
//将剩余的数构造成大根堆(通过顶端的数下降)
public static void heapify(int[] arr, int index, int size)
{
int left = 2 * index + 1;
int right = 2 * index + 2;
while (left < size)
{
int largestIndex;
//判断孩子中较大的值的索引(要确保右孩子在size范围之内)
if (arr[left] < arr[right] && right < size)
{
largestIndex = right;
}
else
{
largestIndex = left;
}
//比较父结点的值与孩子中较大的值,并确定最大值的索引
if (arr[index] > arr[largestIndex])
{
largestIndex = index;
}
//如果父结点索引是最大值的索引,那已经是大根堆了,则退出循环
if (index == largestIndex)
{
break;
}
//父结点不是最大值,与孩子中较大的值交换
swap(arr, largestIndex, index);
//将索引指向孩子中较大的值的索引
index = largestIndex;
//重新计算交换之后的孩子的索引
left = 2 * index + 1;
right = 2 * index + 2;
}
}
//交换数组中两个元素的值
public static void swap(int[] arr, int i, int j)
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}