堆排序
利用大根堆来对数组从小到大排序。
思路:
利用数组来表达堆,把目标排序数组复制给堆数组,对堆数组先进行下沉法,让节点都遵循父节点总比子节点大的规律,再利用另一种重载下沉法,弹出数组中的最大值,并把最大值复制给目标排序数组。
堆类基本成员:
class HeapSort
{
public:
int* heap;
int N;
void exch(int i, int j)
{
int temp;
temp = heap[i];
heap[i] = heap[j];
heap[j] = temp;
}
bool less(int i, int j)
{
if (heap[i] < heap[j])
{
return true;
}
return false;
}
……
排序入口函数sort():
void sort(int* source,int len)
{
creatHeap(source,len);
for (int i = N-1; i>=0; i--)
{
source[i] = delMax(); //弹出最大值,赋给原数组
}
}
创建有序数组函数creatHeap():
void creatHeap(int* source,int len)
{
//拷贝新数组
copySource(source,len);
//对heap中的元素做调整,从长度的一半处sink,不从长度一般遍历也行,具体看接下来的sink2()
for (int i = N / 2; i > 0; i--)
{
sink2(i);
}
}
复制函数copySource():
void copySource(int* source,int len)
{
N = len;
cout << "len=" << len << endl;
this->heap = new int[len+1];
for (int i = 0; i < len+1; i++)
{
heap[i + 1] = source[i];
}
}
下沉函数sink2():
void sink2(int i)
{
int max = 0;
while (i * 2 <= N) //N为数组元素量,在复制时有赋值
{
if (i * 2 + 1 >= N)
{
if (less(i * 2, i * 2 + 1))
{
max = i * 2 + 1;
}
else
{
max = i * 2;
}
}
else
{
max = i * 2;
}
if (less(max, i))
{
return;
}
exch(max, i);
i = max;
}
}
从最末端的子节点下沉没有必要,它们没有子节点可以交换,于是从倒数第二层的父节点开始下沉,让其于它的子节点交换,最外层的交换不会干扰的整个数组的顺序,而里层父节点交换可能会干扰到,比如倒数第三层的父节点与倒数第二层的子节点交换,可能子节点的新元素比其子节点小,因此要追踪其交换的位置,让其再与子节点判断交换,直到末端。
可能你还有些顾虑,这里是二叉树的样子,仅仅正确交换一次就可以使当前子树有序,有元素更改,就去操作更改元素(它为根)所在的子树有序。
剩下就是不那么重要的,弹出最大值,并赋值给原数组:
delmax():
int delMax()
{
if (N == 0)
{
return -1;
}
int max = heap[1];
exch(1, N);
N--;
sink(1);
return max;
}
sink():
void sink(int k)
{
while (2 * k <= N)
{
int max;
if (2 * k + 1 <= N)
{
if (less(2 * k, 2 * k + 1))
{
max = 2 * k + 1;
}
else
{
max = 2 * k;
}
}
else
{
max = 2 * k;
}
if (!less(k, max))
{
return;
}
exch(k, max);
k = max;
}
}
让根节点处元素与末端元素交换,重新在根的位置下沉调整数组有序化。
验证结果:
int main()
{
int source[10] = {10,9,5,7,6,5,4,3,2,1};
cout << "初始遍历:" << endl;
for (int i = 0; i < 10; i++)
{
cout << source[i] << endl;
}
HeapSort heapsort;
heapsort.sort(source,10);
cout << "重新遍历:" << endl;
for (int i = 0; i < 10; i++)
{
cout << source[i] << endl;
}
}
结果:
初始遍历:
10
9
5
7
6
5
4
3
2
1
len=10
重新遍历:
1
2
3
4
5
5
6
7
9
10