当前位置: 首页 > news >正文

LeetCode力扣刷题——千奇百怪的排序算法

排序算法


一、常见的排序算法

        以下是一些最基本的排序算法。虽然在 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; 
    }
    // divide
    int m = l + (r - l) / 2;
    merge_sort(nums, l, m, temp);
    merge_sort(nums, m, r, temp);
    // conquer
    int 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 )即可,不需要对其左右再进行排序。与快速排序一样,快速选择一般需要先打乱数组,否则最坏情况下时间复杂度为 \small O(n^{2})
        数组中第 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]=value
        int 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);
    }
};

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

相关文章:

  • django基于python的疫情防控下医院人员调动系统--python-计算机毕业设计
  • 详解字符串比较函数:strcmp函数及其模拟实现
  • 【Linux】安装Tomcat以yum方式安装
  • 【羊了个羊】背后的计算机网络原理
  • 面试题--框架篇
  • 字节4面通过,我可以跟面试官要30K吗?
  • Python算法性能分析-时间复杂度
  • java 基本微信小程序的心理咨询服务系统 uniapp 小程序
  • JQ----事件
  • FPGA 20个例程篇:16.HDMI显示彩色风景图
  • 【云原生之Docker实战】使用Docker部署NPS内网穿透工具
  • 应对过载- go-zero源码阅读
  • Python 的“self“参数是什么?
  • 模拟前端ADC芯片LH001-91,用于开发心电、脑电医疗设备
  • CAPL 封装了的SeedKey解锁函数,高复用性
  • 【前端学习】-粗谈选择器
  • canvas 绘制双线技巧
  • Create React App 使用
  • Docker 笔记(2):Dockerfile
  • GitUp, 你不可错过的秀外慧中的git工具
  • HTTP中的ETag在移动客户端的应用
  • Idea+maven+scala构建包并在spark on yarn 运行
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • java正则表式的使用
  • Linux中的硬链接与软链接
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • 初识 beanstalkd
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • 开发了一款写作软件(OSX,Windows),附带Electron开发指南
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 聊聊hikari连接池的leakDetectionThreshold
  • 入门到放弃node系列之Hello Word篇
  • 深入 Nginx 之配置篇
  • 实现菜单下拉伸展折叠效果demo
  • 微信小程序--------语音识别(前端自己也能玩)
  • 转载:[译] 内容加速黑科技趣谈
  • ionic异常记录
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • 阿里云ACE认证之理解CDN技术
  • ​ArcGIS Pro 如何批量删除字段
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ​马来语翻译中文去哪比较好?
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • (30)数组元素和与数字和的绝对差
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (四)Android布局类型(线性布局LinearLayout)
  • (转)【Hibernate总结系列】使用举例
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。