【數據結構初階】--排序(四):歸并排序

🔥個人主頁:@草莓熊Lotso

🎬作者簡介:C++研發方向學習者

📖個人專欄:?《C語言》?《數據結構與算法》《C語言刷題集》《Leetcode刷題指南》

??人生格言:生活是默默的堅持,毅力是永久的享受。

前言:在前面的學習中,我們實現了直接插入排序,希爾排序,直接選擇排序,堆排序,冒泡排序,快速排序。我們常見的幾種排序算法也差不過都學完了。那么我們這篇博客就繼續接著上一篇博客實現完的排序往后寫,來講講歸并排序,還是和之前一樣,我們先分部分來講解,特別聲明一下,博客中的排序都是以升序為例的。??


目錄

一.遞歸實現歸并排序

具體過程:

代碼實現:?

時間復雜度:

二.非遞歸實現歸并排序?

具體過程:

?代碼實現:

?三.歸并排序的性能測試

代碼演示:?


一.遞歸實現歸并排序

?歸并排序(MERGE-SORT)是建立在歸并操作上的?種有效的排序算法,該算法是采用分治法(Divide and Conquer)的?個非常典型的應用。將已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合并成?個有序表,稱為二路歸并。

?--我們著重講一下分治的思想。合并操作需要注意的地方,大家看看后面代碼的重點提示就行了

將待排序的數組不斷分割成更小的子數組,直到每個子數組只包含一個元素。

具體過程:

  • 找到數組的中間位置,一般是通過 mid = left + (right - left) / 2 來計算,left 和 right 分別表示當前數組范圍的起始和結束索引。
  • 然后遞歸地對數組的左半部分(索引范圍從 left 到 mid)和右半部分(索引范圍從 mid + 1 到 right)進行同樣的分割操作,直到子數組的長度為 1。例如,對于數組 [5, 3, 8, 6, 2],第一次分割得到 [5, 3] 和 [8, 6, 2],然后繼續遞歸分割這兩個子數組,直到每個子數組都只有一個元素,即 [5]、[3]、[8]、[6]、[2] 。

代碼實現:?

void _MergeSort(int* arr, int left, int right, int* tmp)
{if (left >= right){return;}//分解//[left,mid][mid+1,right]int mid = left + (right-left) / 2;  _MergeSort(arr, left, mid, tmp);_MergeSort(arr, mid+1, right, tmp);//合并int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int index = left;//一定是left而不是0,不然會受到影響while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]){tmp[index++] = arr[begin1++];}else {tmp[index++] = arr[begin2++];}}//有一個結束了,另外一個還沒結束while (begin1 <= end1){tmp[index++] = arr[begin1++];}while (begin2 <= end2){tmp[index++] = arr[begin2++];}//把數據拷貝回去for (int i = left; i <= right; i++){arr[i] = tmp[i];}
}//歸并排序--主函數里面不遞歸,所以可以先不傳left和right
void MergeSort(int* arr, int n)
{//開辟一個新數組int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail!");exit(1);}_MergeSort(arr, 0, n - 1, tmp);free(tmp);
}

重點提示:?

test.c:?

#include"Sort.h"void PrintArr(int* arr, int n)
{for (int i = 0; i < n; i++){printf("%d ", arr[i]);}printf("\n");
}void test1()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };int size = sizeof(a) / sizeof(a[0]);printf("排序前:");PrintArr(a, size);//歸并排序MergeSort(a, size);printf("排序后:");PrintArr(a, size);
}int main()
{test1();return 0;
}

--測試完成,升序排序沒有問題,退出碼為0

時間復雜度:

  • O(nlogn)

--時間復雜度 = 單次遞歸時間復雜度 * 遞歸次數?


二.非遞歸實現歸并排序?

非遞歸版本的歸并排序(迭代實現),核心思想是 “自底向上” 合并子數組,無需遞歸,直接通過循環控制分組大小(gap),逐步完成排序。

具體過程:

1. 分組合并:

  • gap 從 1 開始,每次翻倍(gap *= 2),表示當前合并的子數組長度。
  • 例如:gap=1 時,合并相鄰的 1 個元素 組成的子數組(實際是單個元素,視為有序);gap=2 時,合并相鄰的 2 個元素 組成的有序子數組,以此類推。

2. 邊界修正

  • 計算 end1,end2 時,需判斷是否超出數組范圍(n-1 是最后一個元素的索引)。
  • 若 begin2 >= n,說明第二個子數組不存在,直接跳過合并。

3. 合并操作

  • 用雙指針法合并兩個有序子數組到 tmp,再通過 memcpy 寫回原數組。
  • 保證合并后子數組有序,逐層向上歸并。

?代碼實現:

//非遞歸實現歸并排序
void MergeSortNore(int* arr, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail!");exit(1);}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;if (begin2 >= n){break;}if (end2 >= n){end2 = n - 1;}int index = i;while (begin1 <= end1 && begin2 <= end2){if (arr[begin1] < arr[begin2]){tmp[index++] = arr[begin1++];}else {tmp[index++] = arr[begin2++];}}//有一個結束了,另外一個還沒結束while (begin1 <= end1){tmp[index++] = arr[begin1++];}while (begin2 <= end2){tmp[index++] = arr[begin2++];}//把數據拷貝回去memcpy(arr + i, tmp + i, (end2 - i + 1) * sizeof(int));}gap *= 2;}free(tmp);
}

test.c:

#include"Sort.h"void PrintArr(int* arr, int n)
{for (int i = 0; i < n; i++){printf("%d ", arr[i]);}printf("\n");
}void test1()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };int size = sizeof(a) / sizeof(a[0]);printf("排序前:");PrintArr(a, size);//非遞歸實現歸并排序MergeSortNore(a, size);printf("排序后:");PrintArr(a, size);
}int main()
{//TestOP();test1();return 0;
}

--測試完成,升序排序沒有問題,退出碼為0


?三.歸并排序的性能測試

--我們實現完了這么多種排序,我們還是通過測試函數看看它們的性能對比吧

代碼演示:?

include"Sort.h"void PrintArr(int* arr, int n)
{for (int i = 0; i < n; i++){printf("%d ", arr[i]);}printf("\n");
}void test1()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };int size = sizeof(a) / sizeof(a[0]);printf("排序前:");PrintArr(a, size);//直接插入排序//InsertSort(a, size);//希爾排序//ShellSort(a, size);//直接選擇排序//SelectSort(a, size);//堆排序//HeapSort(a, size);//冒泡排序//BubbleSort(a, size);//快速排序//QuickSort(a, 0, size - 1);//非遞歸快速排序//QuickSortNoare(a, 0, size - 1);//快速排序進階版//QuickSortMore(a, 0, size - 1);//歸并排序//MergeSort(a, size);//非遞歸實現歸并排序//MergeSortNore(a, size);//非比較排序--計數排序CountSort(a, size);printf("排序后:");PrintArr(a, size);
}// 測試排序的性能對比
void TestOP()
{srand(time(0));const int N = 100000;int* a1 = (int*)malloc(sizeof(int) * N);int* a2 = (int*)malloc(sizeof(int) * N);int* a3 = (int*)malloc(sizeof(int) * N);int* a4 = (int*)malloc(sizeof(int) * N);int* a5 = (int*)malloc(sizeof(int) * N);int* a6 = (int*)malloc(sizeof(int) * N);int* a7 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; ++i){a1[i] = rand();a2[i] = a1[i];a3[i] = a1[i];a4[i] = a1[i];a5[i] = a1[i];a6[i] = a1[i];a7[i] = a1[i];}int begin1 = clock();InsertSort(a1, N);int end1 = clock();int begin2 = clock();ShellSort(a2, N);int end2 = clock();int begin3 = clock();SelectSort(a3, N);int end3 = clock();int begin4 = clock();HeapSort(a4, N);int end4 = clock();int begin5 = clock();QuickSort(a5, 0, N - 1);int end5 = clock();int begin6 = clock();MergeSort(a6, N);int end6 = clock();int begin7 = clock();BubbleSort(a7, N);int end7 = clock();printf("InsertSort:%d\n", end1 - begin1);printf("ShellSort:%d\n", end2 - begin2);printf("SelectSort:%d\n", end3 - begin3);printf("HeapSort:%d\n", end4 - begin4);printf("QuickSort:%d\n", end5 - begin5);printf("MergeSort:%d\n", end6 - begin6);printf("BubbleSort:%d\n", end7 - begin7);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);
}int main()
{TestOP();//test1();return 0;
}

--我們可以通過下圖對比一下各個排序的效率


往期回顧:?

【數據結構初階】--排序(一):直接插入排序,希爾排序

【數據結構初階】--排序(二)--直接選擇排序,堆排序

【數據結構初階】--排序(三):冒泡排序,快速排序

