基于交换的排序方法主要有①冒泡排序和②快速排序。文章默认排序算法是升序
一、冒泡排序
基本思想:两两比较和交换,每次冒出一个关键字最大的记录。一般有两层循环,一般用两层for循环,外层循环只是指定这一趟已经有i个已冒出的数字(从而指定内层遍历的终点),内层循环j是遍历元素、比较、交换,直到n-i,循环结束条件是有一趟内层循环未发生交换,则证明有序。
eg:
原始序列:12, 34, 20, 15, 9, 25, 27, 13
第一趟: 12 34 20 15 9 25 27 13
12 20 34 15 9 25 27 13
12 20 15 34 9 25 27 13
12 20 15 9 34 25 27 13
……
12 20 15 9 25 27 13 34
第二趟: 12 15 9 20 25 13 27 24
第三趟: 12 9 15 20 13 25 27 34
第四趟: 9 12 15 13 20 25 27 34
第五趟: 9 12 13 15 20 25 27 34
第六趟: 9 12 13 15 20 25 27 34
代码:
//冒泡
void BubbleSort(double R[], int length)
{
for (int i = 1; i < length; ++i)
{
bool swap = false;
for (int j = 0; j < length - i; ++j)
{
if (R[j] > R[j + 1]) //比较、交换
{
int temp = R[j];
R[j] = R[j + 1];
R[j + 1] = temp;
swap = true;
}
}
if (!swap) break; //没发生交换说明有序,结束排序
}
}
冒泡过程中如果后面的记录未发生交换,则证明后面的序列已经有序,常规冒泡还是会进行比较到结束位置。因此每次记录最后的交换位置,作为下次的比较终点。
eg:23 15 14 25 28 30
15 14 23 25 28 30 //红色为最后交换时 j 的值
代码:
//改进的冒泡
//记录每一躺最后交换的j作为下一次冒泡的终点
void BubbleSortEx(double R[], int length)
{
int i = length - 1;
while (i > 1)
{
int lastIndex = 0;
for (int j = 0; j < i; ++j)
{
if (R[j] > R[j + 1])
{
int temp = R[j];
R[j] = R[j + 1];
R[j + 1] = temp;
lastIndex = j;
}
}
i = lastIndex;
}
}
实际提升效果有限,只有对特殊情况才有很好的效果。
时间复杂度,最好情况,正序,比较n-1次,交换0次。最坏情况,逆序o(n^2),平均o(n^2)。是一种稳定排序
三、快速排序
基本思想是以某个记录的关键字作为基准(划分元),将整个序列划分成两组,左边的记录关键字小于等于划分元,右边的大于等于划分元,而划分元所处的位置就是它最终排序好的位置。一般选取当前记录的第一个记录作为划分元,然后利用递归思想快排左右的子记录,这种思想和二叉树遍历很相似,递归遍历他们的子树。
代码:
//快排一次,返回划分元最终位置
int QuickPartition(double R[], int low, int high)
{
//以左端为划分元
double value = R[low];
while (low < high)
{
int temp = 0;
while (low < high && R[high] >= value) --high; //high左移
R[low] = R[high]; //比划分元小的值复制到左边,这里直接赋值不必担心覆盖,因为“被覆盖的值”已经保存在value
while (low < high && R[low] <= value) ++low; //low右移
R[high] = R[low]; //比划分元大的值复制到右边
}
R[low] = value; //i为划分元最终位置
return low;
}
//对顺序表R[l...h]快排
void QuickSort(double R[], int low, int high)
{
if (low < high)
{
int p = QuickPartition(R, low, high);
QuickSort(R, low, p - 1);
QuickSort(R, p + 1, high);
} //像极了二叉树先序遍历
}
性能方面,从空间复杂度来看,递归调用的层数与n个结点二叉树的深度相同,因此最好情况o(logn),最差o(n)。
时间复杂度一次划分需要o(n),最好o(nlogn),最坏情况有序或基本有序,退化为冒泡,o(n^2)。快排不是稳定排序