C語言歸并排序

以夢為馬,不負韶華

在這里插入圖片描述

文章目錄

      • 引入:
      • 實現原理
      • 問題引出:
      • 遞歸實現:
      • 迭代實現
      • 穩定性分析:
      • 總結:

引入:

如何將兩個有序數組(假設為升序)合并為一個有序數組?

雙指針法,如果第一個數組的第一個元素大于第二個數組的元素,就取第二個(即較小的那個放在合并的數組的首位置),然后在比較第一個數組第一個元素與第二個數組的第二個元素,以此類推,終將有一個數組的元素先被訪問完,剩下的那個數組的元素一定是大于已經排序后的數組,直接將未排完序的數組的元素添加到我們要合并數組即可。
在這里插入圖片描述

代碼如下

while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++];}

實現原理

?歸并排序就是利用上邊的思想進行排序,將無序的數組不斷折中至最短,并在合并的過程中將兩個數組進行合并并排序。其是建立在歸并操作上的一種有效的排序方式,采用分治法,將已經排好序的子序列合并,得到完全有序的序列,在歸的過程中將待排序的數組分為各個小塊,在并的過程中不斷使每個小區間變為有序,最終使該數組有序。
過程刨析
在這里插入圖片描述

問題引出:

能否在原數組進行排序?

  • 如果直接進行數據覆蓋的話明顯不可以,會將未修改的元素覆蓋,直接修改了數組元素,這種想法必然是錯誤的。
  • 聰明的同學會想到那么我用交換的形式呢?

看下邊這種情況
在這里插入圖片描述

?第二個小數組區間已經排好的序已經被打亂,在后邊的排序中,比較時就是先和5比,再和4比,這就違反了我們兩個有序數組分別從第一個元素比大小將小的那個放在合并的數組的第一個位置的核心思想。

?引出空間復雜度,順便也說一說他的時間復雜度,我想大家已經有所猜測了,畢竟和二分法沾邊的算法。
空間復雜度
?使用歸并排序的方法時要開一份同樣大小的數組裝載這個新數組,比較時用原數組,放置時用開好的新數組。因為遞歸時空間可以復用,所以歸并排序的空間復雜度明顯是O(N)。
時間復雜度
?時間復雜度是容易判斷,由遞歸的深度和判斷的次數即可得出歸并排序的時間復雜度,二分法將遞歸深度變為log(N),數據量為N,故時間復雜度為O(N*logN)。

遞歸實現:

void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perroe("malloc fail");return;}_MergeSort(a, tmp, 0, n - 1);free(tmp);
}
void _MergeSort(int* a, int* tmp, int begin, int end)
{if (end <= begin)return;int mid = (end + begin) / 2;MergeSort(a, tmp, begin, mid);MergeSort(a, tmp, mid + 1, end);//歸并到tmp數組,再拷貝回去//a->[begin,mid][mid+1,end]->tmp拷貝過去,新開空間int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int index = begin;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++];}memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}

?要注意遞歸的結束條件已經想要用遞歸來做什么,不斷分化小區間,然后調用小區間的歸并,直至每個數組只有一個元素,然后再用兩個有序數組合并為一個有序數組的方法,將兩個數組合并。

?上圖展開中好似前半段和后半段的排序是同時進行的,其實不然,根據代碼就可以看出,遞歸先排序好數組的前半部分,再排序數組的后半部分
在這里插入圖片描述

?在分割時是有順序的,如果遞歸的過程還是不夠清晰可以自己嘗試畫一畫遞歸展開圖,也可移步二叉樹問題詳談遞歸對遞歸的過程獲得更大的理解和掌握。


迭代實現

無疑是借助==循環(迭代)==來實現。
?仔細想一想我們會發現,在遞歸的過程中我們需要的是不斷獲得分割后每一小區間數組的坐標,分割直至每一個小數組只有一個元素為止,然后一個數組有兩個元素,然后繼續合并為八個元素,直至數組的每個坐標的值都被訪問一遍為止。

?如果大家已經看了快速排序的非遞歸版,可能會想到用棧來實現,然而這里用棧是不能解決這里的問題的,快速排序可以用棧是因為數組個數end在使用之后就pop出棧也沒有影響,然而歸并排序還需要知道劃分的范圍最后要合并數組,然而如果想要實現遞歸的過程,前邊的數據都已經被pop掉,無法確定數組范圍,就無法借此來合并。

如動圖所示:
在這里插入圖片描述
當然,如果一定要用棧或隊列來實現他,會很麻煩,因為有更好的方法可以解決。
要跳出遞歸的思路,利用循環的方法,歸并的思想。
如圖所示
在這里插入圖片描述
首先一個一個元素排序合并,兩個兩個元素進行排序并合并,兩兩排序后,每兩個元素就變成有序的了,然后四個四個合并并排序,然后八個和八個,這樣就將數組所由元素利用歸并的方式進行排序。

void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;//[begin1,end1]  [begin2,end2]歸并//如果第二組不存在,這一組就不用歸并啦if (begin2 >= n){break;}//如果第二組越界if (end2 >= n){end2 = n - 1;}int index = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++];}memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));}printf("\n");gap *= 2;}free(tmp);
}