結語:到目前為止,我們已經講將常見的比較類的各種排序都實現完了,在下篇博客中我會為大家介紹一下非比較排序中的計數排序以及總結各種排序的空間復雜度,時間復雜度以及穩定性,如果文章對你有幫助的話,歡迎評論,點贊,收藏加關注,感謝大家的支持。

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

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

相關文章

GaussDB 并行創建索引

1 背景當業務數據在單表存儲達到一定的數量級時&#xff0c;此時對表創建索引是要花費時間的。GaussDB為了解決這個問題采用并行創建索引技術&#xff0c;以提高創建索引的效率。2 示例步驟1&#xff1a;根據實際情況調整maintenance_work_mem參數該大小。[Rubydtest1 ~]$ gsq…

LOOP Finance:一場 Web3 共和國中的金融制度實驗

LOOP Finance 是建構于幣安智能鏈&#xff08;BNB Chain&#xff09;上的定投型DEFI理財協議。 它以凱因斯經濟學為啟發&#xff0c;設計出一套長期、安全、穩定收益的全新DEFI玩法&#xff0c;兼顧穩健利息回報與DEFI高速成長的潛力。 通過生態機制&#xff0c;LOOP要求每位參…

【golang面試題】Golang遞歸函數完全指南:從入門到性能優化

引言&#xff1a;遞歸的本質與挑戰 在Golang中&#xff0c;遞歸函數是一把鋒利的雙刃劍。它通過函數自身調用實現問題分解&#xff0c;讓代碼變得簡潔優雅&#xff0c;但也容易因無限遞歸、棧溢出或性能問題讓開發者陷入困境。本文將從基礎到高級&#xff0c;全面解析Golang遞歸…

功能安全和網絡安全的綜合保障流程

摘要網絡物理系統是控制機械部件的計算機化系統。這些系統必須既功能安全又網絡安全。因此&#xff0c;已建立的功能安全與網絡安全標準需求創建網絡安全檔案&#xff08;ACs&#xff09;&#xff0c;以論證系統是功能安全與網絡安全的&#xff0c;即所有功能安全與網絡安全目標…

數據科學首戰:用機器學習預測世界杯冠軍

數據科學首戰&#xff1a;用機器學習預測世界杯冠軍Scikit-learn實戰&#xff1a;從數據清洗到冠軍預測的完整指南一、足球預測&#xff1a;數據科學的終極挑戰??世界杯數據價值??&#xff1a;歷史比賽數據&#xff1a;44,000場球隊特征指標&#xff1a;200球員數據點&…

一個php 連sqlserver 目標計算機積極拒絕,無法連接問題的解決

一個接口查詢數據耗時15秒&#xff0c;還沒數據&#xff0c;經查報錯日志&#xff1a;SQLSTATE[08001]: [Microsoft][ODBC Driver 17 for SQL Server]TCP 提供程序: 由于目標計算機積極拒絕&#xff0c;無法連接。 命令行執行&#xff1a;netstat -ano | findstr :1433發現結…

生成網站sitemap.xml地圖教程

要生成 sitemap.xml 文件&#xff0c;需要通過爬蟲程序抓取網站的所有有效鏈接。以下是完整的解決方案&#xff1a; 步驟 1&#xff1a;安裝必要的 Python 庫 ounter(line pip install requests beautifulsoup4 lxml 步驟 2&#xff1a;創建 Python 爬蟲腳本 (sitemap_genera…

idea拉取新項目第一次啟動報內存溢出(java.lang.OutOfMemoryError: Java heap space)

背景&#xff1a; 新拉取一個項目后&#xff0c;第一次啟動的時候報錯內存溢出&#xff1a; Java 堆內存溢出 (java.lang.OutOfMemoryError: Java heap space) 這個錯誤表示你的 Java 應用程序需要的內存超過了 JVM 堆內存的分配上限。 解決方案 1.增加堆內存大小 啟動應用時添…

安卓雷電模擬器安裝frida調試

1.在模擬器中設置調試root和adb 2.在vscode中安裝autox.js 3.在github上下載auto.js組件 新地址鏈接看來大佬的項目也經歷了波折https://blog.csdn.net/weixin_41961749/article/details/145669531 github地址https://github.com/aiselp/AutoX/releases 將下載的apk放入雷電…

Godot ------ 初級人物血條制作02

