排序--面经

来源:互联网 发布:无锡安镇网络 编辑:程序博客网 时间:2024/05/29 17:59

视频学习网址:点击打开链接

参考书籍《算法导论》

Pre比较:

理论时间:

                          堆排序      归并排序        快速排序
最坏时间        O(nlogn)     O(nlogn)        O(n^2)
最好时间        O(nlogn)     O(nlogn)        O(nlogn)
平均时间        O(nlogn)     O(nlogn)        O(nlogn)

空间复杂度     O(1)             O(n)              ???这里不对啊,我觉得是O(1)啊(ps:这里的空间复杂度还包括了递归的过程,由于快排是递归实现的,所以有log(n)到n的空间复杂度,堆排序如果用递归实现也是logn的空间复杂度,不过堆排序可以改成非递归实现)


实际测量:

4种非平方级的排序:
希尔排序,堆排序,归并排序,快速排序

我测试的平均排序时间:数据是随机整数,时间单位是秒
数据规模    快速排序    归并排序    希尔排序    堆排序
1000万       0.75           1.22          1.77          3.57
5000万       3.78           6.29          9.48         26.54  
1亿             7.65          13.06        18.79        61.31

堆排序是最差的。
这是算法硬伤,没办法的。因为每次取一个最大值和堆底部的数据(记为X)交换,重新筛选堆,把堆顶的X调整到位,有很大可能是依旧调整到堆的底部(堆的底部X显然是比较小的数,才会在底部),然后再次和堆顶最大值交换,再调整下来。
从上面看出,堆排序做了许多无用功。


排序算法稳定性定义:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。

堆排序、快速排序、希尔排序、直接选择排序   不是稳定的排序算法,(快排和直接选择排序都是在发生交换的时候破坏的,希尔排序由于步长大于1时开头就会跳过一些元素,堆排序在维护堆的时候会破坏稳定性)

而基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序、计数排序   是稳定的排序算法。

参考:百度百科

参考视频:点击打开链接

1 冒泡排序:两两比较,大的放后;关注for循环中的两个变量的变化范围;时间复杂度O(n^2)

#include <iostream>class BubbleSort{public:BubbleSort(){};//默认构造函数BubbleSort(BubbleSort &other){};//拷贝构造函数//冒泡排序函数int *bubbleSort(int *pa, int n);private:};//冒泡排序函数int *BubbleSort::bubbleSort(int *pa, int n){int temp = 0;//for循环,两两比较排序for (int j = 0; j < n - 1; j++){for (int k = 0; k < n - 1 - j; k++){if (pa[k]>pa[k + 1]){temp = pa[k];pa[k] = pa[k + 1];pa[k + 1] = temp;}}}//返回return pa;}

2 插入排序:逐个选取数组中元素,与前面元素比较插入;时间复杂度O(n^2)