下邊是上述代碼的解釋:

?可以看到,起始條件下gap的值為1,第一次循環時,將begin1為0,end1為0,begin2為1,end2為1。
判斷大小,將小的放前邊,大的放后邊,最后歸并到原數組

memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));

?由于i=0。拷貝回原數組時,a+i還是a的位置,拷貝數量在-0+1后變為了2,就將判斷后的兩個元素歸并到原數組,且已經排好了序。

  • 然后下次循環,i+2,指向了數組第三個元素。
    begin1=2,end1=2,begin2=3,end2=3。
    ?這時就是第二個元素第三個元素排序歸并,直至走完這一次for循環,gap*=2,回到外層while循環,gap<n繼續走,此時gap為2,再次進入for循環時,每次跳躍的間隔就是2,就實現了上邊所說,將數組依次分為一個和一個排序歸并,兩個兩個排序歸并,然后4個和4個歸并。。。。。。

注意:這里while結束的條件為gap>n,在循環內,一定會出現這樣的情況,雖然gap的值沒有大于n,但在for循環中,i的值加等于二倍的gap,所以begin2一定會大于n,這就會造成越界訪問,這時就不應該繼續執行循環,直接break出去,然后在while循環結尾,gap會*=2,當gap的值大于n時,就代表數組已經排序完了。

//如果第二組不存在,這一組就不用歸并啦if (begin2 >= n){break;}

?還有一種情況就是begin2沒有越界,然而end2越界,這時第二個數組還是存在的,直接將end2更改為n-1即可。

if (end2 >= n)
{end2 = n - 1;
}

穩定性分析:

?歸并排序是一個穩定的排序,除了空間復雜度比較大,其他的都是優點,通過上邊的演示,可以發現,在歸并的過程中相同數據的位置排列不會發生變化。

總結:

?歸并排序是一種很不錯的排序方式,如果追求高效率且需要穩定性,歸并排序是首當其沖的最優解,對于當前的計算機來說花費一點內存不算什么,歸并排序的思想很簡單,困難的是細節的把握,相對于其他相同時間復雜度的其他排序方式,大都是不穩定的,所以總體而言歸并排序在排序界還是有一席之地的。

今天的內容就到這里,歡迎大家留言反饋。
在這里插入圖片描述

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

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

相關文章

yolov5/v7修改標簽和檢測框顯示【最全】

《記錄自己在使用yolov5遇到的一些問題》同時也供大家參考&#xff0c;如果對你們有幫助&#xff0c;希望大家可以給個點贊、收藏鼓勵下&#xff0c;非常感謝&#xff01; 以自帶的一張圖片作為示例,yolov5(6.1版本)的初始檢測框應該是如下圖所示 修改線條粗細、隱藏標簽、隱…

EI論文故障識別程序:DBN深度置信/信念網絡的故障識別Matlab程序,數據由Excel導入,直接運行!

