算法學習筆記(5.0)-基于比較的高效排序算法-歸并排序

##時間復雜度O(nlogn)

目錄

##時間復雜度O(nlogn)

##遞歸實現歸并排序

##原理

##圖例

##代碼實現

##非遞歸實現歸并排序

##釋

#代碼實現


##遞歸實現歸并排序

##原理

是一種基于分治策略的基礎排序算法。

1.劃分階段:通過不斷遞歸地將數組從中點處分開,將長數組的排序問題轉化成段數組的排序問題。

2.合并階段:當子數組的長度為1時終止劃分,開始合并,持續不斷地將左右兩個較短的有序數組合并為一個較長的有序數組,直到結束。

##圖例

##代碼實現

1.向下遞歸,對半分割

2.遞歸返回條件:遞歸到最小,1個就是有序【控制的是范圍,歸并的是區間】

3.遞歸到最深處,最小時,就遞歸回去,開始分割按對半分割出的范圍,將已有子序列合并,在tmp里面進行合并

4.再將tmp里形成的有序序列,拷貝回原數組【因下一次遞歸回去上一層的歸并過程中,會將數據在tmp中進行歸并,會將tmp中的數據覆蓋,因此要及時,拷】

//python代碼示例
def Merge(a, start, mid, end) :tmp = [] #創建一個臨時列表,用來存儲合并的值#兩段起點的開始l = startr = mid + 1#當兩段都沒到達末尾,則繼續循環,將其添加到tmpwhile l <= mid and r <= end :if a[l] <= a[r] :tmp.append(a[l])l += 1else :tmp.append(a[r])r += 1#此時肯定只剩下,單一的一個值,即將剩余元素塞入到tmp中tmp.extend(a[l:mid+1])tmp.extend(a[r:end+1])#將合并完成的元素,賦值到a原數組中for i in range(start,end+1):a[i] = tmp[i - start]print(start,end,tmp)def MergeSort(a,start,end) :#利用遞歸來實現歸并排序,將大的問題分解成小的子問題if start == end : #當遞歸到數組只有一個元素時,直接返回returnmid = (start + end) // 2 #計算出mid,找到遞歸點MergeSort(a,start,mid) #左遞歸MergeSort(a,mid+1,end) #右遞歸Merge(a,start,mid,end)#左遞歸,左合并,右遞歸,右合并,類似于樹的后序遍歷a = [7,3,2,6]
MergeSort(a,0,3)

//c++代碼實現示例
#include<iostream>
#include<vector>
using namespace std;
//c++代碼實現示例
void merge(vector<int> &a, int left, int mid, int right)
{int i = left ;int j = mid + 1 ;int k = left ;vector<int> tmp(right - mid + 1) ;while ( i <= mid && j <= right ){if ( a[i] <= a[j] ){tmp[k++] = a[i++] ;}else{tmp[k++] = a[j++] ;}}while ( i <= mid ){tmp[k++] = a[i++] ;}while ( j <= right ){tmp[k++] = a[j++] ;}for (int m = left ; m <= right ; m++){a[m] = tmp[m - left] ;}cout << left << right << " " ;for (int n = 0 ; n < tmp.size() ; ++n){cout << tmp[n] << " " ;}
}void mergeSort(vector<int> &a, int left, int right)
{if (left >= right){return ;}int mid = (left + right) / 2 ;mergeSort(a,left,mid) ;mergeSort(a,mid+1,right) ;merge(a,left,mid,right) ;
}int main()
{
//	vector<int> arr = {1,2,3,4,5};
//	vector<int> arr;arr.push_back(7);arr.push_back(3);arr.push_back(2);arr.push_back(6);
//    arr.push_back(5);mergeSort(arr,0,3) ;return 0 ;}
void _MergerSort(DataType* a ,DataType* tmp, int begin,int end)
{//遞歸返回條件,不正常的范圍,或只剩下一個數 if (begin >= end){return ;}int mid = (begin + end) / 2 ;//遞歸過程 _MergeSort(a,tmp,begin,mid) ;_MergeSort(a,tmp,mid+1,end) ;//合并過程 //歸并到tmp數組中,再拷貝回去 int begin1 = begin ;int end1 = mid ;int begin2 = mid + 1 ;int end2 = end ;//指向tmp,=begin是 根據要進行比較插入的數組的位置 找到其對應在tmp中所對應的位置,則不會覆蓋前面已經排好序的數據int index = begin ;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[index++] = a[begin++] ;}else{tmp[index++] = a[begin2++] ;}}//剩下還沒有插入進去的 while (begin1 <= end1){tmp[index++] = a[begin1++];}while (begin2 <= end2){tmp[index++] = a[begin2++] ;}//拷貝回去原數組a中memcpy(a+begin,tmp+begin,sizeof(DataType)*(end-begin+1)) ; 
}void MergeSort(DataType* a, int n)
{DataType* tmp = (DataType*)malloc(sizeof(DataType)*n) ;if (tmp == NULL){perror("malloc fail") ;return ;}_MergeSort(a,tmp,0,n-1) ;free(tmp) ;
}

對于鏈表,歸并排序相較于其他排序算法具有顯著優勢,可以將鏈表排序任務的空間復雜度優化至?𝑂(1)?。

  • 劃分階段:可以使用“迭代”替代“遞歸”來實現鏈表劃分工作,從而省去遞歸使用的棧幀空間。
  • 合并階段:在鏈表中,節點增刪操作僅需改變引用(指針)即可實現,因此合并階段(將兩個短有序鏈表合并為一個長有序鏈表)無須創建額外鏈表。

##非遞歸實現歸并排序

##釋

歸并排序時二分的思想=>logN層=>遞歸不會太深、且現在編譯器優化后,遞歸、非遞歸的性能差距沒有特別大了=>所以可以不用考慮非遞歸。

遞歸的缺點:遞歸消耗棧幀,遞歸的層數太深,容易爆棧

【棧的空間比較小,在x86(32位)環境下,只有8M。(對比同一環境下的堆,則有2G+)。因為平時函數調用開不了多少個棧幀。理論上遞歸深度>1w 可能就會爆 ,但實際上5k左右就爆掉了】,這時就需要改非遞歸了。

非遞歸改寫方法:

1.改變循環

2.利用數據結構棧(本質是通過malloc在堆上開辟的存儲空間,內存空間需要足夠大)

3.遞歸逆著求-實際上也差不多也是遞歸思想(如斐波那契數列逆著來求也是可行的)

#代碼實現

  1. 開辟新的數組(臨時存放)用于歸并排序過程
  2. int gap=1;gap*=2【gap控制歸并的范圍:11歸并,22歸并,44歸并】
  3. for (int i = 0; i < n; i += 2 * gap) { 【i 控制進行比較輪到的組號,控制進行歸并的組號】
  4. 歸并完一輪,將歸并好的有序數組拷貝回原數組memcpy 。
  5. 進入新的一輪歸并,直至gap>n則歸并完成

?????☆注意的兩個情況

?????6. if (begin2 >= n) { break; } 第二組不存在,這一組不用歸并了?

? ? ?7. if (end2 > n) { end2 = n - 1; } 第二組右邊界越界問題,修正一下

void MergerSortNonR(DataType* a, int n)
{DataType* tmp = (DataType*)malloc(sizeof(DataType) * n); ? //開辟新的數組(臨時存放)用于歸并排序過程if (tmp == NULL){perror("malloc fail") ;return ;}int gap = 1 ;while (gap < n)?{for (int i = 0 ; i < n ; i += 2 * gap){
//?? ??? ??? ?// 1 , 1 歸并?
//?? ??? ??? ?// 2 , 2 歸并
//?? ??? ??? ?//4 , 4 歸并?
//?? ??? ??? ?//[begin1,end1][begin2,end2]歸并?int begin1 = i ;int end1 = i + gap - 1 ;int begin2 = i + gap ;int end2 = i + 2 * gap - 1 ;//?? ??? ??? ?//如果第二組不存在,這一組不用歸并了if (begin2 >= n)?{break;}//第二組右邊界越界問題,修正一下if (end2 > n)?{end2 = n - 1;}
//?? ??? ??? ?
//?? ??? ??? ?//當beigin2 超過 n 的時候,直接break掉就OK了
//?? ??? ??? ?//但end2 超過 n 的時候,需要修改邊界問題 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++];}
//?? ??? ??? ?//為什么這里不能是,2 * gap呢,不一定是2*gap的數都拷貝過去,memcpy(a+i,tmp+i,sizeof(DataType)*(end2 - beigin1 + 1)) 錯誤?
//?? ??? ??? ?// memcpy(a+i,tmp+i,sizeof(DataType)*(end2 - begin1 + 1)) ; 因為begin1++會發生改變的,因此不可以,錯誤?memcpy(a+i,tmp+i,sizeof(DataType)*(end2 - i + 1)) ;
//?? ??? ??? ?
//?? ??? ?}printf("\n");
//?? ??? ?for (int k = 0 ; tmp.size() ; k ++)
//?? ??? ?{
//?? ??? ??? ?printf("%d",tmp[k]) ;
//?? ??? ?}gap = gap * 2 ;}free(tmp);
}
}
def MergeSort(a, n) :tmp = [0] * (n+1)gap = 1while (gap < n) :z = gap * 2for i in range(0,n,z) :begin1 = iend1 = i + gap - 1begin2 = i + gapend2 = i + 2 * gap - 1if begin2 > n :breakif end2 > n :end2 = n - 1index = iwhile begin1 <= end1 and begin2 <= end2 :if a[begin1] < a[begin2] :tmp[index] = a[begin1]index += 1begin1 += 1else :tmp[index] = a[begin2]index += 1begin2 += 1while begin1 <= end1 :tmp[index] = a[begin1]index += 1begin1 += 1while begin2 <= end2 :tmp[index] = a[begin2]index += 1begin2 += 1for j in range(i,end2 + 1) :a[j] = tmp[j - i]print()# print(tmp)gap = gap * 2

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

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

相關文章

Java 開發 框架安全:Spring 命令執行漏洞.(CVE-2022-22965)

什么叫 Spring 框架. Spring 框架是一個用于構建企業級應用程序的開源框架。它提供了一種全面的編程和配置模型&#xff0c;可以簡化應用程序的開發過程。Spring 框架的核心特性包括依賴注入&#xff08;Dependency Injection&#xff09;、面向切面編程&#xff08;Aspect-Or…

【SpringBoot筆記43】SpringBoot應用程序集成spring-boot-admin監控工具

這篇文章,主要介紹SpringBoot應用程序如何集成spring-boot-admin監控工具。 目錄 一、spring-boot-admin監控工具 1.1、創建admin-client客戶端 (1)引入依賴

DeepSpeed

文章目錄 一、關于 DeepSpeed1、DeepSpeed 是什么2、深度學習訓練和推理的極致速度和規模3、DeepSpeed 的四大創新支柱1&#xff09;DeepSpeed 訓練2&#xff09;DeepSpeed 推理3&#xff09;DeepSpeed 壓縮4&#xff09;DeepSpeed4Science 4、DeepSpeed 軟件套件DeepSpeed 庫推…

React 第二十七章 Hook useCallback

useCallback 是 React 提供的一個 Hook 函數&#xff0c;用于優化性能。它的作用是返回一個記憶化的函數&#xff0c;當依賴發生變化時&#xff0c;才會重新創建并返回新的函數。 在 React 中&#xff0c;當一個組件重新渲染時&#xff0c;所有的函數都會被重新創建。這可能會…

青少年軟件編程(Python)等級考試試卷(五級)2024年3月

2024.03 電子學會 青少年軟件編程&#xff08;Python&#xff09;等級考試試卷&#xff08;五級&#xff09; 一、單選題 1.以下代碼的輸出結果是? ) nums list(range(100, 201)) print(nums[::10]) A.[100,110,120,130,140,150,160,170,180,190,200] B.[100,101,1…

QML筆記八

QML與C交互 QML中調用C功能、使用QML或者Quick中的C接口、使用C實現自定義的QML對象 注&#xff1a; 只有QObject的派生類才能與QML交互 QML引擎集成Qt元對象系統&#xff0c;QObject的派生子類的屬性、方法、信號都可以在QML中訪問 C類可以被注冊為一個QML實例 C類可以被注冊為…

【Web后端】請求頭

1、簡介 請求頭&#xff08;Request Headers&#xff09;是在HTTP協議中&#xff0c;客戶端&#xff08;如瀏覽器或應用程序&#xff09;向服務器發送請求時附帶的元數據。包含了關于請求的額外信息&#xff0c;有助于客戶端與服務器之間的有效通信。請求頭中的信息可以讓服務…

.[sqlback@memeware.net].2700勒索病毒數據怎么處理|數據解密恢復

導言&#xff1a; 隨著信息技術的飛速發展&#xff0c;網絡安全問題愈發嚴峻&#xff0c;其中勒索病毒成為了企業和個人用戶面臨的重要威脅之一。.[sqlbackmemeware.net].2700勒索病毒作為其中的佼佼者&#xff0c;以其獨特的攻擊方式和強大的破壞力&#xff0c;引起了廣泛關注…

【Go語言入門學習筆記】Part1.夢開始的地方

一、前言 經過一系列的學習&#xff0c;終于有時間來學習一些新的語言&#xff0c;Go語言在現在還是比較時髦的&#xff0c;多一個技能總比不多的好&#xff0c;故有時間來學一下。 二、配置環境 按照網絡中已有的配置方法配置好&#xff0c;本人采用了Jetbrain的Goland&#…

DTC 2024回顧丨zData X 多元數據庫一體機:開創多元數據庫時代部署新范式

導語 在2024“數據技術嘉年華”上&#xff0c;云和恩墨數據庫一體機產品總經理劉宇在“數據庫極致特性”專題論壇發表了題為《打造多元數據庫部署新范式&#xff0c;引領一體化資源池創新之路》的演講。他深入分析了國產數據庫面臨的挑戰&#xff0c;并詳細介紹了云和恩墨如何利…

5.10.1 Pre-Trained Image Processing Transformer

研究了低級計算機視覺任務&#xff08;例如去噪、超分辨率和去雨&#xff09;并開發了一種新的預訓練模型&#xff0c;即圖像處理變壓器&#xff08;IPT&#xff09;。利用著名的 ImageNet 基準來生成大量損壞的圖像對。 IPT 模型是在這些具有多頭和多尾的圖像上進行訓練的。此…

Megatron-lm、DeepSpeed

1、為了訓練更多的數據、更大的模型&#xff0c;提出了并行訓練框架。 2、并行的方式&#xff1a;數據并行、模型并行&#xff08;張量并行、流水線并行&#xff09;。 3、Megatron-LM 綜合應用了數據并行&#xff08;Data Parallelism&#xff09;&#xff0c;張量并行&…

內網安全工具之ADExplorer的使用

ADExplorer是域內一款信息查詢工具&#xff0c;它是獨立的可執行文件&#xff0c;無需安裝。它能夠列出域組織架構、用戶賬號、計算機賬號登&#xff0c;可以幫助尋找特權用戶和數據庫服務器等敏感目標。 下載地址&#xff1a;http://live.sysinternals.com/ 連接 下載了ADE…

第十四屆藍橋杯大賽軟件賽國賽C/C++ 大學 B 組 拼數字

//bfs只能過40%。 #include<bits/stdc.h> using namespace std; #define int long long int a,b,c,dp[2028]; struct s {int x,y,z;string m; }; map<vector<int>,int>k; signed main() {ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>a…

Java入門基礎學習筆記24——While循環和do-while循環

1、While循環&#xff1a; 例1&#xff1a; package cn.ensource.loop;public class WhileDemo3 {public static void main(String[] args) {// 目標&#xff1a;掌握while循環的書寫格式&#xff0c;以及理解其執行流程// 需求&#xff1a;打印多行Hello Worldint i 0;while…

EFCore_創建項目

添加依賴 Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore.Tools(Migration工具) 根據使用的DB添加對應依賴&#xff1a; SQL Server&#xff1a;Microsoft.EntityFrameworkCore.SqlServer 添加該依賴時可不添加Microsoft.EntityFrameworkCore&#xff0c;該依…

電工能混到這份上

最近看到某電工師傅發了一篇帖子&#xff0c;大致內容是他在處理一個簡單故障的時候居然花了很長的時間。我們一起來看看他遇到的是什么故障吧! plc 控制的一臺設備&#xff0c;行走部分靠 2 個腳踏開關控制&#xff08;內部開關量控制方向&#xff0c;電位器控制速度&#xff…

Java:使用BigDecimal、NumberFormat和DecimalFormat保留小數

一、代碼和調試結果 1.1 BigDecimal ![在這里插入圖片描述](https://img-blog.csdnimg.cn/direct/fa36749de8124266a730817710fdf737.png) 1.2 DecimalFormat 1.3 NumberFormat 二、原代碼 BigDecimalUtil.java 代碼 package utils;import java.math.BigDecimal; import jav…

前端模塊導入導出方式

不同的導出方式和相應的導入方式&#xff0c;可以提煉成 3 種類型&#xff1a;name、default 和 list。 以下是使用示例&#xff1a; // Name Export | Name Import // 一個“命名”的導出 export const name value import { name } from ...? 錯誤示例&#xff1a; export…

Linux平臺和Windows平臺互傳文件

rz和sz的出發對象都是從Linux出發的&#xff0c;例如sz發送&#xff08;Send&#xff09;從Linux->發送到Windows。 rz 從Windows文件發送到Linux中 先創立一個新文本文件 之后將hello Windows輸入到該文本文件中 在顯示器上顯示里面是否有hello Windows內容 sz發送Lin…