排序算法-09-排序1 排序 (25分)-第一部分
来源:互联网 发布:2016年度网络热词 编辑:程序博客网 时间:2024/05/21 17:44
- 题目
09-排序1 排序 (25分)
冒泡排序
- 大概的思想
一趟冒泡排序之后,最大的数在最后;第二趟冒泡之后,次大的数在倒数第二位。
其中一个优化措施是 用一个变量flag标识这一趟冒泡中是否有过交换,如果没有的话,说明数组已经有序了。 - 代码
#include <stdio.h>#define ElementType long#define maxn 100001ElementType A[maxn];void Swap(ElementType A[], int i, int j);void Bubble_sort(ElementType A[],int N);int main(int argc, const char * argv[]) { int N; scanf("%d",&N); for(int i=0; i<N; i++){ scanf("%ld",&A[i]); } Bubble_sort(A, N); printf("%ld",A[0]); for(int i=1; i<N; i++){ printf(" %ld",A[i]); } printf("\n"); return 0;}void Swap(ElementType A[], int i, int j){ ElementType tmp = A[i]; A[i] = A[j]; A[j] = tmp;}void Bubble_sort(ElementType A[],int N){ int flag,i,j; for(i=N-2; i>=0; i--){ flag = 0; for(j=0; j<=i; j++){ if(A[j] > A[j+1]){ Swap(A, j, j+1); flag = 1; //标识发生了交换 } } if(flag == 0) break;//全程无交换,说明前面已经全部有序了,不需要再比较 }}
- 运行结果
- 小结
最坏时间复杂度是O(N^2),如果数组基本有序,那么经过若干趟排序之后数组就可能是有序的,所以可能复杂度是O(N),所以通过了测试点8
评价:交换排序,简单排序,最坏时间复杂度是O(N^2),在数组基本有序的情况下时间复杂度可能是O(N)。空间复杂度O(1)。
插入排序
- 大概的思想
就像自己打牌的时候摸牌一样,一张张摸,然后插入到合适的位置。每次插入后手里的牌都是有序的。
可以有的一点小优化是不要用Swap函数,不要交换,而是依次挪动数据。 - 代码
void Insert_sort(ElementType A[],int N){ int i,j; ElementType tmp; for(i=1; i<N; i++){ tmp = A[i]; //摸下一张牌 for(j=i-1; j>=0 && tmp<A[j]; j--){ A[j+1] = A[j]; //移出空位 } A[j+1] = tmp; //新牌落位 }}
- 运行结果
这儿虽然全部通过了,但可以看到测试点5、7、9用时很长。由于题目要求是10秒内,才会通过。 - 小结
插入排序最坏时间复杂度也是O(N^2),在数组基本有序的情况下,插入牌的时候基本不需要怎么挪动,所以时间复杂度可能降为O(N),空间复杂度O(1)
冒泡排序和插入排序都是每次交换消除一个逆序对,即每次交换只能交换相邻的数据,所以时间复杂度才为O(N^2),希尔排序就是受此启发,从交换距离较远的两个数据,尽快使得数组基本有序
希尔排序
- 大概的思想
类似与插入排序,但是每次比较的不是相邻的数,而是距离为某个值的两个元素。要定义一个间隔序列: 从大到小,最后是1
如Sedgewick序列:
int Sedgewick[] = {929, 505, 209, 109, 41, 19, 5, 1, 0};
这个序列中的元素一般要是互质的,这样希尔排序性能才会并较好。
我在这儿用一个 3*i+1 这样的序列 - 代码:
void Shell_sort(ElementType A[],int N){ int dis = 1; ElementType tmp; int i,j; while (dis < N/3) { dis = dis*3+1; } while (dis>=1) { //将数组变为dis间隔有序 for(i=dis; i<N; i++){ tmp = A[i]; for(j=i; (j>=dis)&&(tmp<A[j-dis]); j-=dis){ A[j] = A[j-dis]; } A[j] = tmp; } dis /= 3; }}
- 运行结果
这儿可看到测试点5、7、9的运行时间比插入排序时间短很多。说明希尔排序是对插入排序的一个很大的改进。 - 小结
一般来说,这要这个序列选取的比较好,希尔排序的时间复杂度一定小于O(N^2)。一般在O(N)-O(N^2)之间。
选择排序
- 大概的思想
每次选出当前未排序部分中的最小值,然后和未排序部分的第一个元素交换。
如果就是简单的扫描后面的整个数组,时间复杂度为一定O(N^2)。 - 代码
void Selection_sort(ElementType A[],int N){ int i,j,minPos; for(i=0; i<N; i++){ minPos = i; for(j=i; j<N; j++){ if(A[j]<A[minPos]) minPos = j; } if(minPos != i) Swap(A, i, minPos); }}
- 运行结果
- 小结
因为不管数据时候原来就是有序,时间复杂度始终为O(N^2),所以后面一定会超时。 - 注意
选择排序在 求N个数中前K大数这类问题是一种不错的选择。(特别是N>>K 时)
(1)如果像前面这样简单选择,时间复杂度是O(K*N)。
(2)一种优化是可以建立一个大小为K的最小堆。首先将前K个数调整为一个最小堆,然后对后面的数,依次与堆顶的数比较,如果大于堆顶的数,则将堆顶替换为该数,调整堆。这样时间复杂度可以降为O(N*logK)。
(3)另外一种方法是直接利用堆排序。将数组中的N个数直接调整为最大堆(时间:O(N)),然后K次删除堆顶元素并调整(时间: K*logN)。所以总的时间复杂度为O(N+K*logN)。
堆排序
- 大概的思想
首先将数组中N个数调整为最大堆(O(N)时间);然后依次把堆顶元素和对应位置的元素交换,然后调整堆(O(N*logN)时间)。总时间复杂度就是O(N*logN)
这儿需要注意的一个地方是 数组从下标0就存放了元素,那么下标为i的结点,它左孩子的下标是 2*i+1,右孩子的结单是 2*i+2。还有建立堆的时候不是从N/2开始调整,而是从 N/2-1 开始调整。 - 代码
/* 将N个元素的数组中以A[p]为根的子堆调整为最大堆, 因为A中的元素从下标0开始,所以左孩子的下标是 2*i+1 */void PercDown( ElementType A[], int p, int N ){ int parent,child; ElementType tmp = A[p]; for(parent=p; (parent*2+1)<N; parent=child){ child = parent*2 + 1; if(child+1<N && A[child+1]>A[child]) child++; if(tmp < A[child]) A[parent] = A[child]; else break; } A[parent] = tmp;}void Heap_sort(ElementType A[],int N){ for(int i=N/2-1; i>=0; i--){ PercDown(A, i, N); //BuildMaxHeap } for(int i=N-1; i>0; i--){ Swap(A, 0, i); //deleteMax PercDown(A, 0, i); }}
- 运行结果
- 小结
堆排序时间复杂度为O(N*logN)。
堆排序适合解决的问题有:求N个数中前K大数的这类问题。
归并排序
基本思想
分而治之的思想。要将一个大数组排序,可以把它分成两部分,先将这两个小一点的数组排好序,然后再把它们归并成一个大的有序数组。
所以就可以递归的将数组分为两个部分,直到数组大小为1,然后再一步步归并上来。代码
void Merge(ElementType A[], ElementType tmpA[], int L, int R, int rightEnd){ //printf("L is %d, rightEnd is %d\n",L,rightEnd); if(L>=R) return; int leftEnd = R - 1; int tmp = L; //保存结果数组的初始位置 int count = rightEnd-L+1; while (L<=leftEnd && R<=rightEnd) { if(A[L] <= A[R]) tmpA[tmp++] = A[L++]; else tmpA[tmp++] = A[R++]; } while (L<=leftEnd) { tmpA[tmp++] = A[L++]; } while (R<=rightEnd) { tmpA[tmp++] = A[R++]; } for(int i=0; i<count; i++){ A[rightEnd-i] = tmpA[rightEnd-i]; }}void Msort(ElementType A[], ElementType tmpA[], int L, int rightEnd){ int center; if (L < rightEnd) { center = (L+rightEnd)/2; Msort(A, tmpA, L, center); Msort(A, tmpA, center+1, rightEnd); Merge(A, tmpA, L, center+1, rightEnd); }}void Merge_sort(ElementType A[],int N){ ElementType *tmpA; tmpA = malloc(N * sizeof(ElementType)); if(tmpA != NULL){ Msort(A, tmpA, 0, N-1); free(tmpA); }else{ printf("空间不足\n"); }}
- 运行结果
-
运行结果说明归并排序也很快。 - 小结
归并排序,利用分而治之的思想。
时间大部分都花在归并的时候。归并时,需要O(N)的时间。
T(N) = 2*T(N/2) + O(N)
利用主方法,可以求得时间复杂度为 O(N*logN)。
同时空间复杂度为O(N),用来保存临时数组。 - 归并排序的优化
(1)当分解到规模比较小的数组时,可以利用简单排序进行排序(比如插入、冒泡排序等)。
(2)测试数组是否有序:可以加一个判断条件,如果a[center]小于a[center+1],我们就认为数组已经有序并跳过merge方法
(3)不要每次都将元素复制到辅助数组,一次排序将输入数组排序到辅助数组,一次将辅助数组排序到输入数组
1 0
- 排序算法-09-排序1 排序 (25分)-第一部分
- 排序算法-09-排序1 排序 (25分)-第二部分
- PTA 09-排序1 排序 (25分)
- 09-排序1 排序 (25分)
- 09-排序1 排序 (25分)
- 09-排序1 排序 (25分)
- 09-排序1 排序 (25分)
- 【算法】排序算法第一讲:插入排序
- 部分排序算法的实现
- 部分排序算法(php版)
- 部分排序算法python实现
- 部分排序
- 部分排序
- 采用部分快速排序算法实现数组的部分排序
- 排序算法(1)---插入排序
- [排序算法1] - 冒泡排序
- 【排序算法1】插入排序
- 排序算法(1)-冒泡排序
- QTextEdit文本编辑框的光标移动
- gson解析json
- 移动app如何进行自动化和探索性测试
- PSVR开发小结
- GCD使用三部曲之:基本用法
- 排序算法-09-排序1 排序 (25分)-第一部分
- Edraw Office Viewer component教程(三):将MS Word、Excel、PowerPoint嵌入到WPF应用程序中
- linux syslog
- 基于神经网络的智能RC小车(Python+OpenCV)
- C#Label、CheckBox等背景色设为透明
- text-dependent speech emotion recognition 相关文献
- SQL Server 数据库IN子句和EXISTS子句转换
- HBase学习总结:HBase的工作原理
- eclipse项目导入IntelliJ IDEA :Error:(1, 1) java: 非法字符: '\ufeff'