排序算法


一、常见的排序算法

        以下是一些最基本的排序算法。虽然在 C++ 里可以通过 std::sort() 快速排序,而且刷题时很少需要自己手写排序算法,但是熟习各种排序算法可以加深自己对算法的基本理解,以及解出由这些排序算法引申出来的题目。

快速排序(Quicksort

        我们采用左闭右开的二分写法。
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);
}

归并排序(Merge Sort

void merge_sort(vector<int> &nums, int l, int r, vector<int> &temp) {if (l + 1 >= r) {return; }// divideint m = l + (r - l) / 2;merge_sort(nums, l, m, temp);merge_sort(nums, m, r, temp);// conquerint p = l, q = m, i = l;while (p < m || q < r) {if (q >= r || (p < m && nums[p] <= nums[q])) {temp[i++] = nums[p++];} else {temp[i++] = nums[q++];} }for (i = l; i < r; ++i) {nums[i] = temp[i];}
}

插入排序(Insertion Sort

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

冒泡排序(Bubble Sort

void bubble_sort(vector<int> &nums, int n) {bool swapped;for (int i = 1; i < n; ++i) {swapped = false;for (int j = 1; j < n - i + 1; ++j) {if (nums[j] < nums[j-1]) {swap(nums[j], nums[j-1]);swapped = true; } }if (!swapped) {break;} }
}

选择排序(Selection Sort

void selection_sort(vector<int> &nums, int n) {int mid;for (int i = 0; i < n - 1; ++i) {mid = i;for (int j = i + 1; j < n; ++j) {if (nums[j] < nums[mid]) {mid = j;}}swap(nums[mid], nums[i]);}
}
以上排序代码调用方法为:
void sort() {vector<int> nums = {1,3,5,7,2,6,4,8,9,2,8,7,6,0,3,5,9,4,1,0};vector<int> temp(nums.size());sort(nums.begin(), nums.end());quick_sort(nums, 0, nums.size());merge_sort(nums, 0, nums.size(), temp);insertion_sort(nums, nums.size());bubble_sort(nums, nums.size());selection_sort(nums, nums.size());
}

二、经典问题

1. 快速选择

215. 数组中的第K个最大元素

215. Kth Largest Element in an Array

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

        快速选择一般用于求解 k-th Element 问题,可以在 O ( n ) 时间复杂度, O ( 1 ) 空间复杂度完成求 解工作。快速选择的实现和快速排序相似,不过只需要找到第 k 大的枢( pivot )即可,不需要对其左右再进行排序。与快速排序一样,快速选择一般需要先打乱数组,否则最坏情况下时间复杂度为
        数组中第 k 个最大元素其实就是求数组中第 nums.length - k 个最小元素(从第0小开始),我们定义target = nums.length - k。先找一个中枢点(pivot),然后遍历其他数字,小于 pivot 的排左边,大于 pivot 的排右边,中枢点是数组中的第几小的数字就确定了,如果 pivot 与 target 相等,直接返回pivot 位置的数字,如果大于 target ,说明要求的数字在左边部分,否则在右边部分。剩下的就是递归了。
class Solution {
public:// 主函数int findKthLargest(vector<int>& nums, int k) {random_shuffle(nums.begin(),nums.end()); // 打乱数组int l = 0,r = nums.size() - 1,target = nums.size() - k;while(l <= r){int mid = quickSelection(nums,l,r);if(mid == target){return nums[mid];}if(mid < target){l = mid + 1; }else{r = mid - 1;}}return nums[l];}// 辅函数 - 快速选择int quickSelection(vector<int>& nums,int l,int r){int i = l + 1,j = r;while(true){while(i < r && nums[i] <= nums[l]){ // i从左往右寻找大于nums[l]的并停止++i;}while(l < j && nums[j] >= nums[l]){ // j从右往左寻找小于nums[l]的并停止--j;}if(i >= j){ // i和j相遇或擦肩而过break;}swap(nums[i],nums[j]); // 如果没有break,就像快速排序一样一直交换大小值}swap(nums[l],nums[j]);  // l归位return j; // 返回的是这次快排归位的那个数的下标}
};

2. 桶排序

347. 前 K 个高频元素

347. Top K Frequent Elements

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

        顾名思义,桶排序的意思是为每个值设立一个桶,桶内记录这个值出现的次数(或其它属 性),然后对桶进行排序。针对样例来说,我们先通过桶排序得到四个桶 [1,2,3,4] ,它们的值分别 为 [4,2,1,1] ,表示每个数字出现的次数。
        紧接着,我们对桶的频次进行排序,前 k 大个桶即是前 k 个频繁的数。这里我们可以使用各种 排序算法,甚至可以再进行一次桶排序,把每个旧桶根据频次放在不同的新桶内。针对样例来说, 因为目前最大的频次是 4 ,我们建立 [1,2,3,4] 四个新桶,它们分别放入的旧桶为 [[3,4],[2],[],[1]] , 表示不同数字出现的频率。最后,我们从后往前遍历,直到找到 k 个旧桶。
class Solution {
public:vector<int> topKFrequent(vector<int>& nums, int k) {unordered_map<int, int> counts; // 无序映射,用法count[key]=valueint max_count = 0;for(const int & num : nums){ // foreach循环遍历nums数组,每次遍历时把nums中的值赋给num max_count = max(max_count, ++counts[num]); // 找出每个num出现频次最多的为多少次 }vector<vector<int>> buckets(max_count + 1);// vector<vector<int>>a(row,vector<column,0>);定义一个row*column二维动态数组,并初始化为0,row必须定义for(const auto & p : counts){buckets[p.second].push_back(p.first);// buckets[出现次数].push_back(当前元素);p.first为key值为num,p.second为value值为++counts[num]的值}vector<int> ans;for(int i = max_count; i >= 0 && ans.size() < k; --i){ // 索引越高,所储存的数组中的元素出现的次数越多for(const int & num : buckets[i]){ans.push_back(num);if(ans.size() == k){break;}}}return ans;}
};

三、巩固练习

451. 根据字符出现频率排序

451. Sort Characters By Frequency

给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。

返回 已排序的字符串 。如果有多个答案,返回其中任何一个。

桶排序的变形题。
class Solution {
public:string frequencySort(string s) {unordered_map<int, int> counts; int max_count = 0;for(const char & num : s){ max_count = max(max_count, ++counts[num]); }vector<vector<int>> buckets(max_count + 1);for(const auto & p : counts){buckets[p.second].push_back(p.first);}string ans = "";for(int i = max_count; i >= 0 ; --i){ for(const char & num : buckets[i]){int tmp = i;while(tmp--){ans.push_back(num);}}}return ans;}
};

75. 颜色分类

75. Sort Colors

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

必须在不使用库的sort函数的情况下解决这个问题。

很经典的荷兰国旗问题,考察如何对三个重复且打乱的值进行排序。
class Solution {
public:// 主函数void sortColors(vector<int>& nums) {quick_sort(nums,0,nums.size());}// 辅函数 - 快速排序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);}
};

欢迎大家共同学习和纠正指教

LeetCode力扣刷题——千奇百怪的排序算法相关推荐

  1. LeetCode力扣刷题——简单易懂的贪心算法

    贪心 一.算法解释 采用贪心的策略,保证每次操作都是局部最优的,从而使最后得到的结果是全局最优的. 贪心算法问题需要满足的条件: (1)最优子结构:规模较大的问题的解由规模较小的子问题的解组成,规模较 ...

  2. 力扣刷题记录-回溯算法相关题目

    首先介绍一下回溯算法 回溯通常在递归函数中体现,本质也是一种暴力的搜索方法,但可以解决一些用for循环暴力解决不了的问题,其应用有: 1.组合问题: 例:1 2 3 4这些数中找出组合为2的组合,有1 ...

  3. 力扣刷题-python-回溯算法-1(回溯算法模板、题型)

    文章目录 1.回溯算法 2.回溯算法模板 3.回溯实例(77.216.17.39.40.131.93.78.90.491.46.47) 4.总结 1.回溯算法 回溯算法的本质就是穷举,最多再加上剪枝, ...

  4. IDEA LeetCode力扣刷题插件 中文类名 模板

    在用idea力扣插件刷题时,用中文作为类名会出现空格或者其他符号,总所周知,变量名和类名是不能出现空格和其他符号的,在我查阅了官方文档后,发现模板的工具类是继承了Stringutils的,所以在代码模 ...

  5. LeetCode力扣刷题数据库(178):分数排名

    178分数排名 表: Scores +-------------+---------+ | Column Name | Type | +-------------+---------+ | id | ...

  6. LeetCode力扣刷题——居合斩!二分查找

    二分查找 一.算法解释         二分查找也常被称为二分法或者折半查找,每次查找时通过将待查找区间分成两部分并只取 一部分继续查找,将查找的复杂度大大减少.对于一个长度为 O ( n ) 的数组 ...

  7. leetcode(力扣)刷题题解网站

    中文题解直接看力扣的题解 如果想要看国外大神的相关题解,只需要将中文题解网址中的'-cn' 去掉即可 英文题解网址: https://leetcode.com/problems/binary-tree ...

  8. LeetCode力扣刷题——巧解数学问题

    数论 一.引言         对于 LeetCode 上数量不少的数学题,我们尽量将其按照类型划分讲解.然而很多数学题的解法并不通用,我们也很难在几道题里把所有的套路讲清楚,因此我们只选择了几道经典 ...

  9. Leetcode力扣刷题

    704. 二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1. clas ...

最新文章

  1. Linux 下搭建 WordPress 个人站点
  2. SAP WM 自动创建TO单的JOB运行报错 - Enter the storage unit type - 对策
  3. Linux日常命令使用记录
  4. Selenium - 简介
  5. 你以为妹子穿短裙真的是为了诱惑你吗?
  6. linux的基础知识——时序竞态
  7. 价值100大洋的简体中文汉化补丁下载
  8. 网络爬虫中Jsoup请求url
  9. SpringCloud工作笔记082---自动化部署Jenkins_Windows下安装使用Jekins
  10. C#中枚举类型enum的使用[转载]
  11. 百度DuerOS负责人景鲲晋升副总裁,继续向李彦宏汇报
  12. 端如何访问rc_如何进行 Linux 启动时间优化
  13. Ubuntu 18.04 网易云音乐通过修改DPI 放大字体的方法
  14. Java SE基础知识点总结(四)
  15. Lamda 表达式里的Join和GroupJoin的区别, 如何实现SQL的Left Join效果
  16. VC 获取任务栏窗口的句柄
  17. 安装rpm包时提示错误:依赖检测失败
  18. 实现单点登录(伪登录)
  19. Springboot毕设项目华阳大众汽车售卖系统v9f90(java+VUE+Mybatis+Maven+Mysql)
  20. 基础技术 - 如果这篇文章说不清epoll的本质,那就过来掐死我吧!

热门文章

  1. spring控制反转与依赖注入
  2. Mysql进阶学习(四)分组函数与分组查询
  3. 高合汽车发布第二款旗舰HiPhi Z量产定型车
  4. 华为v10的android版本,荣耀V10 P 版本(EMUI 9.0+Android 9.0) 正式版 9.0.0.125 发布说明...
  5. WinHex显示的数据不完整, Size not a multiple of 512. Imcomplete image。的问题解决
  6. 微信新玩法,这个隐藏福利太爽了!!!
  7. 胜利vs50线跟vs100线区别_第3节 基础指标的趋势比K线本身的趋势更可靠(3)
  8. 等你学计算机手机壳三,有十款款超潮的手机壳在等你撩喔!
  9. Linux 修改网卡 IP配置
  10. Linux系统中安装Redis并设置开机