视频学习网址:点击打开链接
参考书籍《算法导论》
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 (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)
- {
-
- int 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) {
-
- int 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:
-
- int mergeSortAC(int *pa, int a, int c);
-
-
- int merge(int *pa, int a, int b, int c);
- };
-
-
-
- int MergeSort::mergeSortAC(int *pa, int a, int c)
- {
-
- if (a >= c) return 0;
-
- int b = (a + c) >> 1;
-
-
- 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;
-
-
- 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);
- int key = A[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;
- }
- }
-
-
- 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)
- {
-
- 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);
-
-
- 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)
- {
-
- for (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 希尔排序