兩個數組結果相減_學點算法(三)——數組歸并排序

今天來學習歸并排序算法。

17349d377b446fbd453b26a9a8178446.png

分而治之

歸并算法的核心思想是分而治之,就是將大問題轉化為小問題,在解決小問題的基礎上,再去解決大問題。將這句話套用到排序中,就是將一個大的待排序區間分為小的待排序區間,對小的排序區間進行排序,然后再去解決大區間的排序,而對小區間進行排序的時候可以繼續使用該方法,將小的待排序區間分為小小的待排序區間... ...依次往復。最終將排序區間分到只有一個元素,這個時候,因為一個元素已經就是排好序的,無需繼續切分了,然后我們再依照此結果去解決大區間的排序。

假設我們現在有[53, 89, 32, 45, 67, 2, 32, 89, 65, 54]這么一個數組,我們要對它進行歸并排序(從小到大排序,此文中均為從小到大排序),整體的過程如下圖所示:

0a2591f2f9ad17219b06fd8f1cb29240.png

歸并排序算法完整過程

整個算法分為兩大階段,分割階段歸并階段

分割階段

1. [53, 89, 32, 45, 67, 2, 32, 89, 65, 54]分為[53, 89, 32, 45, 67]和[2, 32, 89, 65, 54]。

2. [53, 89, 32, 45, 67]分為[53, 89]和[32, 45, 67], [2, 32, 89, 65, 54]分為[2, 32]和[89, 65, 54]。

4. ... ...

5. 數組分割完畢,所有小數組依次為[53],[89] ,[32] ,[45],[67],[2],[32],[89],[65],[54]。

歸并階段

1. [53],[89]歸并為[53, 89],[32] ,[45]歸并為[32, 45],[2]和[32]歸并為[2, 32],[65]和[54]歸并為[54, 65](這一步中,[67]和[89]沒有歸并,因為在最后一步分割過程中,它們被單獨分開了)。

2. [32, 45]和[67]歸并為[32, 45, 67],[89]和[54, 65]歸并為[54, 65, 89]。

3. [53, 89]和[32, 45, 67]歸并為[32, 45, 53, 67, 89],[2, 32]和[54, 65, 89]歸并為[2, 32, 54, 65, 89]。

4. [32, 45, 53, 67, 89]和[2, 32, 54, 65, 89]歸并為[2, 32, 32, 45, 53, 54, 65, 67, 89, 89](其中兩個32和兩個89,在歸并的過程中保留它們的原始順序)。

整個分而治之的過程我們已經清楚了,可還有一個問題沒有解決,就是具體應該怎么去歸并呢,即如何將兩個排序子數組(或子區間)合并為大的排序好的數組(或區間)呢?

我們可以先舉個簡單的例子:現在有[2]和[1]兩個數組,我們如何把它們合并為[1, 2]整個數組呢?很簡單,我們首先會把這兩個元素取出來,對比一下,取出2和1,我們一對比,發現1小于2, 所以我們在結果數組中先放入1,然后再放入2。可以發現,我們就是將兩個子數組中的元素取出來比較了一下,哪個小就把哪個先放入結果數組中。

從上面的例子中我們可以得到大概的思路就是,針對兩個有序的子數組(或子區間),我們可以從頭依次取兩個子數組(或子區間)的首元素(因為從小到大排序后首元素肯定最小),然后作對比,把小的元素放入結果數組中,并且這個元素在下次選取的時候剔除,下一個元素也應用同樣的方法得到,放入結果數組中,依次進行,直到兩個數組的元素都取完為止,如果發現其中一個子數組(或子區間)率先取完,就直接將另外一個子數組(或子區間)中剩下的元素全部放入結果數組中即可。具體步驟描述如下:

1. 判斷兩個子數組(或子區間)是否含有剩余元素,如果都有剩余元素,進入第2步;如果只有一個有剩余元素,進入第5步;如果沒有,則退出。

2. 取出左子數組(或左子區間)的首個元素和右子數組(或右子區間)的首個元素。

3. 兩個元素對比,將小的元素放入結果數組,并且從對應數組中剔除該元素。

4. 回到第1步(上一步選中的元素已被剔除)。

5. 將剩余元素直接全部放入結果數組中,退出(因為元素全部移動完畢)。

其中,剔除這一步在代碼實現中可看成索引的移動。

上述這個過程我們取[53, 89]和[32, 45, 67]這兩個子數組的合并來描述一下,如圖所示:

db6e5363a2ecdcd1e6d3bdf1064b6aa7.png

歸并

1. 取出左子數組中的首個元素53和右子數組中的首個元素32,兩個作對比,發現32 < 53,所以我們將32放入結果數組:

07125af4005bed9a00fc01e7fef2d940.png

2. 取出左子數組中的首個元素53和右子數組中的首個元素45,兩個作對比,發現45 < 53,所以我們將45放入結果數組:

d578d1e3c7860efd12245e39f75958b4.png

3. 取出左子數組中的首個元素53和右子數組中的首個元素67,兩個作對比,發現53 < 67,所以我們將53放入結果數組:

4702f5fe45e96658536c6c8aca80e517.png

4. 取出左子數組中的首個元素89和右子數組中的首個元素67,兩個作對比,發現67 < 89,所以我們將67放入結果數組:

f159ef56201fd2f7db99b542f82b4163.png

5. 此時我們發現只有左子數組存在元素,所以直接將左子數組的剩下所有元素,此時只有89放入結果數組:

2f81ad7ee06b10875fc6e289158b3106.png

6. 至此,所有元素移動完畢,退出。

通過以上分析,我們可以知道整個歸并排序算法總體上分為一個整體的大邏輯(分而治之)和一個局部的小邏輯(歸并),在大邏輯(分而治之,將整個數組切分,并在確認子數組有序后歸并)的基礎上,結合使用小邏輯(歸并,將兩個有序子數組歸并為一個大的有序數組)即可實現對整個數組的排序。

最終代碼實現如下:

/** * 數組的歸并排序算法 * * @param nums 數組 * @param lo 區間的lo索引(包含) * @param hi 區間的hi索引(不包含) */public static void mergeSort(int[] nums, int lo, int hi) {    // 數組為null則直接返回    if (nums == null) {        return;    }    // 索引檢查    if (lo < 0 || nums.length <= lo) {        throw new IllegalArgumentException("lo索引必須大于0并且小于數組長度,數組長度:" + nums.length);    }    if (hi < 0 || nums.length < hi) {        throw new IllegalArgumentException("hi索引必須大于0并且小于等于數組長度,數組長度:" + nums.length);    }    if (hi <= lo) {        // lo索引必須小于hi索引(等于也不行,因為區間是左閉右開,如果等于,區間內元素數量就為0了)        throw new IllegalArgumentException("lo索引必須小于hi索引");    }    if (lo + 1 >= hi) {        // 區間元素個數最多為1        // 無需排序        return;    }    int mid = (lo + hi) / 2;    // 對左子區間排序    mergeSort(nums, lo, mid);    // 對右子區間排序    mergeSort(nums, mid, hi);    // 對兩個排序好的子區間歸并,得到一個整體有序的區間    merge(nums, lo, mid, hi);}public static void merge(int[] nums, int lo, int mid, int hi) {    // 這里不用檢查索引,調用方已經決定了索引是有效的    // 結果區間和右子區間使用原有數組    // 左子區間使用臨時數組(因為結果區間可能會覆蓋左子區間的元素,所以需要開辟新數組保存)    int leftLen = mid - lo;    int[] left = new int[leftLen];    System.arraycopy(nums, lo, left, 0, leftLen);    // 左子區間索引    int leftIdx = 0;    // 右子區間索引    int rightIdx = mid;    // 結果區間索引    int resultIdx = lo;    while (true) {        if (leftIdx < leftLen && rightIdx < hi) {            // 兩個子區間都存在元素            // 取兩個子區間的有效首元素對比            if (left[leftIdx] <= nums[rightIdx]) {                // 左子區間首元素小于右子區間首元素                // 將左子區間首元素放到結果位置,同時更新索引位置                nums[resultIdx++] = left[leftIdx++];            } else {                // 右子區間首元素小于左子區間首元素                // 將右子區間首元素放到結果位置,同時更新索引位置                nums[resultIdx++] = nums[rightIdx++];            }        } else {            if (leftIdx < leftLen) {                // 左子區間還有剩余元素                // 直接將左區間所有元素一起移動到結果位置                System.arraycopy(left, leftIdx, nums, resultIdx, leftLen - leftIdx);            } else {                // 右子區間還有剩余元素                // 因為經過上一次判斷,左子區間和右子區間只會有一個存在剩余元素                // 直接將右區間所有元素一起移動到結果位置                System.arraycopy(nums, rightIdx, nums, resultIdx, hi - rightIdx);            }            // 全部元素移動完畢,退出            break;        }    }}

測試代碼如下:

List numList = IntStream.range(0, 10).boxed().collect(Collectors.toList());for (int i = 1; i <= 5; i++) {    System.out.println("================第" + i + "次================");    Collections.shuffle(numList);    int[] nums = new int[numList.size()];    for (int j = 0; j < nums.length; j++) {        nums[j] = numList.get(j);    }    System.out.println("排序前:" + Arrays.toString(nums));    mergeSort(nums, 0, numList.size());    System.out.println("排序后:" + Arrays.toString(nums));}

運行結果如下:

================第1次================排序前:[8, 4, 1, 6, 7, 0, 5, 9, 2, 3]排序后:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]================第2次================排序前:[2, 5, 6, 7, 9, 4, 3, 1, 0, 8]排序后:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]================第3次================排序前:[2, 0, 5, 6, 7, 3, 4, 9, 8, 1]排序后:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]================第4次================排序前:[4, 0, 3, 8, 1, 5, 9, 7, 2, 6]排序后:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]================第5次================排序前:[7, 9, 8, 2, 0, 5, 6, 3, 4, 1]排序后:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