class InsertionSort {public:int* insertionSort(int* A, int n) {// write code hereint key = 0;//遍历数组中每一个元素for (int i = 1; i < n; i++){key = A[i];int j = i - 1;//与前面的元素逐个比较,大于则插入while (j>=0 && A[j]>key){A[j + 1] = A[j];j--;}A[j + 1] = key;}return A;};private:};

3 选择排序:选择最大值,放到数组的后面;时间复杂度O(n^2)

class SelectionSort {public:int* selectionSort(int* A, int n) {// write code hereint maxKey = 0;int maxInd = 0;int temp = 0;//每次选择最大的,放在数组的后面for (int i = n - 1; i >= 0; i--){maxKey = A[0];maxInd = 0;//找出最大的for (int j = 0; j <= i; j++){if (A[j] > maxKey){maxKey = A[j];maxInd = j;}}//交换 最大值 和 查找范围末尾位置的值temp = A[i];A[i] = A[maxInd];A[maxInd] = temp;}return A;}};

4 归并排序:递归分解为子问题,将子问题合并。时间复杂度O(nlogn),空间上需要动态分配内存辅助,额外的空间需求

class MergeSort{public:int *mergeSort(int *A,int n){mergeSortAC(A, 0, n - 1);return A;}private://将pa指向的数组,划分成两个小数组,排序后再合并int mergeSortAC(int *pa, int a, int c);//合并函数int merge(int *pa, int a, int b, int c);};//将pa指向的数组,划分成两个小数组,排序后再合并int MergeSort::mergeSortAC(int *pa, int a, int c){//边界条件:只有一个元素,返回if (a >= c) return 0;int b = (a + c) >> 1;//对[a,b],[b+1,c]的两个数组分别排序,递归调用mergeSortAC(pa, a, b);mergeSortAC(pa, b + 1, c);//合并两个已经排好序的数组merge(pa, a, b, c);return 0;}//合并函数int MergeSort::merge(int *pa, int a, int b, int c){//分配两个动态数组int *pb = new int[b - a + 2];assert(pb != NULL);int *pc = new int[c - b + 1];assert(pc != NULL);//将要排序的部分复制到动态数组中int i = a;int j = b + 1;while (i <= b){pb[i - a] = pa[i];i++;}while (j <= c){pc[j - b - 1] = pa[j];j++;}//为动态数组添加末尾无穷大值pb[b - a + 1] = INT_MAX;pc[c - b] = INT_MAX;//逐个比较复制到pa中i = j = 0;for (int k = a; k <= c; k++){if (pb[i] < pc[j]){pa[k] = pb[i];i++;}else{pa[k] = pc[j];j++;}}//释放动态内存delete[] pb;delete[] pc;//返回return 0;}

5 快速排序:随机抽取一个数,小的放左边,大的放右边。利用小于等于区间,可以原址排序。使用随机函数,引入随机选择。理论最差情况,时间复杂度O(n^2),期望复杂度是O(nlogn),实际使用中,快排的速度是最快的,而且由于是原址排序,所以空间需求少,不像归并排序需要O(n)的辅助空间。

#include <stdlib.h>#include <time.h>class QuickSort{public:int *quickSort(int *A, int n){quickSortBC(A,0,n-1);return A;}int *quickSortBC(int *A, int b, int c){//边界条件if (b >= c) return 0;int i = 0;i = partition(A, b, c);//划分quickSortBC(A, b, i-1);//对划分的子集,快速排序quickSortBC(A, i+1, c);return 0;}//划分函数int partition(int *A, int b, int c){//边界条件if (b >= c) return 0;//为快排加入随机选择srand((unsigned)time(0));int ind = b + rand() % (c - b + 1);//ind取值范围[b,c]int key = A[ind];//将ind对应的元素,换到数组末尾A[ind] = A[c];A[c] = key;int temp = key;int i = b - 1;int j = b - 1;while (j < c){j++;if (A[j] <= key){i++;temp = A[i];A[i] = A[j];A[j] = temp;}}//返回key值的索引return i;}private:};


6 堆排序:利用最大堆,每次取堆根,然后维护堆;时间复杂度上界是O(nlogn),堆排在实际中并不快,做了很多无用的比较;

class HeapSort{public:int *heapSort(int *A, int n);private:int buildHeap(int *A, int size);//建堆int maxHeap(int *A, int ind, int size);//维护最大堆inline int exchange(int *A, int a, int b);//交换两个元素};int *HeapSort::heapSort(int *A, int n){//将最小元素换到A[0],因为A[0]是不参与建堆的int minEle = A[0];int ind = 0;for (int i = 0; i < n; i++){if (minEle>A[i]){minEle = A[i];ind = i;}}A[ind] = A[0];A[0] = minEle;//建堆buildHeap(A, n - 1);//循环取堆根放到数组A末尾,维护堆for (int size = n - 1; size > 1;){exchange(A, 1, size);size--;maxHeap(A, 1, size);}return A;}int HeapSort::buildHeap(int *A, int size)//建堆{//from size/2 to 1for (int i = size >> 1; i > 0; i--){maxHeap(A, i, size);}return 0;}int HeapSort::maxHeap(int *A, int ind, int size)//维护最大堆{int largest = ind;int left = (ind << 1);int right = (ind << 1) + 1;if (left <= size && A[left] > A[ind])//与左孩子比较{largest = left;}if (right <= size && A[right] > A[largest])//与右孩子比较{largest = right;}if (largest != ind){exchange(A, largest, ind);maxHeap(A, largest, size);}return 0;}inline int HeapSort::exchange(int *A, int a, int b){int temp = A[a];A[a] = A[b];A[b] = temp;return 0;}


7 希尔排序




















0 0