一:改良的直接插入排序(希尔排序)
优化考虑:因为直接插入排序对于大致有顺序的数列效率较高,希尔排序即将数列先进行一定排序
希尔:
for (step = count/2; step; step /= 2) {
for (start = 0; start < step; start++) {
shellOneSort(data, count, start, step);
}
}
一次:
for (i = start + step; i < count; i += step) {
tmp = data[i];
for (j = start; j < i && tmp >= data[j]; j += step) {
}
for (t = i; t > j; t -= step) {
data[t] = data[t - step];
}
data[j] = tmp;
}
原理:希尔排序对于初始n个元素的数列,第一轮以n/2为步长(即0, n/2两个数据),将两个数据进行直接插入排序;
第二轮以n / 2 ^ 2为步长,进行直接插入排序
即将一长串数列按固定间距划分成几个数据块,在数据块中进行直接插入排序。
二:改良的选择排序(堆排序)
基于二叉树;
升序(大根堆):每一个非叶子节点值都大于其子节点
二叉树一些计算:
①最大非叶子节点编号:n / 2 - 1
②编号为t的节点,其左孩子编号:t * 2 + 1
堆排序首先将数列表示成一个完全二叉树(即最多有一个只有左孩子的节点,切最后一个节点删除,不影响前面序号的连续性)
将这个二叉树都调整为大根堆(即将非叶子节点和左孩子右孩子值作比较,最大的放到非叶子节点上)
调整后根节点就是值最大的节点,最后一个节点是值最小的节点,将根节点和最后一个节点值交换,最后一个节点上的值即为最大值,
再进行大根堆调整,但此时二叉树的数据个数要减一(因为最后一个节点已经排序好了)
void adjustHeap(int *data, int count, int root) {
int left;
int right;
int maxNode;
int tmp;
while (root <= count / 2 - 1) {
left = 2*root + 1;
right = left + 1;
maxNode = right >= count ? left
: (data[left] > data[right]
? left : right);
maxNode = data[root] > data[maxNode]
? root : maxNode;
if (maxNode == root) {
return;
}
tmp = data[root];
data[root] = data[maxNode];
data[maxNode] = tmp;
root = maxNode;
}
}
void heapSort(int *data, int count) {
int root;
int tmp;
for (root = count / 2 - 1; root > 0; root--) {
adjustHeap(data, count, root);
}
while (count) {
adjustHeap(data, count, 0);
tmp = data[0];
data[0] = data[count - 1];
data[count - 1] = tmp;
--count;
}
}
首先拿到二叉树,取出最后一个非叶子节点,计算它的左右孩子下标,将该左右子树调整为大根堆,
最后一个左右子树调整好后,再取上一个左右子树进行调整,直到取到根节点并调整完,即整个树都调整完毕
调整完后就将①取根节点数,②和最后一个节点交换,③总节点数-1,④在进行一次大根堆调整
这样直到节点数归0,即数列排序完毕
三:改良的交换排序(快速排序)
取出数列第一个元素,既然第一个元素已经取出保护,则第一个元素可以视为空,
分别从第一个元素和最后一个元素开始,用非空的那个元素进行比较,当非空元素小于取出得第一个元素,则将非空元素放到head上,反之则放到tail上
int tmp = data[head];
int start = head;
int end = tail;
if (head >= tail) {
return;
}
while (head < tail) {
while (head < tail && data[tail] >= tmp) {
--tail;
}
if (head < tail) {
data[head] = data[tail];
++head;
}
while (head < tail && data[head] <= tmp) {
++head;
}
if (head < tail) {
data[tail] = data[head];
--tail;
}
}
data[head] = tmp;
quickOnce(data, count, start, head - 1);
quickOnce(data, count, head + 1, end);
开始head为0;
1.用tail与tmp比较:
1.1 若tail >= tmp, tail–,重复第一步
1.2 若tail < tmp, head与tail值互换
1.3 tail–
2.用head与tmp比较:
2.1 若head <= tmp, head++,重复第二步
2.2 若head > tmp, head与tail值互换
2.3 head++
第一步和第二步重复执行,直到head >= tail
这是一次快速排序,但这一次结束后,中间元素是最开始的第一个元素,该元素左侧和右侧分别为比它小和大的元素,
左侧右侧又该重复进行排序
可以发现这种排序一直在进行重复的工作,只是head和tail还有tmp在变化(即起始、终点位置变化,基准元素变化)
所以可以用函数递归调用实现
不用递归也可以用堆栈代替,因为递归是基于新的位置进行重复操作,那么用堆栈去记录新的起始、终点位置,在每次处理前进行
出栈,处理后进行新位置入栈即可
例:一开始入栈0和count-1
接下来入栈(start和head -1) 和 (head +1和end)
若堆栈非空则重复上述操作,就可以替代递归