?適用平臺&#xff1a;Matlab2021b版及以上 本程序參考中文EI期刊《基于變分模態分解和改進灰狼算法優化深度置信網絡的自動轉換開關故障識別》中的深度置信網絡&#xff08;Deep Belief Network&#xff0c;DBN&#xff09;部分進行故障識別&#xff0c;程序注釋清晰&#x…

Python之學生信息管理系統

目錄 一、基礎界面實現 1、主函數 2、保持循環&#xff0c;獲取用戶需求 二、函數實現模塊功能 1、添加學生信息 2、刪除學生信息 3、修改學生信息 4、查找全部學生信息 5、退出系統 三、整合代碼 1、 完整代碼 2、完整實現過程 實現 打印功能菜單、添加學生信息、刪…

想自學軟件測試?一般人我還是勸你算了吧。。。

&#x1f4e2;專注于分享軟件測試干貨內容&#xff0c;歡迎點贊 &#x1f44d; 收藏 ?留言 &#x1f4dd; 如有錯誤敬請指正&#xff01;&#x1f4e2;交流討論&#xff1a;歡迎加入我們一起學習&#xff01;&#x1f4e2;資源分享&#xff1a;耗時200小時精選的「軟件測試」資…

<keep-alive>作用及用法

<keep-alive>是Vue.js的內置組件。它用于緩存具有相同組件樹的組件。當組件使用<keep-alive>包裹時&#xff0c;組件不會被銷毀&#xff0c;而是會緩存到內存中&#xff0c;等到下次再次渲染時&#xff0c;直接使用緩存中的組件實例。 <keep-alive>有以下幾…

【Linux】共享內存

文章目錄 一、共享內存的原理詳談共享內存的實現過程二、共享內存的接口函數1.shmget2. shmatshmdtshmctl 進程間使用共享內存通信三、共享內存的特性 關于代碼 一、共享內存的原理 共享內存是由操作系統維護和管理的一塊內存。 共享內存的本質是內核級的緩沖區。 一個進程向…

C語言精華題目錦集1

第一題 test.c文件中包括如下語句&#xff0c;文件中定義的四個變量中&#xff0c;是指針類型的是&#xff08;&#xff09;【多選】 #define INT_PTR int* typedef int* intptr; INT_PRT a,b; int_ptr c,d;A:a ?B:b ?C:c ?D:d #define是宏定義&#xff0c;此時在程序中IN…

SQLite3 數據庫學習(六):Qt 嵌入式 Web 服務器詳解

參考引用 SQLite 權威指南&#xff08;第二版&#xff09;SQLite3 入門 1. Apache 搭建 cgi 環境 1.1 什么是 Apache Apache 是世界使用排名第一的 Web 服務器軟件 它可以運行在幾乎所有廣泛使用的計算機平臺上&#xff0c;由于其跨平臺和安全性被廣泛使用 1.2 具體搭建流程…

一、用戶管理

一、后端數據庫初始化 1.1 因為版本問題&#xff0c;始終報錯&#xff0c;按照報錯信息去查詢解決方案&#xff0c;無法解決 靈機一動&#xff1a; 網址&#xff1a; Spring Boot 3.0 升級 實戰踩坑記錄 - 掘金 (juejin.cn) &#xff11;.&#xff12; 個人配置【運行成功…

c++的三目運算符

C三目運算符增強 C中的三目運算符表達式返回的可以是一個變量&#xff0c;但是C語言中返回的是一個常量。 C語言中&#xff1a; void test05() { int a 10; int b 20; printf("%d\n", a < b ? a : b); //在C語言中三目運算符返回的是表達式的值&am…

Javascript每天一道算法題(十三)——最大子數組和_中等

文章目錄 動態規劃題三個重要步驟&#xff08;了解思路&#xff09;1、問題2、示例3、解決方法&#xff08;1&#xff09;方法1——動態規劃 總結 動態規劃題三個重要步驟&#xff08;了解思路&#xff09; &#xff08;1&#xff09;定義數組元素的含義 用一個數組來保存歷史數…

