排序算法是一种很重要的算法,虽然c++ stl中有sort函数,但是还是要了解其中常用的几种排序算法,至于std::sort源码实现使用的排序算法是混合式排序,详见std::sort源码剖析
std::sort只支持vector,array等迭代器支持随机存取的容器,因为内部是快排和堆排
std::list这种不支持随机存取,内部有单独的sort函数,实现是迭代版归并排序

有些排序算法时间复杂度都是受限原数据的排序情况的,比如最坏的情况原数组完全逆序,快速排序时间复杂度是O(n^2),但是最好的情况原数组已排序,快速排序时间复杂度是O(nlogn)
堆排序最好和最坏都是O(nlogn),但是大多数情况快排要优于堆排(为什么在平均情况下快速排序比堆排序要优秀)

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

class Solution {
public:
    //冒泡排序:每次两两比较直到将最大数排到末尾,之后边界减1,继续上面操作
    void bubble_sort(vector<int>& arr) {
        for (int i = 0; i < arr.size(); i++) {
            for (int j = 1; j < arr.size() - i; j++) {
                if (arr[j - 1] > arr[j]) {
                    int tmp = arr[j - 1];
                    arr[j - 1] = arr[j];
                    arr[j] = tmp;
                }
            }
        }
    }

    //插入排序:先排序前面的部分,再在已排序序列中从后向前扫描,找到相应位置并插入,为什么已排序序列是从后向前扫描,因为插入后后面元素位置要往后移,从后往前扫描正好可以顺势移动
    void insert_sort(vector<int>& arr) {
        for (int i = 1; i < arr.size(); i++) {
            int cur = arr[i];
            int pre = i - 1;
            while (pre >= 0 && cur < arr[pre]) {
                arr[pre+1] = arr[pre];
                pre--;
            }
            arr[pre+1] = cur;
        }
    }

    //选择排序: 每次记录最小值的位置和"首元素"比较大小,"首元素"递增
    void select_sort(vector<int>& arr) {
        for (int i = 0; i < arr.size(); i++) {
            int min_index = i;
            for (int j = i + 1; j < arr.size(); j++) {
                if (arr[j] < arr[min_index]) {
                    min_index = j;
                }
            }
            std::swap(arr[i], arr[min_index]);
        }
    }

    //快速排序(参考https://cloud.tencent.com/developer/article/1425422)
    int partition(std::vector<int> &sort_vec, int start, int end)
    {
          int pos = start + 1; // partiton最重要的就是这个参考位,为了防止移动比基准值小值需要将后续元素都往后移一位复杂度太高,这里使用一个参考位交换比基准值小的数
          for (int i = start + 1; i < end; i++) {
              if (sort_vec[i] < sort_vec[start]) { 
                  std::swap(sort_vec[i], sort_vec[pos]); // 如果之后元素有比基准小的,先与sort_vec[pos]交换
                  pos++; // 基准元素sort_vec[start]先不动,pos值加一,后续再有比sort_vec[start]小的与sort_vec[pos]交换,直至退出循环
              }
          }
          std::swap(sort_vec[start], sort_vec[pos - 1]); // 最后sort_vec[pos - 1]左边的(包括自己)除了sort_vec[start]都比sort_vec[start]小,右边都比sort_vec[start]大,于是交换sort_vec[start]和sort_vec[pos - 1]
          return pos - 1;
      }

      void quicksort(std::vector<int> &sort_vec, int start, int end)
      {
          if (start >= end) return; // 不断二分直至start大于end
          int pos = partition(sort_vec, start, end); // 调用partition函数将元素比基准值小的划到基准值左边,比基准值大的划到基准值右边
          quicksort(sort_vec, start, pos - 1); // 递归左边
          quicksort(sort_vec, pos + 1, end);  // 递归右边
      }

      // 归并排序, 分治思想,不断二分到只剩一个元素,然后两边已排序数组再不断比较合成一个新的排序数组,如此重复,归并排序虽然最坏时间复杂度也是nlogn,但是递归过程中有大量的临时对象复制,实际没快速排序快
      std::vector<int> merge(std::vector<int> left, std::vector<int> right)
      {
          std::vector<int> tmp;
          int l = 0, r = 0;
          while (l < left.size() && r < right.size()) {
              if (left[l] <= right[r]) {
                  tmp.push_back(left[l++]);
              }
              else {
                  tmp.push_back(right[r++]);
              }
          }
          if (r == right.size()) {
              tmp.insert(tmp.end(), left.begin() + l, left.end());
          }
          else if (l == left.size()) {
              tmp.insert(tmp.end(), right.begin() + r, right.end());
        return tmp;
      }

      std::vector<int> merge_sort(std::vector<int> &sort_vec)
      {
          if (sort_vec.size() < 2) return sort_vec;
          int mid = sort_vec.size() / 2;
          std::vector<int> left(sort_vec.begin(), sort_vec.begin() + mid);
          std::vector<int> right(sort_vec.begin() + mid, sort_vec.end());
          return merge(merge_sort(left), merge_sort(right));
      }

      // 归并排序2,上一个临时对象太多,可优化一下
      void merge(std::vector<int> &sort_vec, int left_s, int left_e, int right_s, int right_e)
      {
          int l = left_s, r = right_s;
          std::vector<int> tmp; // 这里需要一个临时数组保存这次排序好的部分元素
          while (l <= left_e && r <= right_e) {
              if (sort_vec[l] <= sort_vec[r]) {
                  tmp.push_back(sort_vec[l++]);
              }
              else {
                  tmp.push_back(sort_vec[r++]);
              }
          }
          while (l <= left_e) tmp.push_back(sort_vec[l++]);
          while (r < right_e) tmp.push_back(sort_vec[r++]);
          // 排序好后再把数据复制会原数组
          for (int i = 0; i < tmp.size(); i++) {
              sort_vec[left_s + i] = tmp[i];
          }
          return;
      }

      void merge_sort(std::vector<int> &sort_vec, int start, int end)
      {
          if (start >= end) return;
          int mid = (start + end) / 2;
          merge_sort(sort_vec, start, mid); // 归并排序左半部分
          merge_sort(sort_vec, mid + 1, end); // 归并排序右半部分
          merge(sort_vec, start, mid, mid + 1, end); // 合并
      }
};
posted on 2020-06-08 21:46  陈不白  阅读(209)  评论(0)    收藏  举报