冒泡排序(Bubble Sort)一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,知道没有反序的记录为止。冒泡排序是一种比较简单的排序方法,实现代码如下:
// 交换两个下标所指的值
template<typename T>
void swap(T *ptr, int i, int j)
{
T temp = ptr[i];
ptr[i] = ptr[j];
ptr[j] = temp;
}
// 冒泡排序
template<typename T>
void bubbleSort(T * ptr, int length)
{
for (int i = 0; i < length; i++)
{
for (int j = length - 1; j > i; j--)
{
if (ptr[j] < ptr[j - 1])
{
swap(ptr, j, j - 1);
}
}
}
}
测试代码如下:
int main()
{
int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
bubbleSortEx(ptr, 15);
show(ptr, 15);
getchar();
return 0;
}
测试结果如下图:
排序过程如下:
上图显示了,第一次循环过程,第一次把最小的关键字移到了第一位。后面依次类推。只是下一次循环只循环到下标1。依次到2、3、… n-2。(n为元素个数)。
如果我们待排序的序列是{2, 1, 4, 5, 6},则除了第一个和第二个关键字需要交换外,别的都已经是正常的顺序了。也就是说后面的其实不用再进行比较了。代码如下:
// 冒泡排序改进
template<typename T>
void bubbleSortEx(T * ptr, int length)
{
bool flag = true;
// 如果有交换数据则说明未排完序,false表示上一次没有数据交换,即后面已经拍好序了,所以不用再排了
for (int i = 0; i < length && flag; i++)
{
flag = false;
for (int j = length - 1; j > i; j--)
{
if (ptr[j] < ptr[j - 1])
{
swap(ptr, j, j - 1);
flag = true;
}
}
}
}
简单选择排序法(Simple Delection Sort)就是通过n - i 次关键字间的比较,从n - i + 1个记录中选取出关键字最小的记录,并和第i (1 <= i <= n)个记录交换。实现代码如下:
// 简单选择排序
template<typename T>
void simpleSelectionSort(T * ptr, int length)
{
// 最小值下标
int min = 0;
for (int i = 0; i < length; i++)
{
min = i;
for (int j = i; j < length; j++)
{
if (ptr[min] > ptr[j])
{
min = j;
}
}
if (min != i && ptr[min] != ptr[i])
{
swap(ptr, min, i);
}
}
}
测试代码如下:
int main()
{
int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
simpleSelectionSort(ptr, 15);
getchar();
return 0;
}
测试结果如下图:
选择排序相当于依次选出后面中的最小值放到最前面。上图中第一次循环,选出最小值1,后面依次从下标1、2…n - 2(n为元素个数)开始向后查找最小值。
直接插入排序(Straight Insertion Sort)的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。直接插入排序中把每一个单独的关键字看作一个有序的序列。实现代码如下:
template<typename T>
void insertSort(T * ptr, int length)
{
int i, j;
T temp;
for (i = 1; i < length; i++)
{
if (ptr[i] < ptr[i - 1])
{
temp = ptr[i];
for (j = i - 1; ptr[j] > temp && j >= 0; j--)
{
ptr[j + 1] = ptr[j];
}
ptr[j + 1] = temp;
}
}
}
测试代码如下:
int main()
{
int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
insertSort(ptr, 15);
show(ptr, 15);
getchar();
return 0;
}
测试结果如下图:
上图是直接插入排序的一部分,后面依次类推。
堆是具有以下性质的完全二叉树;每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图,是 一个大顶堆。
大顶堆一般是按照层序遍历放在一个数组中如下:
99 | 90 | 80 | 50 | 40 | 5 | 20 | 10 | 30 |
---|
堆排序(Heap Sort)就是利用堆进行排序的方法。它的基本思想是,将待排序的序列构成一个大顶堆。此时,整个序列的最大值就是对顶的根结点。将它移走(其实就是将其与对数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n - 1个序列重新构成一个堆,这样就会得到n个元素中的次小值。如此反复执行,就能得到一个有序序列了。如下图:
上图是排序的一部分,后面依次类推。实现代码如下:
// 堆排序
template<typename T>
void heapAdjust(T * ptr, int nRoot, int nEnd)
{
int j;
T temp = ptr[nRoot];
for (j = 2 * nRoot; j < nEnd; j *= 2)// 完全二叉树性质,左孩子为2n,右孩子为2n + 1
{
if (j < nEnd && ptr[j] < ptr[j + 1])
j++;
if (temp >= ptr[j])
break;
ptr[nRoot] = ptr[j];
nRoot = j;
}
ptr[nRoot] = temp;
}
template<typename T>
void heapSort(T *ptr, int nLength)
{
if (nullptr == ptr)
return;
int i = 0;
for (i = nLength / 2 - 1; i >= 0; i--)// 从0开始计数也是符合的
{
heapAdjust(ptr, i, nLength - 1);
}
for (i = nLength - 1; i > 0; i--)
{
swap(ptr, 0, i);
heapAdjust(ptr, 0, i - 1);
}
}
测试代码如下:
int main()
{
int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
heapSort(ptr, 15);
show(ptr, 15);
getchar();
return 0;
}
测试结果如下图:
希尔排序算是直接插入排序的一种更加抽象的提升。希尔排序是多次组分插入排序。插入排序是希尔排序中间隔为1的一种情况。实现代码如下:
// 希尔排序,插入排序的改进,动态间隔来进行插入排序
template<typename T>
void shellSort(T *ptr, int length)
{
T temp;
int increment = length;
int i, j;
do {
increment = increment / 3 + 1;
i = increment;
for (i = increment; i < length; i++)
{
if (ptr[i] < ptr[i - increment])
{
temp = ptr[i];
for (j = i - increment; j >= 0 && temp < ptr[j]; j -= increment)
{
ptr[j + increment] = ptr[j];
}
ptr[j + increment] = temp;
}
}
} while (increment > 1);
}
排序过程如下:
测试代码如下:
int main()
{
int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
shellSort(ptr, 15);
show(ptr, 15);
getchar();
return 0;
}
测试结果如下图:
快速排序(Quick Sort)的基本思想是:通过一趟排序将待排序记录分割成独立的两部分,其中一部分的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行快速排序,以达到整个序列有序的目的。
快速排序算是冒泡排序的一个改进。选一个数,左边比它小,右边比他大。相当于同时两端冒泡,左边冒大值,右边冒小值。排序过程如下图:
实现代码如下:
// 快速排序, 冒泡排序的改进,选一个数,左边比它小,右边比他大。
// 相当于同时两端冒泡,左边冒大值,右边冒小值
template<typename T>
int partition(T* ptr, int low, int high)
{
T temp = ptr[low];
while (low < high)
{
while (low < high && ptr[high] >= temp)
{
high--;
}
swap(ptr, low, high);
while (low < high && ptr[low] <= temp)
{
low++;
}
swap(ptr, low, high);
}
return low;
}
template<typename T>
void qSort(T *ptr, int low, int high)
{
if (low < high)
{
int par = partition(ptr, low, high);
qSort(ptr, low, par - 1);
qSort(ptr, par + 1, high);
}
}
template<typename T>
void quickSort(T *ptr, int length)
{
qSort(ptr, 0, length - 1);
}
测试代码如下:
int main()
{
int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
quickSort(ptr, 15);
show(ptr, 15);
getchar();
return 0;
}
测试结果如下图:
归并排序(Merging Sort)真利用归并的思想事项的排序方法。它的原理是假设初始序列包含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到不小于n/2个长度为2或1的有序子序列;然后在两两归并,依次类推。知道得到一个长度为n的有序序列为止。
归并排序的思想很好理解,实现代码如下:
// 归并排序,把每个单独的元素当作一个有序序列,然后分别把多个有序序列归并的一起
template<typename T>
void merge(T *pl, T *pr, int left, int m, int right)
{
int i = left, j = m + 1, k = left;
while (i <= m && j <= right)
{
if (pl[i] > pl[j])
{
pr[k++] = pl[j++];
}
else
{
pr[k++] = pl[i++];
}
}
if (i <= m)
{
for (int n = i; n <= m; n++)
{
pr[k++] = pl[n];
}
}
if (j <= right)
{
for (int n = j; n <= right; n++)
{
pr[k++] = pl[n];
}
}
for (int n = left; n <= right; n++)
{
pl[n] = pr[n];
}
}
template<typename T>
void mSort(T * pl, T * pr, int left, int right)
{
int m;
if (left == right)
{
pr[left] = pl[left];
}
if (left < right)
{
m = (left + right) / 2;
mSort(pl, pr, left, m);
mSort(pl, pr, m + 1, right);
merge(pr, pl, left, m, right);
}
}
template<typename T>
void mergeSort(T *ptr, int length)
{
T * temp = new T[length];
mSort(ptr, temp, 0, length - 1);
delete[] temp;
}
测试代码如下:
int main()
{
int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
mergeSort(ptr, 15);
show(ptr, 15);
getchar();
return 0;
}
测试结果如下图:
归并排序除了用递归实现,也可以用迭代方法实现,迭代方法没有函数的多次调用,比递归方法效率更高。代码如下:
// 归并排序迭代版,效率比递归更高
template<typename T>
void mergeSeque(T *pl, T *pr, int step, int length)
{
int i = 0;
for (; i <= length - 2 * step - 1; i += 2 * step)
{
merge(pl, pr, i, i + step - 1, i + 2 * step - 1);
}
if (i < length - step - 1)
{
merge(pl, pr, i, i + step - 1, length - 1);
}
else
{
merge(pl, pr, i - step - 1, i - 1, length - 1);
}
}
template<typename T>
void mergeSortIteration(T *ptr, int length)
{
T * temp = new T[length];
for (int i = 1; i < length; i += i)
{
mergeSeque(ptr, temp, i, length);
}
delete[]temp;
}
测试代码如下:
int main()
{
int ptr[15] = { 4, 5, 7, 3, 8, 9, 1, 2, 6, 2 , 49, 23, 10, 30, 3 };
mergeSortIteration(ptr, 15);
show(ptr, 15);
getchar();
return 0;
}
测试结果如下图:
随机生成10000个整数,使用不同的排序程序来排序,输出运行时间。代码如下:
#include "sort.h"
#include
#include
#include
#include
// 排序算法比较
template<typename T>
void runSort(T * ptr, int length, void (*func)(T *ptr, int length), std::string funcName)
{
T * pTemp = new T[length];
for (int i = 0; i < length; i++)
{
pTemp[i] = ptr[i];
}
clock_t start, stop;
start = clock();
func(pTemp, length);
stop = clock();
std::cout << funcName << "时间:" << stop - start << std::endl << std::endl;
}
void sortCompare()
{
int length = 10000;
int * ptr = new int[length];
for (int i = 0; i < length; i++)
{
ptr[i] = std::rand();
}
runSort(ptr, length, bubbleSort, "冒泡排序");
runSort(ptr, length, bubbleSortEx, "冒泡排序改进");
runSort(ptr, length, simpleSelectionSort, "简单选择排序");
runSort(ptr, length, insertSort, "插入排序");
runSort(ptr, length, heapSort, "堆排序");
runSort(ptr, length, shellSort, "希尔排序");
runSort(ptr, length, quickSort, "快速排序");
runSort(ptr, length, mergeSort, "归并排序");
runSort(ptr, length, mergeSortIteration, "归并排序迭代版");
}
int main()
{
sortCompare();
getchar();
return 0;
}
测试结果如下图: