優選算法的妙思之流:分治——歸并專題

專欄:算法的魔法世界

個人主頁:手握風云

目錄

一、歸并排序

二、例題講解

2.1.?排序數組

2.2.?交易逆序對的總數

2.3.?計算右側小于當前元素的個數

2.4.?翻轉對


一、歸并排序

? ? ? ? 歸并排序也是采用了分治的思想,將數組劃分為多個長度為1的子數組進行排序,再把多個子數組合并為最終的數組。

二、例題講解

2.1.?排序數組

? ? ? ? 我們再來回顧一下歸并排序的大體思路:以數組的中間值將數組劃分為兩個子數組,以此再繼續劃分,直至將數組劃分為里面只有一個元素,就可以向上返回。當左邊排完后,再去排右邊,然后再將兩個子數組進行合并,直到合并為原來的數組長度。

? ? ? ? 完整代碼實現:

class Solution {public int[] sortArray(int[] nums) {MergeSort(nums, 0, nums.length - 1);return nums;}private void MergeSort(int[] nums, int left, int right) {if (left >= right)return;//根據中間點劃分左右子數組int mid = (left + right) / 2;MergeSort(nums, left, mid);MergeSort(nums, mid + 1, right);//合并兩個有序數組int[] tmp = new int[right - left + 1];int p1 = left, p2 = mid + 1, i = 0;while (p1 <= mid && p2 <= right) {tmp[i++] = nums[p1] <= nums[p2] ? nums[p1++] : nums[p2++];}while (p1 <= mid) {tmp[i++] = nums[p1++];}while (p2 <= right) {tmp[i++] = nums[p2++];}//還原for (int j = left; j <= right; j++) {nums[j] = tmp[j - left];}}
}

2.2.?交易逆序對的總數

? ? ? ? 暴力解法,利用兩層for循環,先固定其中一個數,再讓其他數與這個數進行比較,如果小,就讓計數器++。

class Solution {public int reversePairs(int[] record) {int count = 0;for(int i = 0;i < record.length;i ++){for(int j = i + 1;j < record.length;j++){if(record[i] > record[j]) count++;}}return count;}
}

? ? ? ? 第二種解法,將數組劃分為兩塊,先找出左區域的逆序對,再找出右區域的逆序對,最后再一左一右隨機挑一個數找出逆序對。但這樣的解法本質上還是一個暴力枚舉。我們接著來及逆行優化,我們先找完左區域的逆序對,然后對左區域進行排序;找完右區域的逆序對,然后對右區域進行排序。我們在左右區域里面隨機挑數的時候是不會影響逆序對的數量。

? ? ? ? 當我們在左右區域里面分別尋找逆序對時,如果數組長度較大,那么我們還可以再接著劃分,繼續按照上面的思路來找出逆序對的總數,這個部分就可以在遞歸中完成,所以我們在處理一左一右時也可以排個序。

? ? ? ? 接下來是查找子數組里面逆序對的數目,如下圖所示,p1左側是子數組中較小的元素,p2左側也是子數組中較小的元素。我們先固定p2這個數,接下來就是在[left,p1]這段區間里面尋找比p2大的元素。如果p1所指的元素比p2所指的元素小或者等于,那么就讓p1++;如果p1所指的元素比p2所指的元素大,那么p1后面的元素就都是比p2大,就可以快速的統計出數目,然后再讓p2++。

? ? ? ? 完整代碼實現:

class Solution {int[] tmp;public int reversePairs(int[] record) {int n = record.length;tmp = new int[n];return MergeSort(record, 0, n - 1);}private int MergeSort(int[] nums, int left, int right) {if (left >= right) return 0;int ret = 0;//中點元素int mid = (left + right) / 2;//左半部分的數目+右半部分的數目ret += MergeSort(nums, left, mid);ret += MergeSort(nums, mid + 1, right);//一左一右的數目int p1 = left, p2 = mid + 1, i = 0;while (p1 <= mid && p2 <= right) {if (nums[p1] <= nums[p2]) {tmp[i++] = nums[p1++];} else {ret += mid - p1 + 1;tmp[i++] = nums[p2++];}}while (p1 <= mid) tmp[i++] = nums[p1++];while (p2 <= right) tmp[i++] = nums[p2++];for (int j = left; j <= right; j++) {nums[j] = tmp[j - left];}return ret;}
}

2.3.?計算右側小于當前元素的個數

? ? ? ? 這道題要我們求出一個數組元素右邊有多少個數比它小,我們可以仿照上一題的思路,只不過這道題要把數組降序排列。

? ? ? ? 把數組分成兩部分,先找出左區域里面比自身小的個數,再找出右區域比自身小的個數,再一左一右去尋找個數。因為數組是降序排列的,所以p1、p2左邊都是相對較大的元素。如果nums[p1]<=nums[p2],則p2++;如果nums[p1]>nums[p2],因為我們最終是要返回一個順序表,所以我們要用該元素對應的位置來統計結果(right-p2+1)。但是經過歸并排序后,數組下標已經亂了,下一步就是要求數組元素對應的原始下標。

? ? ? ? 我們可以使用哈希思想來解決數組下標與元素的動態綁定,如果數組里面有重復元素,使用哈希表就很難。當對數組進行排序時,數組下標也要隨著變換。

? ? ? ? 完整代碼實現:

class Solution {int[] ret;int[] index;//標記原始下標int[] tmpIndex;//用于合并時的數組int[] tmpNum;public List<Integer> countSmaller(int[] nums) {int n = nums.length;ret = new int[n];index = new int[n];tmpIndex = new int[n];tmpNum = new int[n];//初始化index數組for (int i = 0; i < n; i++) {index[i] = i;}Mergesort(nums,0,n - 1);List<Integer> res = new ArrayList<>();for(int x : ret)res.add(x);return res;}private void Mergesort(int[] nums, int left, int right) {if(left >= right) return;//根據中間元素劃分區間int mid = (left + right) / 2;//處理左右區間Mergesort(nums,left,mid);Mergesort(nums,mid + 1,right);//合并int p1 = left,p2 = mid + 1,i = 0;while(p1 <= mid && p2 <= right){//降序if(nums[p1] <= nums[p2]){tmpNum[i] = nums[p2];tmpIndex[i++] = index[p2++];} else {ret[index[p1]] += right - p2 + 1;tmpNum[i] = nums[p1];tmpIndex[i++] = index[p1++];}}//處理剩余的排序while(p1 <= mid){tmpNum[i] = nums[p1];tmpIndex[i++] = index[p1++];}while(p2 <= right){tmpNum[i] = nums[p2];tmpIndex[i++] = index[p2++];}for (int j = left; j <= right; j++) {nums[j] = tmpNum[j - left];index[j] = tmpIndex[j - left];}}
}

2.4.?翻轉對

? ? ? ? 我們依然可以按照逆序對那道題,將數組劃分為兩塊,求出左區域、右區域以及一左一右的翻轉對的和。但是逆序對那道題是1:1進行比較的,而這道題需要前面的元素大于后面元素的2倍,就不能按照歸并排序的思路來解決。所以我們先計算翻轉對的數目,我們可以有兩種思路:1.計算當前元素后面,有多少元素的2倍比該數小;2.計算當前元素后面,有多少元素的一半比該數大。

? ? ? ? 我們先來講下方法1(按照降序排列,沒有邊界):利用數組有序的特性計算翻轉對。如果nums[p1]<=nums[p2],p2向后移動,直到nums[p1]>nums[p2]時,就可以統計出數目來。如果我們讓p2回退,那么時間復雜度就會達到n^{2}級別。因為數組是降序的,當p1右移時,所指向的元素也會變小,沒必要再讓p2回退,就可以構成同向雙指針了。方法2也同理,如果nums[p1]>= nums[p2],就讓p1向右移動,直到nums[p1]<nums[p2],再讓p2向右移動,p1同樣不用回退。

? ? ? ? 完整代碼實現:

class Solution {int[] tmp;public int reversePairs(int[] nums) {int n = nums.length;tmp = new int[n];return Mergesort(nums,0,n - 1);}private int Mergesort(int[] nums, int left, int right) {if(left >= right) return 0;int ret = 0;int mid = (left + right) / 2;ret += Mergesort(nums,left,mid);ret += Mergesort(nums,mid + 1,right);int p1 = left,p2 = mid + 1,i = left;while(p1 <= mid) {while (p2 <= right && nums[p2] >= nums[p1] / 2.0) p2++;if(p2 > right)break;ret += right - p2 + 1;p1++;}p1 = left;p2 = mid + 1;while(p1 <= mid && p2 <= right){tmp[i++] = (nums[p1] <= nums[p2]) ? nums[p2++] : nums[p1++];}while(p1 <= mid) tmp[i++] = nums[p1++];while(p2 <= right) tmp[i++] = nums[p2++];for (int j = left; j <= right; j++) {nums[j] = tmp[j];}return ret;}
}

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

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

相關文章

C語言查漏補缺:基礎篇

1.原理 C語言是一門編譯型計算機語言&#xff0c;要編寫C代碼&#xff0c;C源代碼文本文件本身無法直接執行&#xff0c;必須通過編譯器翻譯和鏈接器的鏈接&#xff0c;生成二進制的可執行文件&#xff0c;然后才能執行。這里的二進制的可執行文件就是我們最終要形成的可執行程…

TPS入門DAY02 服務器篇

1.創建空白插件 2.導入在線子系統以及在線steam子系統庫 MultiplayerSessions.uplugin MultiplayerSessions.Build.cs 3.創建游戲實例以及初始化會話創建流程 創建會話需要的函數&#xff0c;委托&#xff0c;委托綁定的回調&#xff0c;在線子系統接口綁定某一個委托的控制其…

產品經理課程

原型工具 一、土耳其機器人 這個說法來源于 1770 年出現的一個騙局&#xff0c;一個叫沃爾夫岡馮肯佩倫&#xff08;Wolfgang von Kempelen&#xff09;的人為了取悅奧地利女皇瑪麗婭特蕾莎&#xff08;Maria Theresia&#xff09;&#xff0c;“制造”了一個會下國際象棋的機…

nginx中的limit_req 和 limit_conn

在 Nginx 中&#xff0c;limit_req 和 limit_conn 是兩個用于限制客戶端請求的指令&#xff0c;它們分別用于限制請求速率和并發連接數。 limit_req limit_req 用于限制請求速率&#xff0c;防止客戶端發送過多請求影響服務器性能。它通過 limit_req_zone 指令定義一個共享內存…

基于winform的串口調試助手

目錄 一、串口助手界面設計 1.1 串口配置 1.2 接收配置 1.3 發送配置 1.4 接收窗口和發送窗口 1.5 狀態顯示窗口 1.6 串口通訊控件 二、程序編寫 2.1 端口號自動識別并顯示在端口號下拉框 功能說明&#xff1a; 2.2 波特率下拉框顯示 2.3 數據位下拉框顯示 2.4 校…

Docker基礎2

如需轉載&#xff0c;標記出處 本次我們將下載一個 Docker 鏡像&#xff0c;從鏡像中啟動容器 上一章&#xff0c;安裝 Docker 時&#xff0c;獲得兩個主要組件&#xff1a; Docker 客戶端 Docker 守護進程&#xff08;有時稱為“服務器”或“引擎”&#xff09; 守護進程實…

Rocketmq2

一、生產者端防丟失 1. 發送方式選擇 同步發送&#xff1a;使用 send() 方法&#xff0c;等待 Broker 確認響應&#xff08;SendResult&#xff09;&#xff0c;確保消息已成功發送。異步發送&#xff1a;使用 sendAsync() 方法并設置回調函數&#xff0c;處理發送成功 / 失敗…

RabbitMQ詳解,RabbitMQ是什么?架構是怎樣的?

目錄 一,RabbitMQ是什么? 二,RabbitMQ架構 2.1 首先我們來看下RabbitMQ里面的心概念Queue是什么? 2.2 交換器Exchange 2.3 RabbitMQ是什么? 2.4 重點看下優先級隊列是什么? 三,RabbitMQ集群 3.1 普通集群模式 3.2 鏡像隊列集群 一,RabbitMQ是什么? 假設我們程序…

【一步步開發AI運動APP】六、運動計時計數能調用

之前我們為您分享了【一步步開發AI運動小程序】開發系列博文&#xff0c;通過該系列博文&#xff0c;很多開發者開發出了很多精美的AI健身、線上運動賽事、AI學生體測、美體、康復鍛煉等應用場景的AI運動小程序&#xff1b;為了幫助開發者繼續深耕AI運動領域市場&#xff0c;今…

MySQL——DQL的多表查詢

一、交叉連接 標準語法&#xff1a;select * from 表1 cross join 表2 where 表1.公共列 表2.公共列; 簡單語法&#xff1a;select * from 表1 , 表2 where 表1.公共列 表2.公共列; 公共列&#xff1a;兩張表具有相同含義的列&#xff0c;不是列名一樣。 …

【Linux內核】如何更加優雅閱讀Linux內核源碼(vscode)

1. 前言 因為已經習慣在Ubuntu下進行嵌入式工作開發&#xff0c;但Linux源碼在Source Insight下進行閱讀&#xff0c;一直很苦惱Linux/Windows來回切換的開發方式&#xff0c;當前發現可以通過 vscode clangd(擴展組件) 方式進行更好的內核源碼閱讀。 2. 環境 操作系統&…

21.OpenCV獲取圖像輪廓信息

OpenCV獲取圖像輪廓信息 在計算機視覺領域&#xff0c;識別和分析圖像中的對象形狀是一項基本任務。OpenCV 庫提供了一個強大的工具——輪廓檢測&#xff08;Contour Detection&#xff09;&#xff0c;它能夠幫助我們精確地定位對象的邊界。這篇博文將帶你入門 OpenCV 的輪廓…

LETTERS(DFS)

【題目描述】 給出一個rowcolrowcol的大寫字母矩陣&#xff0c;一開始的位置為左上角&#xff0c;你可以向上下左右四個方向移動&#xff0c;并且不能移向曾經經過的字母。問最多可以經過幾個字母。 【輸入】 第一行&#xff0c;輸入字母矩陣行數RR和列數SS&#xff0c;1≤R,S≤…

Day2-2:前端項目uniapp壁紙實戰

再在wallpaper新建一個目錄components 在components下新建組件common-title 記得點擊創建同名目錄 在index加 <view class"select"><common-title></common-title></view> 圖片換了下&#xff0c;原來的有點丑&#xff0c;圖片可按自己喜歡…

其他 vector 操作詳解(四十)

介紹 除去向 vector 添加元素&#xff08;如 push_back&#xff09;之外&#xff0c;vector 還提供了許多其他操作&#xff0c;這些操作大多與 string 的操作類似。通過掌握這些操作&#xff0c;我們可以方便地查詢、修改和比較 vector 中的元素&#xff0c;從而構建靈活、高效…

【Leetcode 每日一題】368. 最大整除子集

問題背景 給你一個由 無重復 正整數組成的集合 n u m s nums nums&#xff0c;請你找出并返回其中最大的整除子集 a n s w e r answer answer&#xff0c;子集中每一元素對 ( a n s w e r [ i ] , a n s w e r [ j ] ) (answer[i], answer[j]) (answer[i],answer[j]) 都應當…

python基礎-13-處理excel電子表格

文章目錄 【README】【13】處理Excel電子表格【13.1】Excel文檔【13.2】安裝openpyxl模塊【13.3】讀取Excel文檔【13.3.1】使用openpyxl模塊打開excel文檔【13.3.2】從工作簿取得工作表【13.3.3】從工作表sheet獲取單元格cell【13.3.5】從表中獲取行和列【13.3.6】工作簿、工作…

ABS函數c++

簡介&#xff1a; abs 函數用于計算一個數的絕對值&#xff0c;在 C 中它繼承自 C 語言的標準庫&#xff0c;其歷史可以追溯到早期的 C 語言發展歷程&#xff0c;以下是詳細介紹&#xff1a; 早期編程語言的需求 在計算機編程的早期階段&#xff0c;處理數學運算就是一項基本…

閉環SOTA!北航DiffAD:基于擴散模型實現端到端自動駕駛「多任務閉環統一」

端到端自動駕駛目前是有望實現完全自動駕駛的一條有前景的途徑。然而&#xff0c;現有的端到端自動駕駛系統通常采用主干網絡與多任務頭結合的方式&#xff0c;但是它們存在任務協調和系統復雜度高的問題。為此&#xff0c;本文提出了DiffAD&#xff0c;它統一了各種駕駛目標并…

整車CAN網絡和CANoe

車載網絡中主要包含有Can網絡,Lin網絡,FlexRay,Most,以太網。 500kbps:500波特率,表示的數據傳輸的速度。表示的是最大的網速傳輸速度。也就是每秒 500kb BodyCan車身Can InfoCan娛樂信息Can 車身CAN主要連接的是ESB電動安全帶 ADB自適應遠光燈等 PTCan動力Can 底盤Can