【基礎算法總結】分治—快排

分治—快排

  • 1.分治
  • 2.顏色分類
  • 3.排序數組
  • 4.數組中的第K個最大元素
  • 5.庫存管理 III

在這里插入圖片描述

點贊👍👍收藏🌟🌟關注💖💖
你的支持是對我最大的鼓勵,我們一起努力吧!😃😃

1.分治

分治思想就如同它的名字一樣:分而治之

將一個大問題劃分成若干個相同或者相識的子問題。然后在將子問題在劃分成若干個相同或者相識的子問題,直到劃分到不能在劃分。然后解決子問題,子問題都解決完了,大問題也就被解決完了。快速排序和歸并排序就用的分治思想。

在這里插入圖片描述

以前我們學快速排序是在數組中選擇一個基準元素,然后將數組分成左右兩個區間,左區間比基準元素小,右區間比基準元素大。然后遞歸的去左區間和有區間排序,這種做法是將數組分成了兩份。但是對于重復元素非常多的數組即使使用快速排序也會超時。因此這里我們學習新的劃分方法,將數組劃分成三份。

還是選一個基準元素將數組劃分成三份,左區間元素都比基準元素小。中間區間元素和基準元素相同,右區間元素都比基準元素大。因為中間都是等于key的一定就是在最終位置,所以接下來遞歸還是只考慮左區間和右區間。

在這里插入圖片描述

2.顏色分類

題目鏈接:75. 顏色分類

題目分析:

在這里插入圖片描述
說起來這道題并不是真正的分治快速排序,而是把數組按照一定規則劃分成三塊的。當把這道題解決后,快排寫的就非常簡單。

這道題就相當于選擇一個基準元素1,把小于1的放左邊,大于1的放右邊,等于1的放中間。

算法原理:

雙指針可以將數組分成兩塊,具體怎么分,雙指針系列第一道題移動零。這里我們需要三個指針將數組分成三塊!

每個指針的作用:
i指針:遍歷整個數組
left:標記 0 區域的最右側
right:標記 2 區域的最左側

在這里插入圖片描述

三個指針將數組分成4份:
[0,left] :全都是0
[left+1,i-1]:全都是1
[i,right-1]:待掃描的元素
[rigth+1,n-1]:全都是2

在這里插入圖片描述

接下來就討論nums[i]是0或1或2應該怎么辦?

nums[i]為0的時候,要把0加入到左邊區域,left指向的是 0 最右側區域,此時left+1,然后將 i 位置和 left+1 元素交換,然后i+1。

在這里插入圖片描述

還有一種極端情況 i 就在 left+1的為位置,并且正好是0。但是這種處理方法和上面一樣。

在這里插入圖片描述

nums[i]為1的時候,i 指針往后走就行了

在這里插入圖片描述

nums[i]為2的時候,我們要將2加入到右邊區間,也就是加入到 right - 1 的位置。交換 i 位置和 right -1 位置的元素。但是此時需要注意的是 交換給 i 位置的元素是待掃描的元素,因此 i 指針不能往后走!

在這里插入圖片描述

class Solution {
public:void sortColors(vector<int>& nums) {int n = nums.size();int i = 0, left = -1, right = n;while(i < right){if(nums[i] == 0)swap(nums[++left], nums[i++]);else if(nums[i] == 2)swap(nums[--right], nums[i]);else++i;}}
};

3.排序數組

題目鏈接:912. 排序數組

題目描述:

在這里插入圖片描述
算法原理:

在數組中選擇一個基準元素key,根據key將數組分成左右區間,左區間元素小于等于key,右區間元素大于key。這個key就處于排序的最終位置。然后在將左區間排排序,右區間排排序,重復上述過程。整體就有序了。時間復雜度(nlogn)

但是如果數組都是重復元素,此時選擇基準元素間將數組分成左右兩區間就不行了。時間復雜度退化成O(n^2)

在這里插入圖片描述

所以我們學習一個更優的做法,將 數組分三塊 思想來實現快速排序

我們先選一個基準元素key,將數組分成三塊。左區間小于key,中間區間等于key,右區間大于key。中間區間已經在排序后的最終位置,所以只用去去左區間排序,右區間排序。重復上述過程,整體就有序了。

數組如何分三塊和顏色分類一模一樣。定義一個i 指針 掃描數組,left指針 指向左區間小于key的最右側, right指針 指向右區間大于key的最左側。然后分情況討論就好了。

在這里插入圖片描述

即使數組全部都是重復元素,我們經過一次數組劃分,整個數組都是中間區間,左右區間不存在,也不用在遞歸下去了,直接結束。時間復雜度O(n)

在這里插入圖片描述

優化:用隨機的方式選擇基準元素
之前常用的三數取中,還是不夠隨機。要想時間復雜度逼近O(nlogn)就要用隨機的方式選擇基準元素。