Godot ------ 初級人物血條制作02引言正文血條動態顯示引言 在 Godot ------ 初級人物血條制作01 一文中我們介紹了如何構建一個初級血條&#xff0c;但是我們并沒有涉及如何動態顯示血條。本文我們將介紹如何動態顯示血條。 正文 血條動態顯示 首先&#xff0c;我們為當前…

(Python)待辦事項升級網頁版(html)(Python項目)

源代碼&#xff1a; app.py from flask import Flask, render_template, request, redirect, url_for, jsonify import json import osapp Flask(__name__)# 數據存儲文件 DATA_FILE "todos.json"def load_todos():"""從文件加載待辦事項"&q…

智慧養老破局:科技如何讓“老有所養”變成“老有優養”?

隨著人口老齡化加劇&#xff0c;“養老”成了社會關注的焦點。傳統養老往往停留在“有地方住、有人照顧”的基礎需求&#xff0c;而智慧養老則通過科技與人文的結合&#xff0c;讓老年人的生活從“老有所養”升級到“老有優養”。不僅活得安心&#xff0c;更能活得有尊嚴、有質…

自學嵌入式 day45 ARM體系架構

一、SOCRAM&#xff1a;隨機訪問存儲器&#xff0c;存放隨機變量&#xff0c;掉電數據丟失ROM&#xff1a;只讀存儲器&#xff0c;存放單片機的程序、指令&#xff0c;掉電數據不丟失注&#xff1a;1、馮諾依曼架構中將數據與指令存放在同一存儲器中2、哈佛架構是將數據與指令存…

HTML應用指南:利用GET請求獲取全國OPPO官方授權體驗店門店位置信息

本篇文章將利用GET請求從OPPO官方網站或公開接口中獲取官方授權體驗店的分布信息&#xff0c;并通過Python編程語言中的requests庫來實現HTTP請求&#xff0c;從而提取詳細的門店位置數據。隨著OPPO品牌的發展和市場布局的擴展&#xff0c;其官方授權體驗店已經遍布全國各大城市…

Self-RAG:基于自我反思的檢索增強生成框架技術解析

本文由「大千AI助手」原創發布&#xff0c;專注用真話講AI&#xff0c;回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我&#xff0c;一起撕掉過度包裝&#xff0c;學習真實的AI技術&#xff01; 一、核心定義與原始論文 Self-RAG&#xff08;Self-Reflective Retri…

【YOLOv8改進 - C2f融合】C2f融合DBlock(Decoder Block):解碼器塊,去模糊和提升圖像清晰度

YOLOv8目標檢測創新改進與實戰案例專欄 專欄目錄: YOLOv8有效改進系列及項目實戰目錄 包含卷積,主干 注意力,檢測頭等創新機制 以及 各種目標檢測分割項目實戰案例 專欄鏈接: YOLOv8基礎解析+創新改進+實戰案例 文章目錄 YOLOv8目標檢測創新改進與實戰案例專欄 介紹 摘要 文…

LLamafactory是什么?

LLamaFactory是一個專注于大型語言模型&#xff08;LLM&#xff09;訓練、微調和部署的開源工具平臺&#xff0c;旨在簡化大模型的應用開發流程。?1.核心功能與特點?LlamaFactory&#xff08;全稱Large Language Model Factory&#xff09;作為一站式AI開發工具平臺&#xff…

Element Plus編輯表格時的頁面回顯(scope)

1、前提&#xff1a;自定義列模版(把id作為參數&#xff0c;傳遞到調用的edit函數里)<template #default"scope"><el-button type"primary" size"small" click"edit(scope.row.id)"><el-icon><EditPen /><…

河南萌新聯賽2025第四場-河南大學

今天又是坐牢的一次比賽&#xff0c;恭喜獲得本次比賽稱號&#xff1a;掛機王&#xff0c;一個簽到題能卡住兩個小時&#xff0c;這兩個小時簡直坐的我懷疑人生&#xff0c;實在是找不出哪里錯了&#xff0c;后來快結束的時候才發現少了一個等于號&#xff0c;也不至于連簽到題…

【Excel】通過Index函數向下拖動單元格并【重復引用/循環引用】數據源

文章目錄CASE1: 列數據源&#xff0c;向下拖動&#xff0c;每個單元重復N次步驟1&#xff1a;基本的INDEX函數步驟2&#xff1a;添加行號計算步驟3&#xff1a;添加絕對引用以便拖動CASE2:列數據源&#xff0c;向下拖動&#xff0c;每個單元重復1次&#xff0c;周而復始步驟1&a…