重点:二分查找,归并排序,快速排序

https://blog.csdn.net/weixin_41190227/article/details/86600821
最小的k个数(排序算法) - 图1
最小的k个数(排序算法) - 图2
计数排序、基数排序、桶排序属于非比较排序,其他为比较排序

直接插入排序

  • 步骤1: 从第一个元素开始,该元素可以认为已经被排序;
  • 步骤2: 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  • 步骤3: 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  • 步骤4: 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  • 步骤5: 将新元素插入到该位置后;
  • 步骤6: 重复步骤2~5。

最小的k个数(排序算法) - 图3

  1. //直接插入排序
  2. //size_t大小是由系统的位数决定的,在32位系统中size_t是4字节的,而在64位系统中,size_t是8字节的
  3. void InsertSort(vector<int> &input){
  4. if (input.empty())return;
  5. int cur;
  6. for (size_t i = 0; i < input.size() - 1; i++){
  7. cur = input[i + 1];//因为i+1所以input.size()-1
  8. int pre = i;
  9. while (pre >= 0 && input[pre] > cur){
  10. input[pre + 1] = input[pre];
  11. pre--;
  12. }
  13. input[pre + 1] = cur;
  14. }
  15. }
  • 最佳情况:T(n) = O(n),数组基本有序时,插入排序复杂度最低
  • 最坏情况:T(n) = O(n2)
  • 平均情况:T(n) = O(n2)

希尔排序(缩小增量排序)

最小的k个数(排序算法) - 图4

  1. //希尔排序
  2. void ShellSort(vector<int> & input){
  3. if (input.empty())return;
  4. int len = input.size();
  5. int cur, gap = len / 2;
  6. while (gap > 0){
  7. for (size_t i = 0; i < len - gap; i++){
  8. cur = input[i + gap];
  9. int preIndex = i;
  10. while (preIndex >= 0 && input[preIndex] > cur){
  11. input[preIndex + gap] = input[preIndex];
  12. preIndex -= gap;
  13. }
  14. input[preIndex + gap] = cur;
  15. }
  16. gap /= 2;
  17. }
  18. }
  • 最佳情况:T(n) = O(nlog2n)
  • 最坏情况:T(n) = O(nlog2n)
  • 平均情况:T(n) =O(nlog2n)

选择排序

是不稳定的
例如 3,3,…,2,1 -> 1, 3, …, 2, 3 -> 1, 2, …, 3, 3

  1. //选择排序
  2. void SelectionSort(vector<int> & input){
  3. if (input.empty())return;
  4. for (size_t i = 0; i < input.size(); i++){
  5. int minIndex = i;
  6. for (size_t j = i; j < input.size(); j++){
  7. if (input[j] < input[minIndex]){
  8. minIndex = j;
  9. }
  10. }
  11. int temp = input[minIndex];
  12. input[minIndex] = input[i];
  13. input[i] = temp;
  14. }
  15. }

冒泡排序

  1. //冒泡排序
  2. void Bubble(vector<int> & input){
  3. if (input.empty())return;
  4. for (size_t i = 0; i < input.size(); i++){
  5. bool flag = false;
  6. for (size_t j = 0; j < input.size() - i - 1; j++){
  7. if (input[j] > input[j + 1]){
  8. swap(input[i], input[j+1]);
  9. flag = true;
  10. }
  11. }
  12. if(!flag)break;
  13. }
  14. }

最优:O(N)
平均:O(N2)

归并排序

  • 步骤1:把长度为n的输入序列分成两个长度为n/2的子序列
  • 步骤2:对这两个子序列分别采用归并排序
  • 步骤3:将两个排序好的子序列合并成一个最终的排序序列

