LeetCode4. 排序算法

1、快速排序:

主要思路:参考快速排序 - 如果天空不死 - 博客园

选择一个分界值,一般初始可以是数组的第一个元素。第一次排序:使得大于等于该元素的值都在右边,小于该元素的值都在左边。相当于将数组分成两个独立的子数组,再递归对两个子数组进行排序。

快速排序主要用于求解K-th Element问题,即从一个数组中找到第K大的数字。

void quick_sort(vector<int> &nums, int l, int r) {
  if (l + 1) >= r return;
  int first = l, last = r - 1, key = nums[first];
  while (first < last) {
    while (first < last && nums[last] >= key) --last;
    nums[first] = nums[last];
    while (first < last && nums[first <= key]) ++first;
    nums[last] = nums[first];
  }
  nums[first] = key;
  quick_sort(nums, l, first);
  quick_sort(nums, first+1, r);
}

2、归并排序

主要思路:参考 图解排序算法(四)之归并排序 - dreamcatcher-cx - 博客园

归并排序是分而治之的思想,先去把数组分成最小的块,也就是单个元素,然后有序合并两个块。这里重点是要搞清楚怎么用递归去写正确。

void merge_sort(vector<int> &nums, int l, int r, vector<int> &temp) {
  if (l+1) >= r return;
  int m = l + (r-l)/2;

  //切块
  merge_sort(nums, l, m, temp);
  merge_sort(nums, m, r, temp);
  
  //合并
  int p = l, q = m, i = l;
  while (p < m || q < r) {
    if (q >= r || nums[p] <= nums[q]) {
      temp[i++] = nums[p++];
    }
    else temp[i++] = nums[q++];
  }
  
  //将temp数组复制到nums数组
  for (i = l; i < r; i++) nums[i] = temp[i];
}

3、插入排序

主要思路:每一步将一个待排序的数据插入到前面已经排好序的有序序列中,直到插完所有元素为止。

void insert_sort(vector<int> &nums, int n) {
  if (n <= 1) return;
  for (int i = 1; i < n; i++) {
    for (int j = i; j > 0 && nums[j]<nums[j-1]; j--) {
      swap(nums[j], nums[j-1]);
    }
  }
}

4、冒泡排序

主要思路:每一次遍历,都选出最大的元素放到数组末尾,直到所有元素都被选完。

void gudugudu_sort(vector<int> &nums, int n) {
  if (n <= 1) return;
  bool swapped; //注意在一次遍历中,如果从未交换过,则证明数组本身就是有序的,直接跳出循环。
  for (int i = 0; i < n-1; i++) { //只需要遍历n-1次,因为最后剩下的那个元素就是最小的了。
    swapped = false;
    for (int j = 1; j < n - i; j++) {
      if (nums[j] < nums[j-1]) {
        swap(nums[j], nums[j-1]);
        swapped = true;
      }
    }
    if (!swpped) break;
  }
}

5、选择排序

主要思路:每次都从数组中选择最大的元素,放在数组的末尾处。不稳定的排序算法。

void gudugudu_sort(vector<int> &nums, int n) {
  if (n <= 1) return;
  int m, temp;
  for (int i = 0; i < n-1; i++) {
    m = 1;
    for (int j = 1; j < n - i; j++) {
      if (max_val < nums[j]) {
        m = j;
      }
    }
    //将m位置,和n-i-1位置交换。
    temp = nums[n-i-1];
    nums[n-i-1] =nums[m];
    nums[m] = temp;
  }
}

6、选出无序数组nums中第K大的元素

LeetCode215 力扣

主要思路:

*** 快速排序可以通过一次遍历,就把数组分成:{ 小于等于起始元素的左边区间,起始元素,大于等于起始元素的右边区间 } 这三部分。这种方法天然就比较适合找第K大元素的场景。

**** 对于数组[3,2,1,5,6,4],找它的第2大元素,可以转换成找第5小的元素(5 = nums.size() - K +1)。因为数组的下标从0开始,所以第5小就是有序数组中index=4的位置。 第一遍快排遍历完,数组变为[1,2,3,5,6,4],中间index=2(比较位元素3所落在的位置)。而我们的答案应该是中间index=4的元素。
****第一次快排遍历完,如果中间位置的index并不是K,那么我们可以将区间缩小到[0, 中间index-1],或者[中间index+1, nums.size()-1],继续下一次的快排。对于上面那个例子来说,搜索区间的子数组变为[5,6,4]。继续下一次快排。

**** 注:index表示数组的下标,从0开始计数。

//主函数
int findKthLargest(vector<int>& nums, int k) {
  int l = 0, r = nums.size()-1, target = nums.size()-k, mid;
  while (l < r) {
    mid = quickSort(nums, l, r);
    if (mid == target) return nums[mid];
    else if (target > mid) l = mid + 1;
    else r = mid - 1;
  }
  return nums[l];
}

//辅助函数,交换函数
void swap(int& a, int& b) {
  int tmp = a;
  a = b;
  b = tmp;
}

//辅助函数,快排
int quickSort(vector<int>& nums, int l, int r) {
  int i = l + 1, j = r; //这里为什么+1,因为l位置是比较位,不参与快排遍历
  while (true) {
    while (i < r && nums[i] <= nums[l]) i++;
    while (j > l && nums[j] >= nums[l]) j--;
    if (i >= j) break; //这种情况下,已经表明左边都是小的,右边都是大的。不需要再swap。
    swap(nums[i], nums[j]);
  }
  swap(nums[l], nums[j]);
  return j;
}

7、选出前K个高频元素 

LeetCode 347  力扣

思路:首先需要知道每个元素的频次是多少;然后定义一个二维数组buckets,buckets[i]存放的是频次为i的所有元素。从后往前遍历buckets,取出K个元素即可。

vector<int> topKFrequent(vector<int>& nums, int k) {
  //step1. 统计每个元素的出现频次,存在一个map里
  unordered_map<int, int> counts; //注意要学会map的定义。
  int max_count = 0;
  for (const auto& num: nums) max_count = max(max_count, ++counts[num]);

  //step2. 定义一个桶,每个桶存放相同频次的元素。
  vector<vector<int>> buckets(max_count+1);
  for (const auto& p: counts) {
    buckets[p.second].push_back(p.first);
  }
  
  //step3. 从后往前遍历buckets,直到取出K个元素为止。
  vector<int> res;
  bool flag = false;
  for (int i = max_count; i >= 0; i--) {
    for (const auto& val: buckets[i]) {
      if (res.size() >= k) {
        flag = true;
        break;
      }
      res.push_back(val);  
    }
    if (flag) break;
  }

  return res;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值