隨機選擇就是讓數組中每個元素被選擇的概率相同,然后返回被選擇的元素。

使用產生隨機數的函數 rand(),讓生成的隨機數%這個區間的長度,然后加上left,這是在這個區間內的隨機數的下標,然后返回對應下標的元素。
在這里插入圖片描述

class Solution {
public:vector<int> sortArray(vector<int>& nums) {srand((unsigned int)time(nullptr));QuickSort(nums,0,nums.size()-1);return nums;}void QuickSort(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;elseswap(nums[--right], nums[i]);}QuickSort(nums, l , left);QuickSort(nums, right, r);}int getRandom(vector<int>& nums, int left, int right){return nums[rand() % (right - left + 1) + left];}

4.數組中的第K個最大元素

題目鏈接:215. 數組中的第K個最大元素

題目分析:

在這里插入圖片描述
給定整數數組 nums 和整數 k,請返回數組中第 k 個最大的元素。其實就是一個TopK問題。

TopK問題四種問法:

第 k 個最大的元素
第 k 個最小的元素
前 k 個最大的元素
前 k 個最小的元素

可以使用堆排序, 時間復雜度O(nlogn)

還有一種就是快速選擇算法,快速選擇算法是基于快排實現的。時間復雜度O(n)。

算法原理:

一定要把數組分三塊+隨機選擇基準元素掌握,才能懂這道題。

核心操作還是選擇一個基準元素key,將數組分成< key , = key ,> key 三塊區域。在這道題中我們是要找到第K大元素,這個第K大元素有可能落在三個區域中的任何一個區域。如果有一種方法能夠確定第K大元素能夠落在那個區域,那另外兩個區域就不用考慮了。僅需在確定的區域里面遞歸找。所以說它是比快排更快的一種算法。
在這里插入圖片描述

接下來重點就是如何確定第K大元素落在左邊區域、中間區域、還是右邊區域。
此時我們先統計出每個區域中元素的個數,假設左邊區域元素個數為 a,中間區域元素個數為 b,右邊區域元素個數為 c。
在這里插入圖片描述

此時就分三種情況討論:

因為求第K大,所以可以先考慮右邊區域,因為右邊區域都是大元素聚集地,第K大最有可能在右邊區域。

  1. 第一種情況:如果第K大是落在右邊區域,此時 c >= K,因為c表示大元素有多少個,而K表示找第K大的元素。如果 c >= K ,那第K大一定是落在右邊區域。此時我們僅需到[right,r],找第K大
  2. 第二種情況:如果到了第二情況那第一種情況一定是不成立的。如果第K大是落在中間區域,此時 b + c >= K,又因為第一種情況不滿足,所有第K大一定是落在中間區域。此時就找到了也不用遞歸了。直接返回就可以
  3. 第三種情況:第一、第二種情況全部不成立,第K大一定落在左邊區域,去**[l,left]找**,但是此時并不是去找第K大了,本來是想在整個區間找第K大,但是第K大元素確定不在中間區域和右邊區域,中間和右邊區域都要被拋棄,此時去找左邊區間找的是第 K - b - c 大的元素

在這里插入圖片描述

class Solution {
public:int findKthLargest(vector<int>& nums, int k) {srand((unsigned int)time(nullptr));return QuickSort(nums,0,nums.size()-1,k);}int QuickSort(vector<int>& nums, int l, int r, int k){//不用考慮區間不存在的情況,因為下面的判斷K落在那個區域,只要滿足進入的一定是有的if(l == r) return nums[l];// 1.隨機選擇基準元素int key = GetRandom(nums, l, r);// 2.根據基準元素將數組分三塊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]);}//3.計算每個區間都有多少元素,然后選擇區間遞歸int b = right - 1 - left , c = r - right + 1; if(c >= k) return QuickSort(nums, right, r ,k);else if(b + c >= k) return key;else return QuickSort(nums, l, left, k - b - c);}int GetRandom(vector<int>& nums, int left, int right){return nums[rand() % (right - left + 1) + left];}// int findKthLargest(vector<int>& nums, int k) {//     //前k個建小堆//     priority_queue<int,vector<int>,greater<int>> pq(nums.begin(),nums.begin()+k);//     //N-k與堆頂元素比較,大于堆頂就入堆,再次調整成小堆//     for(size_t i=k;i<nums.size();++i)//     {//         if(nums[i]>pq.top())//         {//             pq.pop();//             pq.push(nums[i]);//         }//     }//     return pq.top();// }
};

5.庫存管理 III

題目鏈接:LCR 159. 庫存管理 III

題目分析:

在這里插入圖片描述

實際上這也是一個TopK問題,讓找前K個最小元素。注意返回結果并不考慮順序問題。

算法原理:

解法一:排序 ,時間復雜度O(nlogn)
解法二:堆 ,時間復雜度O(nlogk)
解法三:快速選擇算法,時間復雜度O(n)

數組分三塊+隨機選擇基準元素。
選擇一個基準元素key,將數組分成< key , = key ,> key 三塊區域。找前K個最小的元素,落在三個區域中任何一個。統計除每個區域元素個數,然后選擇去那個區域找。

分三種情況:
因為前K下最小元素最有可能出現在左邊區域,因此先判斷左邊區域