最小的k个数(排序算法) - 图5

  1. //归并排序:采用分治法
  2. vector<int> merge(vector<int> left, vector<int> right){
  3. vector<int> result;
  4. for (size_t i = 0, j = 0, k = 0; (j < left.size() || k < right.size());){
  5. if ((j < left.size()) && ((k >= right.size()) || (left[j] <= right[k])))
  6. {
  7. result.push_back(left[j++]);
  8. }
  9. if ((k < right.size()) && ((j >= left.size()) || (left[j] > right[k]))){
  10. result.push_back(right[k++]);
  11. }
  12. }
  13. return result;
  14. }
  15. vector<int> MergeSort(vector<int> input){
  16. if (input.size() < 2)return input;
  17. int mid = input.size() / 2;
  18. vector<int> left(input.begin(), input.begin() + mid);
  19. vector<int> right(input.begin() + mid, input.end());
  20. return merge(MergeSort(left), MergeSort(right));
  21. }
  22. //第二种方法,在原数组上操作
  23. void mergeSort(vector<int> &arr, int lo, int hi){
  24. if (lo == hi) {
  25. return;
  26. }
  27. int mid = ((hi - lo) >> 1) + lo;//加括号阿!!移位运算符优先级很低
  28. mergeSort(arr, lo, mid);
  29. mergeSort(arr, mid + 1, hi);
  30. merge(arr, lo, mid, hi);
  31. }
  32. void merge(vector<int> &arr, int lo, int mid, int hi){
  33. vector<int> temp(hi - lo + 1, 0);
  34. int i = 0, p1 = lo, p2 = mid + 1;
  35. while (p1 <= mid && p2 <= hi) {
  36. if(arr[p1] <= arr[p2]){//必须是<=,不然就不稳定了
  37. temp[i++] = arr[p1++];
  38. }else{
  39. temp[i++] = arr[p2++];
  40. }
  41. }
  42. while (p1 <= mid) {
  43. temp[i++] = arr[p1++];
  44. }
  45. while (p2 <= hi) {
  46. temp[i++] = arr[p2++];
  47. }
  48. for (i = 0; i < temp.size(); i++) {
  49. arr[lo + i] = temp[i];
  50. }
  51. }
  • 最佳情况:T(n) = O(nlogn)
  • 最差情况:T(n) = O(nlogn)
  • 平均情况:T(n) = O(nlogn)

快速排序

快速排序 的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

  • 步骤1:从数列中挑出一个元素,称为 “基准”(pivot );
  • 步骤2:重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 步骤3:递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

最小的k个数(排序算法) - 图6
TIM图片20200303112735.png

三种选取基准的方式:

https://blog.csdn.net/weixin_42636552/article/details/82258184?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
最坏情况:使用第一种,并且数组基本有序。因为每次划分只能使待排序序列减一,此时为最坏情况,快速排序沦为冒泡排序,时间复杂度为O(n^2)。

//快速排序(采用分治法)
int partition(vector<int> & input, size_t start, size_t end){
    swap(input[start], input[start + rand() % (end - start + 1)]);
    //cout << input[start] << endl;
    int pivot = input[start];
    while (start < end){
        while (start < end){
            if (pivot < input[end])end--;
            else{
                input[start++] = input[end];
                break;
            }
        }
        while (start < end){
            if (input[start] < pivot)start++;
            else{
                input[end--] = input[start];
                break;
            }
        }
    }
    input[start] = pivot;
    return start;
}

vector<int> QuickSort(vector<int> & input, size_t start, size_t end){
    size_t mid = partition(input, start, end);
    if (mid > start)QuickSort(input, start, mid - 1);
    if (mid < end)QuickSort(input, mid + 1, end);
    return input;
}

堆排序

大顶堆是升序,小顶堆是降序

  • 步骤1:将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
  • 步骤2:将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
  • 步骤3:由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

最小的k个数(排序算法) - 图8

void adjustHeap(vector<int> &input, int i, int len){
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        int maxIndex = i;
        if(left < len && input[left] > input[maxIndex]){
            maxIndex = left;
        }
        if(right < len && input[right] > input[maxIndex]){
            maxIndex = right;
        }
        if(maxIndex != i){
            swap(input[i], input[maxIndex]);
            adjustHeap(input, maxIndex, len);//向下调整
        }
    }
    void HeapSort(vector<int> &input){
        int len = input.size();
        for(int i = len / 2 - 1;i >= 0;i--){
            adjustHeap(input, i, len);
        }
        while(len > 1){
            swap(input[0], input[len-1]);//此时input[0]是最大值
            len--;
            adjustHeap(input, 0, len);//向下调整
        }
    }