算法系列--分治排序|再談快速排序|快速排序的優化|快速選擇算法

前言:本文就前期學習快速排序算法的一些疑惑點進行詳細解答,并且給出基礎快速排序算法的優化版本

一.再談快速排序

快速排序算法的核心是分治思想,分治策略分為以下三步:

  1. 分解:將原問題分解為若干相似,規模較小的子問題
  2. 解決:如果子問題規模較小,直接解決;否則遞歸解決子問題
  3. 合并:原問題的解等于若干子問題解的合并

應用到快速排序算法:

  1. 分解:快速排序算法要實現的是對整個數組進行排序,但是規模較大,要想辦法減少規模;他的解決策略是"選擇一個基準元素,將數組劃分為兩部分,左邊都是小于基準元素,右邊都是大于基準元素",不斷的重復上述過程,就能完成對整個數組的排序.對整個數組完成一次這樣的操作后,再對左右兩個區間分別執行上述過程
  2. 遞歸地對兩個子數組進行快速排序,直到每個子數組的長度為0或1,此時數組已經有序。
  3. 由于在遞歸過程中子數組已經被分別排序,因此不需要再進行額外的合并步驟。

二.代碼實現和細節講解

快速排序的關鍵代碼在于如何根據基準元素劃分數組區間(parttion),分解的方法有很多,這里只提供一種方法挖坑法
代碼:

class Solution {public int[] sortArray(int[] nums) {quick(nums, 0, nums.length - 1);return nums;}private void quick(int[] arr, int start, int end) {if(start >= end) return;// 遞歸結束條件int pivot = parttion(arr, start, end);// 遞歸解決子問題quick(arr, start, pivot - 1);quick(arr, pivot + 1, end);}// 挖坑法進行分解private int parttion(int[] arr, int left, int right) {int key = arr[left];while(left < right) {while(left < right && arr[right] >= key) right--;arr[left] = arr[right];while(left < right && arr[left] <= key) ++left;arr[right] = arr[left];}arr[left] = key;return left;}}

細節解答:
1.為什么start>=end是遞歸結束條件?

不斷的分解子問題,最終子問題的規模大小是1,即只有一個元素,此時無需繼續進行分解,start和end指針同時指向該元素

2.為什么要right先走?而不是left先走?

具體誰先走取決于基準元素的位置,在上述代碼中,基準元素(key)是最左邊的元素,如果先移動left,left先遇到一個比基準元素大的元素,此時執行arr[right] = arr[left],由于沒有保存arr[right],這個元素就會丟失
如果先走right,right先遇到一個比基準元素小的元素,此時執行arr[left]=arr[right],因為此時left并沒有移動,還是pivot,但是pivot已經被我們使用key進行保存了

3.為什么是arr[right]>=key?>不行嗎

大于等于主要是為了處理重復元素問題
例如有數組[6,6,6,6,6]如果是>,right指針不會發生移動,left指針也不會發生移動,此時陷于死循環

4.為什么叫做挖坑法

當r指針遇到第一個<pivot的元素后停止,執行arr[r] = arr[l],此時l位置就空白出來,形成了一個坑


三.快速排序的優化

主要有兩個優化方向:

  1. 基準值pivot的選取,可以證明的是當隨機選取基準值時,快速排序的時間復雜度趨近于O(N*logN),即最好的時間復雜度
  2. 重復元素的處理:如果區間內部有大量的重復元素,上述版本的快速排序算法會對相同的元素重復執行多次;為了減少冗余的操作,使用數組分三塊的思想解決,同時如果遇到特殊的測試用例(順序數組或逆序數組)時間復雜度會退化到O(N^2)

先根據一道題目(顏色分類)了解什么是數組分三塊
分析

在這里插入圖片描述
代碼:

class Solution {public void sortColors(int[] nums) {// 分治 --// 1.定義三指針int i = 0;// 遍歷整個數組int l = -1, r = nums.length;while(i < r) {if(nums[i] == 0) swap(nums,++l,i++);else if(nums[i] == 1) i++;else swap(nums,--r,i);}return;}private void swap(int[] nums,int x,int y) {int tmp = nums[x]; nums[x] = nums[y]; nums[y] = tmp;}
}
  • 注意l,r的起始位置,第一個元素和最后一個元素在開始的時候屬于未處理狀態,所以`l,r不能指向這兩個元素,必須在區間之外
  • 所謂的數組分三塊,就是使用三個指針去分別維護四個區間,其中的一個區間是未處理區間,隨著cur指針的不斷移動,所有的區間都被處理,最終也就只有三個區間

將上述思路應用于快速排序的parttion中,最終的結果就是劃分為三個區間
在這里插入圖片描述
代碼實現:

class Solution {// 快速排序優化版// 分解--解決--合并public int[] sortArray(int[] nums) {qsort(nums, 0, nums.length - 1);return nums;}private void qsort(int[] nums, int start, int end) {if(start >= end) return;// 遞歸結束條件// 分解int pivot = nums[start];int l = start - 1, r = end + 1, i = start;while(i < r) {int cur = nums[i];if(cur < pivot) swap(nums, ++l, i++);else if(cur == pivot) ++i;else swap(nums, --r, i);}// [start, l]  [l+1, r-1]  [r, end]// 遞歸解決qsort(nums, start, l);qsort(nums, r, end);}private void swap(int[] nums,int i, int j) {int tmp = nums[i];  nums[i] = nums[j]; nums[j] = tmp;}
}

在這里插入圖片描述
2.隨機選取基準值
采用隨機數的方式隨機選取基準值

        int pivot = nums[start + new Random().nextInt(end - start + 1)];//               起始位置      隨機產生的偏移量

完整的改進代碼:

class Solution {// 快速排序優化版// 分解--解決--合并public int[] sortArray(int[] nums) {qsort(nums, 0, nums.length - 1);return nums;}private void qsort(int[] nums, int start, int end) {if(start >= end) return;// 遞歸結束條件// 分解int pivot = nums[start + new Random().nextInt(end - start + 1)];int l = start - 1, r = end + 1, i = start;while(i < r) {int cur = nums[i];if(cur < pivot) swap(nums, ++l, i++);else if(cur == pivot) ++i;else swap(nums, --r, i);}// [start, l]  [l+1, r-1]  [r, end]// 遞歸解決qsort(nums, start, l);qsort(nums, r, end);}private void swap(int[] nums,int i, int j) {int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp;}
}

在這里插入圖片描述

  • 效率明顯提升

四.快速選擇算法

快速選擇算法是基于快速排序優化版本的一種時間復雜度可達到O(N)的選擇算法,使用場景為第K大/前K大之類的選擇問題

01.數組中的第K個最大的元素
鏈接:https://leetcode.cn/problems/kth-largest-element-in-an-array/
分析

  • 暴力解法就是直接使用sort進行排序,然后返回第K大即可
  • 時間復雜度:O(N*logN)
  • 空間復雜度:O(logN)遞歸產生的棧調用

接下來采用快速選擇算法,實現O(N)的時間復雜度
代碼:

class Solution {public int findKthLargest(int[] nums, int k) {return qsort(nums, 0, nums.length - 1, k);}private int qsort(int[] nums, int start, int end, int k) {if(start >= end) return nums[start];int pivot = nums[start + new Random().nextInt(end - start + 1)];// 數組分三塊  <pivot  ==pivot  >pivotint l = start - 1, r = end + 1, i = start;while(i < r) {if(nums[i] < pivot) swap(nums, ++l, i++);else if(nums[i] == pivot) ++i;else swap(nums, --r, i);}// [start, l]  [l+1, r - 1]  [r, end]int c = end - r + 1, b = r - 1 - (l + 1) + 1, a = l - start + 1;// 分情況討論  進行選擇if(c >= k) return qsort(nums, r, end, k);else if(b + c >= k) return pivot;else return qsort(nums, start, l, k - b - c);// 找較小區間的第(k-b-c)大}private void swap(int[] arr, int i, int j) {int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;}
}
  • 快速選擇算法和快速排序的思想很像,不同點在于快速選擇算法只對每次parttion結果的一部分區間進行遞歸,而不是像快速排序一樣對整個區間進行遞歸,所以快速選擇算法的時間復雜度降到了O(N)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/40896.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/40896.shtml
英文地址,請注明出處:http://en.pswp.cn/web/40896.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

策略模式的應用

前言 系統有一個需求就是采購員審批注冊供應商的信息時&#xff0c;會生成一個供應商的賬號&#xff0c;此時需要發送供應商的賬號信息&#xff08;賬號、密碼&#xff09;到注冊填寫的郵箱中&#xff0c;通知供應商賬號信息&#xff0c;當時很快就寫好了一個工具類&#xff0…

Python 學習中什么是字典,如何操作字典?

什么是字典 字典&#xff08;Dictionary&#xff09;是Python中的一種內置數據結構&#xff0c;用于存儲鍵值對&#xff08;key-value pair&#xff09;。字典的特點是通過鍵來快速查找值&#xff0c;鍵必須是唯一的&#xff0c;而值可以是任何數據類型。字典在其他編程語言中…

vue實現搜索文章關鍵字,滑到指定位置并且高亮

1、輸入搜索條件&#xff0c;點擊搜索按鈕 2、滑到定位到指定的搜索條件。 <template><div><div class"search_form"><el-inputv-model"searchVal"placeholder"請輸入關鍵字查詢"clearablesize"small"style&quo…

HashMap的底層實現原理詳解

HashMap是Java中最常用的集合類之一&#xff0c;其基于哈希表的Map接口實現&#xff0c;提供了快速的鍵值對存儲和檢索功能。深入理解HashMap的底層實現原理&#xff0c;對于提升編程技能、應對技術面試以及優化程序性能都具有重要意義。以下從技術難點、面試官關注點、回答吸引…

數據庫作業day3

創建一個student表用于存儲學生信息 CREATE TABLE student( id INT PRIMARY KEY, name VARCHAR(20) NOT NULL, grade FLOAT ); 向student表中添加一條新記錄 記錄中id字段的值為1&#xff0c;name字段的值為"monkey"&#xff0c;grade字段的值為98.5 insert into …

對于老百姓而言VR到底能做什么?

VR技術自誕生以來不斷發展&#xff0c;已經廣泛應用于教育、醫療、工程、軍事、航空、航海、影視、娛樂等方面&#xff0c;譬如&#xff0c;大型工程或軍事活動VR預演可以大幅度減少人力物力投入&#xff1b;在航空領域&#xff0c;航天飛行員在訓練艙中面對屏幕進行各種駕駛操…

mysql修改密碼失敗報錯無法登錄解決辦法

mysql: [Warning] Using a password on the command line interface can be insecure. ERROR 1045 (28000): Access denied for user root@localhost (using password: YES) 這個問題是因為在嘗試使用命令行連接MySQL時,使用了明文密碼,這是不安全的。同時,由于某種原因,您…

Kylin中的查詢引擎:大數據查詢加速的引擎解析

Kylin中的查詢引擎&#xff1a;大數據查詢加速的引擎解析 Apache Kylin是一個開源的分布式分析引擎&#xff0c;專為大規模數據集提供快速的SQL查詢和多維分析&#xff08;OLAP&#xff09;能力。在Kylin的架構中&#xff0c;查詢引擎&#xff08;Query Engine&#xff09;扮演…

【Linux進階】文件系統4——文件系統特性

1.磁盤組成與分區的復習 首先說明一下磁盤的物理組成&#xff0c;整塊磁盤的組成主要有&#xff1a; 圓形的碟片&#xff08;主要記錄數據的部分&#xff09;&#xff1b;機械手臂&#xff0c;與在機械手臂上的磁頭&#xff08;可擦寫碟片上的數據);主軸馬達&#xff0c;可以…

打開瀏覽器控制臺,點擊應用,瀏覽器崩潰

調試的時候&#xff0c;打開控制臺&#xff0c;點擊 “應用” 立馬瀏覽器奔潰&#xff0c;但是點擊別的沒問題 調查發現是因為manifest.json這個文件引起的 manifest.json 最主要的原因是因為沒有設置這個sizes字段 Google瀏覽器更新大概到126之后的版本會有問題&#xff0c;之…

AI多模態教程:Qwen-VL多模態大模型實踐指南

一、模型介紹 Qwen-VL&#xff0c;由阿里云研發的大規模視覺語言模型&#xff08;Large Vision Language Model, LVLM&#xff09;&#xff0c;代表了人工智能領域的一個重大突破。該模型具有處理和關聯圖像、文本、檢測框等多種類型數據的能力&#xff0c;其輸出形式同樣多樣…

代碼隨想錄Day69(圖論Part05)

并查集 // 1.初始化 int fa[MAXN]; void init(int n) {for (int i1;i<n;i)fa[i]i; }// 2.查詢 找到的祖先直接返回&#xff0c;未進行路徑壓縮 int.find(int i){if(fa[i] i)return i;// 遞歸出口&#xff0c;當到達了祖先位置&#xff0c;就返回祖先elsereturn find(fa[i])…

py基礎語法簡述

py基礎&#xff0c;常用sdk 一些要點 python中是沒有常量的關鍵字的&#xff0c;只是我們常常約定使用大寫字符組合的變量名表示常量&#xff0c;也有“不要對其進行賦值”的提醒操作 PI 3.14python3中有六個標準的數據類型&#xff1a; Number(數字)、String(字符串)、Boo…

基于Python爬蟲的城市二手房數據分析可視化

基于Python爬蟲的城市二手房數據分析可視化 一、前言二、數據采集(爬蟲,附完整代碼)三、數據可視化(附完整代碼)3.1 房源面積-總價散點圖3.2 各行政區均價3.3 均價最高的10個小區3.4 均價最高的10個地段3.5 戶型分布3.6 詞云圖四、如何更換城市一、前言 二手房具有價格普…

CSS position屬性之relative和absolute

目錄 1 參考文章2 五個屬性值3 position:static4 position:relative&#xff08;相對&#xff09;5 position:absolute&#xff08;絕對&#xff09; 1 參考文章 https://blog.csdn.net/lalala_dxf/article/details/123566909 https://blog.csdn.net/WangMinGirl/article/deta…

最靈活且易用的C++開源串口通信調試軟件

這款C開源串口調試軟件&#xff0c;集成了豐富的功能&#xff0c;為用戶提供高效、便捷的串口通信調試體驗。以下是其核心功能亮點&#xff1a; 基礎功能&#xff1a; 數據交互自如&#xff1a;支持串口數據的輕松讀取與發送&#xff0c;讓數據交換變得簡單直接。 靈活配置參…

基于順序表的通訊錄實現

一、前言 基于已經學過的順序表&#xff0c;可以實現一個簡單的通訊錄。 二、通訊錄相關頭文件 //Contact.h #pragma once#define NAME_MAX 20 #define TEL_MAX 20 #define ADDR_MAX 20 #define GENDER_MAX 20typedef struct PersonInfo {char name[NAME_MAX];char gender[G…

Python的招聘數據分析與可視化管理系統-計算機畢業設計源碼55218

摘要 隨著互聯網的迅速發展&#xff0c;招聘數據在規模和復雜性上呈現爆炸式增長&#xff0c;對數據的深入分析和有效可視化成為招聘決策和招聘管理的重要手段。本論文旨在構建一個基于Python的招聘數據分析與可視化管理系統。 該平臺以主流招聘平臺為數據源&#xff0c;利用Py…

MSPM0G3507——解決printf重定向在其他位置不能用的問題(printf重定向的補充)

除了之前發的文章的printf重定向的代碼之外&#xff0c;還要加上這樣一段代碼即可 int puts(const char *_ptr) {int count fputs(_ptr,stdout);count fputs("\n",stdout);return count;} 完整的重定向&#xff1a; int fputc(int c, FILE* stream) {DL_UART_Main_…

昇思25天學習打卡營第2天|MindSpore快速入門

打卡 目錄 打卡 快速入門案例&#xff1a;minist圖像數據識別任務 案例任務說明 流程 1 加載并處理數據集 2 模型網絡構建與定義 3 模型約束定義 4 模型訓練 5 模型保存 6 模型推理 相關參考文檔入門理解 MindSpore數據處理引擎 模型網絡參數初始化 模型優化器 …