  1. a >= K ,去[l,left] 找前K個最小元素
  2. b + a >= K ,直接返回
  3. 1、2都不成立,去[right,r] 找 K - a - b 個最小元素

在這里插入圖片描述

class Solution {
public:vector<int> inventoryManagement(vector<int>& nums, int k) {srand((unsigned int)time(nullptr));QuickSort(nums, 0, nums.size() - 1, k);return {nums.begin(),nums.begin() + k};}void QuickSort(vector<int>& nums, int l, int r, int k){if(l >= r) return;// 1. 隨機選擇基準元素int key = GetRandom(nums, l, r);// 2. 數組分三塊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]);}// 3. 分情況討論int a = left - l + 1, b = right - left - 1;if(a >= k) QuickSort(nums, l, left, k);else if(a + b >= k) return;else QuickSort(nums, right, r, k - a - b);}int GetRandom(vector<int>& nums, int left, int right){return nums[rand() % (right - left + 1) + left];}
};

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

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

相關文章

搜狐新聞HarmonyOS版本 push 推送開發

背景 搜狐新聞作為HarmonyOS的合作伙伴&#xff0c;于2023年12月成功上架鴻蒙單框架應用市場&#xff0c;成為首批鴻蒙應用矩陣的一員。 新聞類推送作為應用的重要組成部分&#xff0c;在二期規劃中&#xff0c;我們將推送功能列為核心功能模塊。本文將推送集成過程中的步驟和…

JAVA婦產科專科電子病歷系統源碼,前端框架:Vue,ElementUI

JAVA婦產科專科電子病歷系統源碼&#xff0c;前端框架&#xff1a;Vue&#xff0c;ElementUI孕產婦健康管理信息管理系統是一種將孕產婦健康管理信息進行集中管理和存儲的系統。通過建立該系統&#xff0c;有助于提高孕產婦健康管理的效率和質量&#xff0c;減少醫療事故發生的…

新華三通用大模型算力底座方案:為AI時代注入強大動力

在人工智能技術日新月異的今天&#xff0c;大模型作為推動AI進步的重要驅動力&#xff0c;是百行百業不斷追逐的熱點。大模型以其強大的泛化能力、卓越的模型效果和廣泛的應用場景&#xff0c;正改變著人工智能的未來。作為國內領先的ICT解決方案提供商&#xff0c;新華三集團憑…

Linux kfence使用與實現原理

0 背景 為了更好的檢測linux kernel中內存out-of-bounds、mem-corruption、use-after-free、invaild-free等問題&#xff0c;調研了kfence功能&#xff08;該功能在linux kernel 5.12引入&#xff09;&#xff0c;幫助研發更好的分析與定位這類內存錯誤的問題。 一、kfence介…

【ES】--Elasticsearch的Nested類型介紹

目錄 一、問題現象二、普通數組類型1、為什么普通數組類型匹配不準?三、nested類型四、nested類型查詢操作1、只根據nested對象內部數組條件查詢2、只根據nested對象外部條件查詢3、根據nested對象內部及外部條件查詢4、向nested對象數組追加新數據5、刪除nested對象數組某一個…

2025中國淄博化工展|淄博化工技術展|淄博化工裝備展

CTEE2025第九屆中國&#xff08;淄博&#xff09;化工技術裝備展覽會 時間&#xff1a;2025年5月16-18日 地點&#xff1a;山東淄博國際會展中心 主辦單位:山東省機械工業科學技術協會 青島藍博國際會展有限公司 眾所周知&#xff0c;山東省是我國化工大省。2023年上半年&am…

Go GMP:并發編程實踐

&#x1f49d;&#x1f49d;&#x1f49d;歡迎蒞臨我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦:「stormsha的主頁」…

0053__CancelIO的作用:防止為發送的數據丟失

CancelIO的作用&#xff1a;防止為發送的數據丟失-CSDN博客 cancelIoEx 函數 (ioapiset.h) - Win32 apps | Microsoft Learn

【Java】Logbook優化接口調用日志輸出,優雅!

logbook 簡介 很多人可能沒有接觸過 logbook&#xff0c;但它的確是一個很好用的日志框架。引用官網的介紹 Logbook 是一個可擴展的 Java 庫&#xff0c;可以為不同的客戶端和服務器端技術啟用完整的請求和響應日志記錄。它通過以下方式滿足了特殊需求&#xff1a; 允許 Web 應…

計算機網絡期末復習4(武夷學院版)

第四章 網絡層 1、網際協議IP以及配套協議&#xff08;書P119&#xff09; 網際協議&#xff08;IP&#xff09;&#xff1a;IP協議是網絡層的核心協議&#xff0c;負責數據包的編址和路由。它定義了數據包的格式和處理規則。 配套協議&#xff1a;地址解析協議ARP&#xf…

【工具】VS Code使用global插件實現代碼跳轉

&#x1f41a;作者簡介&#xff1a;花神廟碼農&#xff08;專注于Linux、WLAN、TCP/IP、Python等技術方向&#xff09;&#x1f433;博客主頁&#xff1a;花神廟碼農 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列專欄&#xff1a;善假于物&#…

粵港聯動,北斗高質量國際化發展的重要機遇

今年是香港回歸27周年&#xff0c;也是《粵港澳大灣區發展規劃綱要》公布5周年&#xff0c;5年來各項政策、平臺不斷為粵港聯動增添新動能。“十四五”時期的粵港澳大灣區&#xff0c;被國家賦予了更重大的使命&#xff0c;國家“十四五”《規劃綱要》提出&#xff0c;以京津冀…

時序約束(二): input delay約束和output delay約束

一、input delay約束 在千兆以太網數據收發項目中&#xff0c;RGMII的數據輸入方式為DDR&#xff0c;源同步輸入方式&#xff0c;可以用之前提到的分析模型進行約束。 在時序約束原理中我們提到&#xff0c;input delay約束的就是發射沿lunch到數據有效的延時&#xff0c;根據…

Vue 3中 <script setup> 與生命周期鉤子函數的詳細解析

Vue 3中 <script setup> 與生命周期鉤子函數的詳細解析 Vue 3 引入了 <script setup> 語法糖&#xff0c;這是一種簡化和集成組件邏輯的新方式。盡管 <script setup> 簡化了組件的編寫&#xff0c;但仍然可以利用 Vue 提供的生命周期鉤子函數來管理組件的生…

【光伏開發】光伏項目開發流程

光伏項目作為可再生能源領域的重要組成部分&#xff0c;其開發過程涉及多個環節&#xff0c;從項目初期的可行性研究到后期的運營維護&#xff0c;每一步都至關重要。本文將按照項目確認、前期階段、中期階段、后期階段的順序&#xff0c;詳細介紹光伏項目的開發流程。 一、項…

Rust 基礎教程

Rust 編程語言教程 Rust是一門注重安全、并發和性能的系統編程語言。本文將從Rust的基本語法、常用功能到高級特性&#xff0c;詳細介紹Rust的使用方法。 目錄 簡介環境配置基礎語法 變量和常量數據類型函數控制流 所有權和借用 所有權借用 結構體和枚舉 結構體枚舉 模塊和包…

一文搞懂 java 線程池:基礎知識

你好&#xff0c;我是 shengjk1&#xff0c;多年大廠經驗&#xff0c;努力構建 通俗易懂的、好玩的編程語言教程。 歡迎關注&#xff01;你會有如下收益&#xff1a; 了解大廠經驗擁有和大廠相匹配的技術等 希望看什么&#xff0c;評論或者私信告訴我&#xff01; 文章目錄 …

Linux:網絡基礎1

文章目錄 前言1. 協議1.1 為什么要有協議&#xff1f;1.2 什么是協議&#xff1f; 2. 網絡2.1 網絡通信的問題2.2 網絡的解決方案——網絡的層狀結構2.3 網絡和系統的關系2.4 網絡傳輸基本流程2.5 簡單理解IP地址2.6 跨網絡傳輸 總結 前言 在早期的計算機發展中&#xff0c;一開…

【云計算】阿里云、騰訊云、華為云平臺數據庫對比

目錄 一、云數據庫關鍵信息調研對比 二、詳細功能 1、阿里云RDS 2、騰訊云RDS 3、華為云RDS 一、云數據庫關鍵信息調研對比 云平臺支持數據庫部署對比支持功能備注阿里云 Mysql、Postgresql等 特有數據庫&#xff1a;PolarDB&#xff0c;適配mysql 基礎-單節點賬號管…

實現漸變字體的方案

需要注意&#xff0c;這個切圖是把一整塊&#xff0c;都切出來做的。所以需要用span&#xff0c;不能是div 還有描邊的話&#xff0c;scale會有邊距縮放的問題&#xff0c;描邊就用font weight 來實現 style{{ background: "var(--Linear, linear-gradient(96deg, #fff…