排序总结
来源:互联网 发布:c语言大小写字母互换 编辑:程序博客网 时间:2024/06/10 05:54
1. 直接插入排序
//直接插入排序void insert_sort(int a[], int n){ int i, j, temp; for (i = 1; i < n; i++) { //暂存下标为i的数 temp = a[i]; //从后往前找要插入的位置 for (j = i - 1; j >= 0 && a[j]>temp; j--) { a[j + 1] = a[j]; } a[j + 1] = temp; }}
时间复杂度,在最好的情况下是:
空间复杂度:
它是稳定排序(相等元素的相对次序在排序后不会发生改变)
2. 希尔排序
eg:
void shell_sort(int a[],int n){ int h, i, j, temp;//h是增量 for (h = n / 2; h > 0; h = h / 2) { //下面是一个插入排序,0~h-1时在各自组的第一位,认为是最好的排序 for (i = h; i < n; i++) { temp = a[i]; for (j = i - h; j >= 0 && a[j] > temp; j -= h) a[j + h] = a[j]; a[j + h] = temp; } }}
希尔排序的实质是分组插入排序,由于相等的元素可能分在不同的组,导致他们的相对次序发生改变,所以希尔排序不稳定。
希尔排序优于直接插入排序,因为:
(1)当增量h较大时,一组内的元素个数
(2)当增量h减小,每组元素的个数不断增多,但经过以前的处理,数组较接近有序状态,当数组初始状态基本有序时,直接插入排序所需的比较和移动次数都较小。
一般情况下,时间复杂度优于
空间复杂度:
3. 冒泡排序
void bubble_sort(int a[], int n){ int i, j, temp, exchange = 0; for (i = 0; i < n; i++) { exchange = 0; for (j = n-1;j>i; j--)//从后往前交换,使得最轻的气泡在最前面 { if (a[j] < a[j - 1]) { temp = a[j]; a[j] = a[j - 1]; a[j - 1] = temp; exchange = 1; } } //如果此趟扫描没有交换,说明已经是有序的了 if (exchange != 1) return; }}
冒泡排序是稳定排序。
时间复杂度,在最好的情况下是:
空间复杂度:
4. 快速排序
思想:分治法(Divide-and-Conquer),划分交换排序
首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面。
eg:
void quick_sort(int a[], int low, int high){ int i, j, pivot; if (low < high) { pivot = a[low]; i = low; j = high; while (i < j) { //从后往前搜索(j--),找到第一个小于pivot的a[j],交换a[i]与a[j]; while (i < j&&a[j] >= pivot) j--; if (i < j) a[i++] = a[j];//将比pivot小的元素移到低端 //从前往后找(i++),找到第一个大于pivot的a[i],交换a[i]与a[j] while (i < j && a[i] <= pivot) i++; if (i < j) a[j--] = a[i];//将比pivot大的元素移到高端 } a[i] = pivot; quick_sort(a, low, i - 1); quick_sort(a, i + 1, high); }}
快速排序在最坏情况下的时间复杂度:
空间复杂度:
快速排序是不稳定的,因为相等元素在分组交换时不能保证相对位置不变。
5. 直接选择排序
思想:第一次从R[0]~R[n-1]中选取最小值,与R[0]交换,第二次从R[1]~R[n-1]中选取最小值,与R[1]交换,….,第i次从R[i-1]~R[n-1]中选取最小值,与R[i-1]交换,…..,第n-1次从R[n-2]~R[n-1]中选取最小值,与R[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列·
void select_sort(int a[], int n){ int i, j, x, l; for (i = 0; i < n - 1; i++) { x = a[i]; l = i; for (j = i; j < n; j++) { if (a[j] < x) { x = a[j]; l = j; } } a[l] = a[i];//将最小元素与a[i]交换 //这种不相邻元素的互换,所以不是稳定的排序 a[i] = x; } }
直接选择排序不是一种稳定的排序。
平均时间复杂度:
6. 堆排序
思想:
(1)先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区;
(2)再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key;
(3)由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
eg:
//以下开始是堆排序int heap_size = 0;int Left(int idx) { return ((idx<<1)+1); }int Right(int idx) { return ((idx<<1)+2); }//a[idx]与其左右子树进行递归对比,用最大值替换a[idx]void maxHeapify(int a[], int idx){ int largest = 0; int left = Left(idx), right = Right(idx); if ((left <= heap_size) && (a[left] > a[idx])) largest = left; else largest = idx; if ((right <= heap_size) && (a[right] > a[largest])) largest = right; //此时largest为堆顶、左子节点、右子节点中的最大者 if (largest != idx) { swap(a[largest], a[idx]); maxHeapify(a, largest); }}//初始化堆,将数组中的每一个元素放到适当的位置//完成之后,堆顶的元素为数组的最大值void buildMaxHeap(int a[], int n){ int i=0; heap_size = n ; //堆大小为数组长度 for (i = (n >> 1); i >= 0; i--) maxHeapify(a, i);}void heap_sort(int a[], int n){ int i=0; //初始化堆 buildMaxHeap( a, (n-1));//O(n) for (i = (n - 1); i > 0; i--) { //堆顶元素a[0](数组的最大值)被置换到数组的尾部a[i] swap(a[0], a[i]); heap_size--;//从堆中移除该元素 maxHeapify(a, 0);//重建堆,O(lgn) }}
堆排序不稳定。
时间复杂度:建最大堆过程buildMaxHeap是
7. 归并排序
两路归并思想:将两个已排好序的子文件合并。
自顶向下的算法设计:分解->求解->合并。
//下面是归并排序void merge(int a[], int temp[], int lpos, int rpos, int rend){ int i, lend, numEles, tmpPos; lend = rpos - 1; tmpPos = lpos; numEles = rend - lpos + 1; while (lpos <= lend && rpos <= rend) { if (a[lpos] < a[rpos]) temp[tmpPos++] = a[lpos++]; else temp[tmpPos++] = a[rpos++]; } while (lpos <= lend) temp[tmpPos++] = a[lpos++]; while (rpos <= rend) temp[tmpPos++] = a[rpos++]; //把临时数组拷贝到原始数组 for (i = 0; i < numEles; i++, rend--) a[rend] = temp[rend];}void msort(int a[], int tmp[], int low, int high){ if (low >= high) return; int mid = (low + high) / 2; msort(a, tmp, low, mid); msort(a, tmp, mid + 1, high); merge(a, tmp, low, mid + 1, high);}void merge_sort(int a[], int n){ int *tmp = NULL; tmp = new int[n]; if (tmp != NULL) { msort(a, tmp, 0, n - 1); delete[]tmp; }}
**最好、最坏和平均时间复杂度:
归并排序是稳定的**。
8. 基数排序
桶排序:设置若干箱子,依次扫描,按关键字等于k装入第k个箱子,然后依次按序号将各非空的箱子首尾连接起来。
有最低位优先(LSD)和最高位优先(MSD )。LSD:从低位到高位依次对数据进行箱排序。在第i趟排序中,所需的箱子数就是基数。
以下是对正整数的排序:
//下面是基数排序int find_max(int a[], int n){ int max = a[0]; for (int i = 1; i < n; i++) if (max < a[i]) max = a[i]; return max;}int digit_num(int num){ int digit = 0; do { num /= 10; digit++; } while (num != 0); return digit;}int kth_digit(int num, int k)//第k位的数字{ num /= pow(10, k); return num % 10;}void radix_sort(int a[], int n){ int *temp[10];//指针数组,每一个指针表示一个箱子 int count[10] = { 0,0,0,0,0,0,0,0,0,0 }; int max = find_max(a, n); int maxDigit = digit_num(max); int i, j, k; for (i = 0; i < 10; i++) { temp[i] = new int[n]; memset(temp[i], 0, sizeof(int) * n); } for (i = 0; i < maxDigit; i++) { memset(count, 0, sizeof(int) * 10); for (j = 0; j < n; j++) { int xx = kth_digit(a[j], i); temp[xx][count[xx]] = a[j]; count[xx]++; } int index = 0; //把数据从暂存数组中返回到原始数组 for (j = 0; j < 10; j++) for (k = 0; k < count[j]; k++) a[index++] = temp[j][k]; }}
基数排序是稳定的。
时间复杂度:假设有n个元素,每个元素有m个关键字(即这里的最大位数maxDigit),此时为
9. 小结
参考:
希尔排序_百度百科
冒泡排序_百度百科
快速排序_百度百科
直接选择排序_百度百科
堆排序_百度百科
归并排序_百度百科
基数排序_百度百科
- 排序总结
- 排序总结
- 排序总结
- 排序总结
- 排序总结
- 排序总结
- 排序总结
- 排序总结
- 排序总结
- 排序总结
- 排序总结
- 排序总结
- 排序 -- 总结
- 排序总结
- 排序总结
- 排序总结
- 排序总结
- 排序总结
- Android程序Crash时的异常上报
- 浅谈未来的人工智能与奇点临近
- Fragment使用总结
- Node.js系列——(2)发起get/post请求
- jQuery评分插件实现星型评分
- 排序总结
- poj3349 Snowflake Snow Snowflakes(哈希表,散列表)
- 图片过大导致OOM
- 正则
- cmake的使用helloworld
- message pack
- 使用JNI在JAVA和C++之间进行交互操作
- React Native提高在模拟器上的运行速度
- [LeetCode]551. Student Attendance Record I