前言
这里保存个人对常见交换排序算法的理解和实现思路过程。 供大家学习和参考。
一、几点注意
对于耗时时间,随机数选取,单调性的问题详见排序算法(一)。
二、交换类排序
2.1 交换类排序的分类
常见的交换类排序分为冒泡排序,快速排序等等,这里着重实现这两种插入排序。
2.2 冒泡排序
2.2.1 冒泡排序的思路
反复扫描待排序的记录序列,在扫描的过程中顺次比较相邻的两个元素的大小,若逆序就交换位置。每趟冒泡排序都将一个记录排到位,知道剩下一个最小的记录。若在某一趟中未发现一个逆序,则结束整个排序过程。
2.2.2 具体代码
void Swap(int* a, int* b) {
int temp = *b;
*b = *a;
*a = temp;
}
void BubbleSort(int* arr, int n) {
int i, j;
for (i = 0; i <= n - 1; i++) {
for (j = 1; j <= n - 1; j++) {
if (arr[j - 1] > arr[j]) {
Swap(&arr[j - 1], &arr[j]);
}
}
}
}
2.2.3 测试代码
冒泡排序 对于10000个数据的处理结果(随机数选取10000以内的)
多次尝试后,冒泡排序的时间对于10000个数据处理在390ms到400ms之间
2.2.4 稳定性分析
冒泡排序因为是相邻的两两交换因此冒泡排序是稳定的。
2.3 快速排序
2.3.1 快速排序的思路
思想:从待排序记录序列中选取一个记录(通常选取第一个记录)为枢轴,其关键字设为k1,然后将其余关键小于k1的记录移到前面,而将关键字大于或等于k1的记录移到后面,将待排序记录序列分成两个子表,最后将关键字为的k1的记录插到其分界线的位置处。将这个过程称为一趟快速排序。之后再对分割后的子表继续按上述原则分割,直到所有子表表长不超过1为止,此时待排序记录就变成了有序表。
2.3.2 具体代码
void QuickSort(int* arr, int low, int high) {
int first = low;
int end = high;
if (low < high) {
int index = arr[low];
while (low < high) {
while (low < high && arr[high] >= index) {
high--;
}
arr[low] = arr[high];
while (low < high && arr[low] <= index) {
low++;
}
arr[high] = arr[low];
}
arr[low] = index;
QuickSort(arr, first, low - 1);
QuickSort(arr, low + 1, end);
}
}
2.3.3 测试结果
对于10000个数据的处理结果(随机数选取10000以内的) 对于10000个数据的处理快速排序只用了1ms,比起前两个快的多。
2.3.4 稳定性
快速排序因为是左右同时交换,所以存在将相同元素换到相反的方向的可能,所以是不稳定的。
2.4 两种交换排序的对比
冒泡排序
时间复杂度:平均情况下为O(N2)。 ??????最好情况下为O(N),顺序。 ??????最坏情况下为O(N2)。 空间复杂度:因为每回只移动一个所以空间复杂度为O(1)。 稳定性:稳定。
快速排序
时间复杂度:平均情况下为O(Nlog2N)。 ??????最好情况下为O(Nlog2N),每趟排序分为两半,类似于折半查找。 ??????最坏情况下O(N2),已经排好序。 空间复杂度:O(Nlog2N)。 从空间性能上看,尽管快速排序只需要一个元素的辅助空间,但快速排序需要一个栈空间来实现递归。最好的情况下,即快速排序的每一趟排序都将元素序列均匀地分割成长度相近的两个子表,所需栈的最大深度为log2(n+1);但最坏的情况下,栈的最大深度为n。这样,快速排序的空间复杂度为O(log2n) 稳定性:不稳定。
三、对于已有排序算法的使用
3.1 qsort()的使用
对于C语言的头文件#include< algorithm >中提供一种快速排序的函数使用。
3.1.1 qsort()函数简介
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
参数 base– 指向要排序的数组的第一个元素的指针。 nitems– 由 base 指向的数组中元素的个数。 size– 数组中每个元素的大小,以字节为单位。 compar– 用来比较两个元素的函数,即函数指针(回调函数)
参数中compar需要我们自己写回调函数,这里就要复习一下函数指针的问题了。
3.1.2 函数指针的使用
函数指针的声明方法为:
返回值类型 ( * 指针变量名) ([形参列表]);
我们要记住无论是[],还是(),运算优先级都要比*高。这样我们就能理解什么是函数指针了。 定义函数指针有三种方式: 先设置一个fun函数
int fun (int a){
printf("a = %d\n",a);
return 0;
}
第一种:先定义函数类型,根据类型定义指针变量。
typedef int FUN (int);
FUN *p1=NULL;
p1=fun;
fun(5);
p1(5);
第二种:先定义函数指针类型,根治类型定义指针变量
typedef int(*PFUN)(int);
PFUN p2=fun;
p2(5);
第二种这种定义不太明显不建议使用。 第三种:直接定义函数指针类型(常用)
int(*p3)(int)fun;
p3(5);
或者
int (*p4)(int a);
p4=fun;
p4(5);
3.1.3 qsort()的具体使用
有了刚刚函数指针的学习,对于回调函数compar我们也就有个了解 因此先对int (*compar)(const void , const void)写回调函数
int compare(const void* left, const void* right) {
return *(int*)left - *(int*)right;
}
接着调用qsort()。
qsort(arr, n, sizeof(int), compare);
3.1.4 qsort()测试
3.2 sort()的使用
对于C++中,头文件algorithm中存在sort()函数 记得加using namespace std;
3.2.1 sort()函数简介
template< class RandomIt >
void sort( RandomIt first, RandomIt last );
template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );
参数 (1)first表示要排序数组的起始地址; (2)last表示数组结束地址的下一位; (3)comp用于规定排序的方法,可不填,默认升序。 sort()可以对链表、结构体等等进行排序,不过要自己写回调函数,这里就只测试对于数组的排序。
3.2.2 sort()具体调用
对于数组arr的调用
sort(a, a + n);
3.2.3 测试结果
四、对于几个性能比较高的算法的比较
对于100000个数据,随机数选取在100000以内。 这里只比较了我自己写的快排和希尔,以及库函数里的qsort和sort。
对于1000000个数据,随机数选取在1000000以内 说实话我也不知道为啥自己写的快排效率要比库函数的qsort()要好。
总结
对于交换类排序的总结就到这里,有问题欢迎大家指正。
|