排序算法——歸并排序以及非遞歸實現

一、歸并排序思想

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

我們現在要樹立這么一個思路,分解數組并不是真的分解數組,而是可以通過下標的形式來區分數組。歸并排序實際上就是將一個數組分解到不能子啊分解的部分,兩兩經行比較排序,再將數據更新到新的數組中去。這非常類似于我們學的數的后序遍歷。

二、代碼實現

1、單趟:

我們先寫單趟,因為歸并排序是對半分組,所以最后為兩個區間經行比較,思路轉化為之前我們學過的兩個數組的合并

int mid = (rigt + left) / 2;
int begin1 = left;
int end1 = mid-1;
int begin2 = mid;
int end2 = rigt;
int i = left;
while (begin1 <= end1 && begin2 <= end2)
{if (a[begin1] > a[begin2]){tmp[i++] = a[begin2++];}else{tmp[i++] = a[begin1++];}
}
while (begin1<=end1)
{tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{tmp[i++] = a[begin2++];
}
memcpy(a+left, tmp+left, sizeof(int) * (rigt - left + 1));

這里要注意兩個問題:1、區間怎么劃分合適,2、為什么每排序一次就得將數組拷貝回去

?2、單趟問題解釋

首先我們先解決第一個問題:區間怎么劃分合適?

我們這里以下面圖為例:

因為有-1的存在很容易導致下標為負數,最后導致數組的違法訪問。同時可能還會有死循環的問題:

具體原因如下:

但是如果改為區間為?

int begin1 = left;
int end1 = mid;
int begin2 = mid+1;
int end2 = rigt;就不會出現這個問題:

所以正確代碼如下:

int mid = (rigt + left) / 2;
int begin1 = left;
int end1 = mid;
int begin2 = mid+1;
int end2 = rigt;
int i = left;
while (begin1 <= end1 && begin2 <= end2)
{if (a[begin1] > a[begin2]){tmp[i++] = a[begin2++];}else{tmp[i++] = a[begin1++];}
}
while (begin1<=end1)
{tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{tmp[i++] = a[begin2++];
}
memcpy(a+left, tmp+left, sizeof(int) * (rigt - left + 1));

第二個問題:因為比較的時候是在原數組經行比較如果不及時將數組內容更新,可能會導致再排序時經行錯誤的大小比較,最后導致結果錯誤?

3、總體代碼實現

結束條件當分割區間等于1或者小于1的時候。

void _MergeSort(int* a, int* tmp, int left, int rigt)
{if (left >= rigt){return;}int mid = (rigt + left) / 2;_MergeSort(a, tmp, left, mid);_MergeSort(a, tmp, mid + 1, rigt);int begin1 = left;int end1 = mid;int begin2 = mid+1;int end2 = rigt;int i = left;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] > a[begin2]){tmp[i++] = a[begin2++];}else{tmp[i++] = a[begin1++];}}while (begin1<=end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a+left, tmp+left, sizeof(int) * (rigt - left + 1));
}
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){return;}else{_MergeSort(a, tmp, 0, n - 1);free(tmp);tmp = NULL;}
}

三、非遞歸

對于歸并排序我們實現非遞歸用循環更好一點,非遞歸我們可以直接從遞歸回去那出發,定義一個組個數,通過控制組的個數實現“并”這個過程:

還是我們先寫一個數組只有一個數的情況:

for (int i = 0; i < end - begin + 1; i += 2 * gap)//每次比較兩組,所以加2gap
{int begin1 = i;int end1 = begin1 + gap - 1;int begin2 = begin1 + 2 * gap - 1;//第二組的開頭與第一組的開頭差2倍int end2 = begin2 + gap - 1;int k = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] > a[begin2]){tmp[k++] = a[begin2++];}else{tmp[k++] = a[begin1++];}}while (begin1 <= end1){tmp[k++] = a[begin1++];}while (begin2 <= end2){tmp[k++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * (2 * gap));//同理這里也得排序一次就拷貝回去}

那么后面就是gap*2控制即可:

void _MergeSortNonR(int* a, int* tmp,int begin, int end)
{int gap = 1;for (int j = gap; j < end-begin+1; j *= 2){for (int i = 0; i < end - begin + 1; i += 2 * gap)//每次比較兩組,所以加2gap{int begin1 = i;int end1 = begin1 + gap - 1;int begin2 = begin1 + 2 * gap - 1;//第二組的開頭與第一組的開頭差2倍int end2 = begin2 + gap - 1;int k = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] > a[begin2]){tmp[k++] = a[begin2++];}else{tmp[k++] = a[begin1++];}}while (begin1 <= end1){tmp[k++] = a[begin1++];}while (begin2 <= end2){tmp[k++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * (2 * gap));}}
}

但是上面的代碼仍然存在問題,和快排類似這里會存在數組越界問題:這里我們打印數組下標區間來觀察一下:、

大致我們可以分為這兩種情況:

?如果end1越界,那么說明分為了只有這一組數據,不用排了直接跳出循環,如果end2越界我們就需要給end2修改值。

所以改進代碼如下:

void _MergeSortNonR(int* a, int* tmp,int begin, int end)
{int gap = 1;for (int j = gap; j < end-begin+1; j *= 2){for (int i = 0; i < end - begin + 1; i += 2 * gap)//每次比較兩組,所以加2gap{int begin1 = i;int end1 = begin1 + gap - 1;int begin2 = begin1 + 2 * gap - 1;//第二組的開頭與第一組的開頭差2倍int end2 = begin2 + gap - 1;int k = i;// 第二組都越界不存在,這一組就不需要歸并if (begin2 >= end - begin + 1)break;// 第二的組begin2沒越界,end2越界了,需要修正一下,繼續歸并if (end2 >= end - begin + 1)end2 = end - begin + 1 - 1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] > a[begin2]){tmp[k++] = a[begin2++];}else{tmp[k++] = a[begin1++];}}while (begin1 <= end1){tmp[k++] = a[begin1++];}while (begin2 <= end2){tmp[k++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * (2 * gap));}}
}
void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){return;}else{_MergeSortNonR(a, tmp, 0, n - 1);free(tmp);tmp = NULL;}
}

可能有人會想如果只剩一組數據了,我直接return行不行?實際上是可以的,因為break跳出循環后在gap*2還是只有一組數據,所以直接return即可。

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

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

相關文章

OkHttp,一個賊牛的Java工具包

在當今的網絡應用開發中,Java 作為一種成熟的編程語言,廣泛應用于服務器端和客戶端的開發。網絡請求作為應用開發中不可或缺的一部分,選擇一個高效、穩定的網絡庫尤為重要。OkHttp 就是這樣一款優秀的網絡庫,它為Java提供了簡單易用、功能強大的網絡請求能力。本文將向讀者…

關于編譯的一些思路和猜想

一、編譯原理的難度 編譯原理特別復雜&#xff0c;研究的是高級語言如何翻譯成匯編語言的過程。 二、編譯過程中一些思路 (一)語義識別的作用 語義識別指的是把一些無關字符忽略&#xff0c;把一些變量名保存在一起&#xff0c;把用空格隔開的關鍵字單獨放一起。 例如&#…

重新ysyx

一、克隆倉庫 1.創建ssh key ssh-keygen -t rsa cd ~/.ssh ls 查看里面是否有id_rsa id_rsa.pub ssh-keygen -t rsa -C "xiantong15834753336outlook.com" cat id_rsa.pub***********查看里面的內容&#xff0c;復制到下圖中綠色的按鈕 git init ssh -T g…

spark3.0.1版本查詢Hbase數據庫例子

需求背景 現有需求&#xff0c;需要采用spark查詢hbase數據庫的數據同步到中間分析庫&#xff0c;記錄spark集成hbase的簡單例子代碼 import org.apache.hadoop.hbase.HBaseConfiguration import org.apache.hadoop.hbase.client.{ConnectionFactory, Scan} import org.apach…

Marin說PCB之Max parallel知多少?

今天是個陽光明媚&#xff0c;萬里烏云的好日子。小編我一如既往地到家打開電腦準備看騰訊視頻的五十公里桃花塢的第四季&#xff0c;在看到汪蘇瀧汪臺說650電臺要解散的時候小編我差點也哭了。650電臺之于桃花塢就像樂隊的鼓手一樣&#xff0c;都是一個團隊的靈感啊&#xff0…

CSS中的長度單位詳解

在CSS中&#xff0c;長度單位是定義元素尺寸、間距、邊距等的重要工具。不同的長度單位具有不同的特性和使用場景。 絕對長度單位 絕對長度單位在所有設備和瀏覽器中表示相同的長度。這些單位包括&#xff1a; 1.像素&#xff08;px&#xff09; 像素是最常用的長度單位。一…

C語言分支和循環(2)

我的相關博客&#xff1a; C語言的分支與循環&#xff08;1&#xff09; 1.switch語句 除了 if 語句外&#xff0c;C語?還提供了 switch 語句來實現分?結構。 switch 語句是?種特殊形式的 的 if...else 結構&#xff0c;?于判斷條件有多個結果的情況。它把多重 else if…

非質量成本總結

非質量成本 非質量成本 定義 舉例 固定成本 不隨生產量或工作量變動而變動的成本 辦公室租賃費 可變成本 隨著生產量或工作變動而變動的成本 材料費 直接成本 可以直接計入某項目的成本 工人工資 間接成本 不能直接計入某項目而需要再幾個項目之間或在項目與職能部…

Linux基本指令3

Linux基本指令3 目錄 Linux基本指令3 一、Linux文件系統管理 二、Linux進程與服務管理

億發:制造型企業信息化規劃——從破冰到全面落地

在制造型企業中&#xff0c;信息化規劃的落地是一個復雜而關鍵的過程。盡管規劃和藍圖可能已經制定完畢&#xff0c;但如何成功地實施信息化才是關鍵所在。本文將詳細介紹制造型企業信息化規劃的落地過程&#xff0c;通過三個周期逐步推進&#xff0c;最終實現信息化與自動化的…

深度學習知識與心得

目錄 深度學習簡介 傳統機器學習 深度學習發展 感知機 前饋神經網絡 前饋神經網絡&#xff08;BP網絡&#xff09; 深度學習框架講解 深度學習框架 TensorFlow 一個簡單的線性函數擬合過程 卷積神經網絡CNN&#xff08;計算機視覺&#xff09; 自然語言處理NLP Wo…

OpenAI助手API接入-問答對自動生成

支持GPT-3.5-Turbo, GPT-4o, GPT-4-Turbo import json import openai from pathlib import Path import os client openai.OpenAI(base_urlbase_url, api_keyapi_key) file client.files.create( fileopen("H3.pdf", "rb"), purposeassistants ) …

HTTP 的三次握手

????? HTTP 的三次握手是指在建立 TCP 連接時&#xff0c;客戶端和服務器之間進行的三步握手過程。這個過程確保了雙方都能夠互相通信&#xff0c;并且同步了彼此的序列號和確認號。 概念&#xff1a; 第一次握手&#xff1a;客戶端發送一個 SYN&#xff08;同步…

2.1數據的表示和運算--進位制

2.數據的表示和運算 2.1進位制 &#x1f53a;問題&#xff1a;計算機采用二進制有什么優點&#xff1f; 答&#xff1a; 1.制造兩個穩態的物理器件較容易。 2.二進制的運算規則簡單。 3.便于用邏輯門電路實現運算。 4.二進制的0和1正好對應邏輯值真和假。 &#x1f53a;…

成功解決“ModuleNotFoundError: No Module Named ‘utils’”錯誤的全面指南

成功解決“ModuleNotFoundError: No Module Named ‘utils’”錯誤的全面指南 在Python編程中&#xff0c;遇到ModuleNotFoundError: No Module Named utils這樣的錯誤通常意味著Python解釋器無法找到名為utils的模塊。這可能是由于多種原因造成的&#xff0c;比如模塊確實不存…

念念不忘,必有回響 的 echo

念念不忘&#xff0c;必有回響 的 echo 念念不忘&#xff0c;必有回響 的 echo幾個示例更多信息 念念不忘&#xff0c;必有回響 的 echo echo命令用于在終端設備上輸出字符串或變量的值&#xff0c;類似于Python的print和C語言的printf&#xff0c;是Linux系統中最常用的命令…

【GIC400】——PLIC,NVIC 和 GIC 中斷對比

文章目錄 PLIC,NVIC 和 GIC 中斷對比中斷向量表PLIC中斷向量表中斷使能中斷服務函數NVIC中斷向量表中斷使能中斷服務函數GIC中斷向量表系列文章 【ARMv7-A】——異常與中斷 【ARMv7-A】——異常中斷處理概述

深度學習筆記:0.cuda安裝,成功

B站上說&#xff1a;cs上騙子太多。文章太久&#xff0c;我深以為然。用了一天。才裝好。其實很簡單。 CUDA安裝教程&#xff08;超詳細&#xff09;-CSDN博客文章瀏覽閱讀1w次&#xff0c;點贊5次&#xff0c;收藏56次。windows10 版本安裝 CUDA &#xff0c;首先需要下載兩個…

AI技術的演進與未來

隨著科技的不斷進步&#xff0c;人工智能&#xff08;AI&#xff09;技術已經成為引領時代發展的重要力量。從最初的模糊概念到如今的具體應用&#xff0c;wre98.cnAI技術已經滲透到我們生活的方方面面&#xff0c;并不斷拓展其邊界。本文將探討AI技術的演進歷程、當前應用領域…

【并發程序設計】總篇集(八萬字)

11_Concurrent_Programing 1.進程概念 在Linux中&#xff0c;進程是操作系統分配資源和調度運行的基本單位。 Linux中的進程有以下用處&#xff1a; 提高CPU利用率&#xff1a;通過進程的并發執行&#xff0c;可以讓多個程序同時利用計算機的資源&#xff0c;這樣每個用戶都…