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

分治算法(2)_快速排序_排序数组

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

分治算法(2)_快速排序_排序数组

收录于专栏【经典算法练习】
本专栏旨在分享学习算法的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

温馨提示:

1. 快速排序简介:

原理:

时空复杂度分析:

优缺点:

2. 题目链接:

3. 题目描述:

4. 解法(快速排序)

快速排序算法思路:

快速排序的缺点;

1. 数组有序或重复效率低

2. 对于较小规模的数据,快速排序可能不如插入排序高效

快速排序的优化:

1. 随机取key

2. 数组分三块  

代码展示:

结果分析: 


温馨提示:

这里并不会对快速排序进行详细讲解,只会对它的思想进行解释,如果大家想看详细讲解版的,可以去下面博客查看:

排序算法(4)之快速排序(1)-CSDN博客

1. 快速排序简介:

原理:

选择基准:从待排序的数组中选择一个元素作为“基准”(Pivot),通常可以选择第一个元素、最后一个元素或随机选择。

分区:将数组中的其他元素根据基准值进行分区。所有小于基准值的元素移动到基准值的左侧,所有大于基准值的元素移动到右侧。这样,基准值的最终位置就确定了。

递归排序:对基准值左侧和右侧的两个子数组递归进行快速排序。

合并结果:由于子数组已经排序完毕,因此只需要将它们与基准值组合在一起即可。

时空复杂度分析:

时间复杂度
平均时间复杂度:O(n log n)
最坏时间复杂度:O(n²),通常发生在选择的基准值极端不平衡时(例如,已经排好序的数组)。
最佳时间复杂度:O(n log n)
空间复杂度
快速排序的空间复杂度为O(log n),因为递归调用的深度为O(log n)。它是一种原地排序算法,不需要额外的存储空间。 

优缺点:

 优点:

平均情况下性能优异。
原地排序,节省空间。
可以选择不同的基准选择策略以优化性能。
缺点:

最坏情况下的时间复杂度较高。
对于较小规模的数据,快速排序可能不如插入排序高效。

2. 题目链接:

OJ链接 : 排序数组

3. 题目描述:

给你一个整数数组 nums,请你将该数组升序排列。

你必须在 不使用任何内置函数 的情况下解决问题,时间复杂度为 O(nlog(n)),并且空间复杂度尽可能小。

示例 1:

输入:nums = [5,2,3,1]
输出:[1,2,3,5]

示例 2:

输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

提示:

  • 1 <= nums.length <= 5 * 104
  • -5 * 104 <= nums[i] <= 5 * 104

4. 解法(快速排序)

快速排序算法思路:

1. 选择基准值: 通常选择数组的第一个元素作为基准值,也可以随机选择数组中的某个元素。

2. 划分过程: 使用两个指针,一个从左边开始(左指针),一个从右边开始(右指针)。它们分别向中间移动,直到找到需要交换的元素。

3. 具体步骤:

        i. 左指针移动: 从左往右找到第一个大于或等于基准值的元素。
        ii. 右指针移动: 从右往左找到第一个小于或等于基准值的元素。
        iii. 交换元素: 如果左指针所指元素大于基准值,并且右指针所指元素小于基准值,则交换这两个元素。
        iv. 继续移动指针: 继续移动左右指针,直到它们相遇。
4. 交换基准值: 最后将基准值与右指针所指的元素进行交换,使得基准值左侧的元素都小于或等于基准值,右侧的元素都大于或等于基准值。

// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right)
{if (right - left <= 1)return;// 按照基准值对array数组的 [left, right)区间中的元素进行划分int div = partion(array, left, right);
}
// 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)
// 递归排[left, div)
QuickSort(array, left, div);
// 递归排[div+1, right)
QuickSort(array, div + 1, right);
class Solution {
public:vector<int> sortArray(vector<int>& nums) {qsort(nums, 0, nums.size() - 1);return nums;}void qsort(vector<int>& arr, int left, int right){if(left >= right) return;int key = left;int begin = left;int end = right;while(begin < end){while(begin < end && arr[end] >= arr[key]) end--;while(begin < end && arr[begin] <= arr[key]) begin++;swap(arr[begin], arr[end]);}swap(arr[key], arr[begin]);key = begin;//[left, key - 1][key][key + 1, right]qsort(arr, left, key - 1), qsort(arr, key + 1, right);}
};

 

快速排序的缺点;

1. 数组有序或重复效率低

当数组有序时,如果key还是取数组中第一个值得话,快排得递归深度就退化为O(N),整体的时间复杂度就退化为O(N^2)

2. 对于较小规模的数据,快速排序可能不如插入排序高效

对于较小的数据集,快速排序的递归深度可能较高,每次递归调用都会消耗额外的栈空间,这在小规模数据上显得不够高效。 

快速排序的优化:

1. 随机取key

因为key取数组第一个数或者最后都会在数组有序中效率退化,所以我们可以直接取随机值,这样我们的递归深度是接近O(logN)--详细证明大家可以去看算法导论 

class Solution {
public:vector<int> sortArray(vector<int>& nums) {qsort(nums, 0, nums.size() - 1);return nums;}void qsort(vector<int>& arr, int left, int right){if(left >= right) return;int key = left;int begin = left;int end = right;while(begin < end){while(begin < end && arr[end] >= arr[key]) end--;while(begin < end && arr[begin] <= arr[key]) begin++;swap(arr[begin], arr[end]);}swap(arr[key], arr[begin]);key = begin;//[left, key - 1][key][key + 1, right]qsort(arr, left, key - 1), qsort(arr, key + 1, right);}
};

可以看到这一次通过了19个例子,卡在一个超长的连续数组, 因为这样无论怎么随机,取到的数都相同,递归深度退化为O(N),需要我们使用数组分成三块来解决!

2. 数组分三块  

我们直接将数组分成三块 : 小于key, 等于key, 大于key.这样我们再遇到上次的超长重复数组,我们可以直接以O(1)的时间复杂度解决它

算法思路:  

1. 定义递归出口

2. 利用随机选择基准函数生成一个基准元素

3. 将数组分成三个区域

4. 递归处理左边区域和右边区域

代码展示:

class Solution {
public:vector<int> sortArray(vector<int>& nums) {srand(time(NULL));qsort(nums, 0, nums.size() - 1);return nums;}void qsort(vector<int>& nums, int l, int r){if(l >= r) return;int key = getRandom(nums,l,r);int i = l, left = l - 1, right = r + 1;while(i < right){if(nums[i] < key) swap(nums[++left], nums[i++]);else if(nums[i] == key) i++;else swap(nums[--right], nums[i]);}qsort(nums, l, left), qsort(nums, right, r);}int getRandom(vector<int>& nums, int left, int right){int r = rand();return nums[left + r % (right - left + 1)];}
};

结果分析: 

经过改良后的快排能过顺利通过!!!并且效率还很不错!!!

最重要的的是代码只有不到30行,大家可以作为模板

这里不得不吐槽一下官方提供的快速排序解法通过不了的问题,总之这个快排算法很优秀! 


http://www.mrgr.cn/news/44702.html

相关文章:

  • 不同jdk版本间的替换
  • 原神5.1前瞻网页HTML+CSS+JS
  • WPF 手撸插件 八 操作数据库一
  • 【C++】入门基础介绍(下)输入输出,函数重载,缺省与引用
  • wxPython中wx.ListCtrl用法(样式和事件)
  • 杭电2041-2050
  • VBA学习(77):Excel表格拆分通用版终极神器
  • 牛客:小红的字符移动,小红的数轴移动,小红的圆移动
  • S7-200 SMAR Modbus RTU主站
  • ubuntu下vscode插件arm keil studio pack遇到的问题
  • 利士策分享,旅游是否要舟车劳顿才能尽兴?
  • 【查找算法概念】与【线性表的相关查找算法】
  • WPF|依赖属性SetCurrentValue方法不会使绑定失效, SetValue方法会使绑定失效?是真的吗?
  • Vue2电商平台(五)、加入购物车,购物车页面
  • 黑马头条(10-1开始学习)
  • 【计算机网络 - 基础问题】每日 3 题(二十九)
  • 数据结构与算法笔记:概念与leetcode练习题
  • 手术器械检测系统源码分享
  • 如何给父母安排体检?
  • Cherno游戏引擎笔记(61~72)