測試代碼中5次隨機將數組打亂,然后運行我們的歸并排序算法,均得到有序結果,符合我們的預期。

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

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

相關文章

python實習生面試題_大數據分析實習生面試題庫

原標題&#xff1a;大數據分析實習生面試題庫大數據分析是一個有吸引力的領域&#xff0c;因為它不僅有利可圖&#xff0c;而且您有機會從事有趣的項目&#xff0c;而且您總是在學習新事物。如果您想從頭開始&#xff0c;請查看大數據分析實習生面試題庫以準備面試要點。大數據…

JavaScript編程語言 基礎 (1)

問題&#xff1a;什么是web前端前端&#xff1a;指界面&#xff0c;計算機&#xff08;PC&#xff09;軟件桌面的界面&#xff1b; 計算機端的瀏覽器界面&#xff1b; 移動端的軟件&#xff08;app&#xff09;界面&#xff1b; 移動端的瀏覽器界面。HtmlcssJavaScript 使用網頁…

shell結合expect寫的批量scp腳本工具

轉載鏈接&#xff1a;http://www.jb51.net/article/34005.htm expect用于自動化地執行linux環境下的命令行交互任務&#xff0c;例如scp、ssh之類需要用戶手動輸入密碼然后確認的任務。有了這個工具&#xff0c;定義在scp過程中可能遇到的情況&#xff0c;然后編寫相應的處理語…

ASP記數器

這兩天有好幾個老的ASP網站要改&#xff0c;其中有要求加記數器&#xff0c;為圖簡單&#xff0c;就用文本文件的形式存儲記數。以前用ifream的形式嵌入&#xff0c;不能很好的控制記數器顯示的風格&#xff0c;現在改進了一下&#xff0c;可以很好的與嵌入板塊風格結合了。把做…

php利用openssl實現RSA非對稱加密簽名

轉載鏈接&#xff1a;http://liuxufei.com/weblog/jishu/376.html 1. 先用php生成一對公鑰和私鑰 $res openssl_pkey_new(); openssl_pkey_export($res,$pri); $d openssl_pkey_get_details($res); $pub $d[key]; var_dump($pri,$pub); 2. 保存好自己的私鑰&#xff0c;把公…

[轉] DevExpress 第三方控件漢化的全部代碼和使用方法

DevExpress.XtraEditors.Controls 此控件包中包含的控件最多&#xff0c;包括文本框&#xff0c;下拉列表&#xff0c;按鈕&#xff0c;等等 DevExpress.XtraGrid 網格 DevExpress.XtraBars 菜單欄 和 工具欄 DevExpress.XtraNavBar 導航條 DevExpress.XtraPr…

QPM 性能監控組件總篇

QPM &#xff08;Quality Performance Monitor&#xff09; 是一個質量性能監控組件&#xff0c;可以很方便的查看當前 App 的性能和常用數據。目前主要運行在 Android 平臺上&#xff0c;通過集成 QPM 組件&#xff0c;可以在 App 中通過懸浮窗可視化相關實時數據。意在幫助廣…

福音!微信個人公眾號可以改名了!

微信個人公眾號可以改名了&#xff01;&#xff01;&#xff01;今年&#xff0c;我們學校從景德鎮陶瓷學院更名為景德鎮陶瓷大學&#xff0c;但苦于微信限制&#xff0c;很多微信公眾號無法更名。很多組織社團就放棄了原先的關注量&#xff0c;重新申請注冊賬號。當前我們的訂…

js list刪除指定元素_刪除js數組中的指定元素,有這兩步就夠了

js數組是js部分非常重要的知識&#xff0c;有時我們有這么個需求js數組刪除指定元素&#xff0c;先定義一個函數來獲取刪除指定元素索引值&#xff0c;然后用js數組刪除的方法&#xff0c;來刪除指定元素即可&#xff0c;就兩步不難&#xff0c;很簡單。1、JS的數組對象定義一個…