2020年06月 Scratch(三級)真題解析#中國電子學會#全國青少年軟件編程等級考試

Scratch等級考試(1~4級)全部真題?點這里 一、單選題(共25題,每題2分,共50分) 第1題 執行以下腳本后舞臺上的角色將 ? A:先克隆自身,克隆體出現后被刪除。 B:先克隆自身,克隆體出現后刪除本體。 C:克隆出自身后本體與克隆體同時被刪除。 D:克隆出自身后本體與克…

docker常用命令, 鏡像版本的導入、導出并加載,打包鏡像的命令

文章目錄 docker常用命令&#xff1a;打鏡像包&#xff1a;鏡像版本的導入、導出并加載 docker常用命令&#xff1a; 打鏡像包&#xff1a; ? docker build -t calc:20230630 /home/apps/calc/docker/ 刪除某個鏡像的版本&#xff0c;allen_mysql的5.7版本 docker rmi all…

Redis深入理解-內核請求處理流程、數據傳輸協議

Redis 內核級請求處理流程 Redis Server 其實就是 Linux 服務器中的一個進程 主要還是下圖的流程 應用先和 server 端建立 TCP 連接建立連接之后&#xff0c;server 端就會有一個與該客戶端通信的 socket&#xff0c;客戶端的讀寫請求發送到服務端的 socket那么通過 IO 多路…

分組背包問題學習筆記 AcWing 9. 分組背包問題

原題 有 N&#xfffd; 組物品和一個容量是 V&#xfffd; 的背包。 每組物品有若干個&#xff0c;同一組內的物品最多只能選一個。 每件物品的體積是 vij&#xfffd;&#xfffd;&#xfffd;&#xff0c;價值是 wij&#xfffd;&#xfffd;&#xfffd;&#xff0c;其中 …

PC8233(CC/CV控制)高耐壓輸入5V/3.4A同步降壓電路內建補償帶恒流恒壓輸出

概述 PC8233&#xff08;替代CX8853&#xff09;是一款同步降壓調節器,輸出電流高達3.4A,操作范圍從8V到32V的寬電源電壓。內部補償要求最低數量現成的標準外部組件。PC8233在CC&#xff08;恒定輸出電流&#xff09;模式或CV&#xff08;恒定輸出電壓&#xff09;模式&#x…

【前端】前端監控?埋點

文章目錄 前端監控分為三個方面前端監控流程異常監控常見的錯誤捕獲方法主要是 try / catch 、window.onerror 和window.addEventListener 等。Promise 錯誤Vue 錯誤React 錯誤 性能監控用戶行為監控常見的埋點方案來源 前端監控分為三個方面 異常監控&#xff08;監控前端頁面…

基于element-ui后臺模板,日常嘮嗑

后面會補充github地址 文章目錄 目錄 文章目錄 案例說明 1.引入庫 2.創建布局組件 3.創建布局組件 4.菜單效果展示 5.創建頂部組件 5.創建頂部面包屑組件 6.創建內容區域組件 7.效果總覽 7.布丁&#xff08;實現一些小細節&#xff09; 前言一、pandas是什么&#xff1f;二、使…

CentOS7中升級OpenSSL詳細教程

文章目錄 一. 引言二. 升級前的準備1.備份現有配置2. 檢查系統版本3. 安裝依賴 三. OpenSSL安裝四. 驗證 一. 引言 OpenSSL: 是用于保護數據安全的重要工具。它能提供加密&#xff0c;解密等多項功能。 然而&#xff0c;隨著技術的發展和新的安全漏洞的出現&#xff0c;使用最…

管理類聯考——英語二——備考 100 句涵蓋所有詞匯

全中 在海里的這個地區&#xff0c;熊貓們喜歡就著蘇打碗豆喝茶。而大洋州的民兵則喜歡經過半島&#xff0c;帶著編劇本的公式上餐廳去。附件的電影院里有額外的歌劇和香蕉&#xff0c;這一時代的斑馬們被外面的天線所吸引。實驗室里的蟹想用它的肋骨去戳四肢象燈炮的小羊。但…