sudo 安裝 常見錯誤

運行環境Linux&#xff1a; 1、sudo&#xff1a;安裝 apt-get install sudo 2、sudo: must be setuid root錯誤解決方法. ls -l /usr/bin/sudo chown root:root /usr/bin/sudo chmod 4755 /usr/bin/sudo reboot 3、sudo&#xff1a;提示用戶無權限之類 在 /etc/…

慕課網高并發實戰(一)-并發與高并發基本概念

課程網址 并發&#xff1a; 同時擁有兩個或者多個線程&#xff0c;如果程序在單核處理器上運行&#xff0c;多個線程交替得換入或者換出內存&#xff0c;這些線程是同時“存在”的&#xff0c;每個線程都處于執行過程中的某個狀態&#xff0c;如果運行在多核處理器上&#xff…

2009最經典名句

一&#xff1a;我的優點是&#xff1a;我很帥&#xff1b;但是我的缺點是&#xff1a;我帥的不明顯. 二&#xff1a;談錢不傷感情&#xff0c;談感情最他媽傷錢。 三&#xff1a;我詛咒你一輩子買方便面沒有調料包。 四&#xff1a;會計說&#xff1a;“你晚點來領工資吧&#…

計算機協會丨讓技能得到提升,讓思維受到啟迪

“ 各位2016級新生&#xff0c;新的學期馬上就要開始了&#xff0c;學校的各個組織和社團你真的了解了嗎&#xff1f;在眼花繚亂的社團里如何找到自己真正喜歡的呢&#xff1f;或許看完計算機協會的納新微信你就都明白啦&#xff01;關鍵詞&#xff1a;計算機協會景德鎮陶瓷大學…

ondestroy什么時候調用_尾調用和尾遞歸

尾調用1. 定義尾調用是函數式編程中一個很重要的概念&#xff0c;當一個函數執行時的最后一個步驟是返回另一個函數的調用&#xff0c;這就叫做尾調用。注意這里函數的調用方式是無所謂的&#xff0c;以下方式均可&#xff1a;函數調用: func()方法調用: obj.method()call調用:…

查看/修改Linux時區和時間

轉載鏈接&#xff1a;http://blog.csdn.net/colincjl/article/details/6133036 查看/修改Linux時區和時間 一、時區 1. 查看當前時區 date -R 2. 修改設置時區 方法(1) tzselect 方法(2) 僅限于RedHat Linux 和 CentOS timeconfig 方法(3) 適用于Debian dpkg-reconfigure tzdat…

dhl:使用return RedirectToAction()和 return view()

一個Action&#xff1a; Code/// <summary> /// Friend好友的地 /// </summary> /// <returns></returns> public ActionResult FriendFarm(string pid) {BLL.DTOFarm farm new AppleGrange.BLL.DTOFarm(pid); …

【更名通知】將以個人名義繼續更新維護

這是我&#xff08;2013年任職計算機協會會長&#xff09;在2013年申請的公眾號。由于2016年學校陶院更名為陶大&#xff0c;在當時公眾號無法修改名稱。后來計協的的學弟學妹申請了新的公眾號"陶大計算機Association"&#xff0c;大家可以前往關注&#xff0c;所以該…

CentOS7.6 MySQL8環境搭建 配置遠程登錄 字符集UTF8 簡單密碼

一、環境準備 1、清理環境中系統自帶的MySQL &#xff08;1&#xff09;刪除系統自帶的MySQL或Mariadb yum remove mysql-libs &#xff08;2&#xff09;查詢系統中是否還有殘余的依賴包 rpm -qa | grep mariadb &#xff08;3&#xff09;刪除rpm依賴包 rpm -e --nodeps mar…

radio切換控制div顯示_JavaScript連載31圖片動態切換以及關閉圖片案例

一、圖標切換31.1點擊那兩個按鈕可以做到輪番顯示圖片二、關閉圖片案例31.2點擊右上角的叉&#xff0c;圖片會消失。三、源碼&#xff1a;D31_iconSwitch.htmlD31_2_CloseImage.html地址:https://github.com/ruigege66/JavaScript/blob/master/D31_iconSwitch.htmlhttps://gith…

jQuery 1.9+ 移除$.browser方法

轉載鏈接&#xff1a;http://blog.csdn.net/czplplp_900725/article/details/8704438 jQuery 從 1.9 版開始&#xff0c;移除了 $.browser 和 $.browser.version &#xff0c;取而代之的是 $.support。 在更新的 2.0 版本中&#xff0c;將不再支持 IE 6/7/8。 以后&#xff0c;…