數據結構——快排和歸并排序(非遞歸)

快速排序和歸并排序一般都是用遞歸來實現的,但是掌握非遞歸也是很重要的,說不定在面試的時候面試官突然問你快排或者歸并非遞歸實現,遞歸有時候并不好,在數據量非常大的時候效率就不好,但是使用非遞歸結果就不一樣了,總之各有各的好。

目錄

快速排序非遞歸實現

?歸并排序非遞歸實現

計數排序?


快速排序非遞歸實現

要實現快排的非遞歸需要借助棧,將區間存入棧中,在快排的遞歸中先是對整個區間單趟排序,把key值放到最終位置,在非遞歸中與遞歸的思想不變,先手搓一個棧,當然這里博主以前寫過,直接復制粘貼就行,有了棧就先對棧進行初始化,將整個區間壓棧,先壓左區間,再壓右區間,根據棧的特性“先進后出”,在一個循環中出棧再壓棧,每次出棧和壓棧都是一個區間,出棧就對該區間進行排序,排序好后就返回該一個值,這個值就是排序好的值的下標,以該下標為分界分為兩個區間,再開始進行排序,循環往復,直到棧中沒有數據退出循環,最后銷毀棧。

int PartSort(int* a, int left, int right)
{if (left >= right)return -1;int prev = left, cur = left + 1;int keyi = left;while (cur <= right){if (a[cur] < a[keyi]){prev++;Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[keyi], &a[prev]);return prev;	
}void PartSortR(int* a, int left, int right)
{ST st;STInit(&st);STPush(&st, left);STPush(&st, right);int begin = left, end = right;while (!STEmpty(&st)){end = STTop(&st);STPop(&st);begin = STTop(&st);STPop(&st);int keyi = PartSort(a, begin, end);//對一個區間單趟排序if (keyi+1 < end){STPush(&st, keyi + 1);STPush(&st, end);}if (keyi > begin){STPush(&st, begin);STPush(&st, keyi);}}STDestory(&st);
}

?在實現快排非遞歸時,單趟排序可以用hoare版本或者挖坑法以及前后指針法,這里使用的是前后指針法,在出棧和壓棧都是兩個數據,代表區間。

?歸并排序非遞歸實現

非遞歸的歸并排序和遞歸歸并排序的思想一樣,將一個大的問題分成一個一個小問題,將一串數據先通過分解來分成一個一個,再進行合并,在合并過程中比較大小,再放入一個新的數組中,最后把新數組拷貝到原來的數組,而非遞歸也是如此,只不過不需要和遞歸一樣分解,我們直接設置間距gap,一開始gap等于1,每個數據單獨為一組,每個數據進行比較,放入新數組,gap等于2,每兩個數據為一組,與下一組進行比較,每次gap需要乘以2,直到gap大于或者等于數據個數就結束,對數組的拷貝可以是在全部結束之后拷貝,也可以是部分拷貝只不過要拷貝多次。

因為gap每次都是乘以2,所以在數據個數不是2的n次方時會出現越界訪問,需要對該問題解決,可以通過調試解決,但是這里有一個小技巧,我們可以打印出區間,更好的看出哪個區間出現了越界訪問。

錯誤代碼:

void MergesortR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){int j = 0;for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i - 1 + gap;int begin2 = i + gap, end2 = i + 2 * gap - 1;printf("[%d,%d][%d,%d] ", begin1, end1, begin2, end2);while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}}printf("\n");memcpy(a, tmp, sizeof(int) * n);gap *= 2;}free(tmp);
}

這里數據是9個,看運行結果:

?這里可以看出gap為不同值時比較的區間,這里有三種情況:

1、begin2越界訪問了,begin2越界end2肯定也越界了。

2、end1越界了,begin2和end2肯定也越界了。

3、end2越界了。

所以只需要根據這三種情況對癥下藥:

1.當begin2越界了,就把begin2調成end2大的數就可以,因為end2 大于begin2就不會進入下面的循環,也就不會越界訪問。

2.當end1越界了,就把end1調成邊界值就可以,begin2 和 end2 就和第一種情況一樣的解決方法。

3.end2越界了就只需要將end2調成邊界就好了。

調整代碼:

void MergesortR(int* a,int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){int j = 0;for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i - 1 + gap;int begin2 = i + gap, end2 = i + 2 * gap - 1;//printf("[%d,%d][%d,%d] ", begin1, end1, begin2, end2);if (end1 > n - 1){end1 = n-1;begin2 = n + 1;end2 = n;}else if (end2 > n - 1){end2 = n - 1;}else if (begin2 > n - 1){end2 = n;begin2 = n + 1;}//printf("[%d,%d][%d,%d] ", begin1, end1, begin2, end2);while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}}//printf("\n");memcpy(a, tmp, sizeof(int) * n);gap *= 2;}free(tmp);
}

?運行結果:

最后不要忘記釋放向操作系統申請的空間,有借有還才行。?

計數排序?

思想:計數排序又稱為鴿巢原理,是對哈希直接定址法的變形應用。
1. 統計相同元素出現次數
2. 根據統計的結果將序列回收到原來的序列中

代碼展示:

//計數排序
void Countsort(int* a, int n)
{int max = a[0], min = a[0];for (int i = 0; i < n; i++){if (max < a[i]){max = a[i];}if (min > a[i]){min = a[i];}}int k = max - min+1;int *CountA = (int*)calloc(k, sizeof(int));if (CountA == NULL){perror("calloc fail");return;}for (int i = 0; i < n; i++){CountA[a[i] - min]++;}int j = 0;for (int i = 0; i < k; i++){while (CountA[i]--){a[j++] = i + min;}}
}

寫計數排序最好不要寫絕對位置,相對位置是最好的,絕對位置就是1就是在新數組對應的就是下標為1的位置,當數據中有負數時就會出現越界?,相對位置就是在統計某個值時,放入新數組時將該值減去這組數據中的最小值,所以需要找出該組數據中的最大和最小值,那么新數組的大小就是最大值減去最小值加一,當統計數據出現的個數時需要減去最小值,因為最小值減去最小值等于0,也就是新數組的下標范圍是0~最大值減去最小值。當完成以上操作就可以開始排序了,從新數組起始位置開始也就是0,將下標值加上最小值放入原來數組,循環次數就是在該下標對應值,如果CountA[i]等于0,說明i+最小值這個值不存在。

計數排序的特性總結:
1. 計數排序在數據范圍集中時,效率很高,但是適用范圍及場景有限。
2. 時間復雜度:O(MAX(N,范圍))
3. 空間復雜度:O(范圍)

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

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

相關文章

【筆記】網絡安全管理

計算機硬件中,運算器和控制器通常集成在一塊芯片內,一般稱為()。 數據庫DB、數據庫系統DBS、數據庫管理系統DBMS,三者之間的關系是()。 OSI/RM體系結構中的網絡層與TCP/IP體系結構中的()功能相同。 三級系統應按照等保2.0要求采用密碼技術通信過程中數據的()。 …

.net core web api 數據驗證(DataAnnotations)

目錄 一、什么是 DataAnnotations&#xff1f; 二、擴展驗證邏輯&#xff08;自定義驗證器&#xff09; 一、什么是 DataAnnotations&#xff1f; DataAnnotations 是一組特性&#xff08;Attributes&#xff09;&#xff0c;用于在模型類上定義驗證規則。主要用于屬性級別的…

小白從0學習網站搭建的關鍵事項和避坑指南

以下是針對小白從零學習網站搭建時需要注意的關鍵事項和避坑指南&#xff0c;幫助你高效學習、少走彎路&#xff1a; 一、學習路徑注意事項 不要跳過基礎 誤區&#xff1a;直接學習框架&#xff08;如 React、Laravel&#xff09;而忽視 HTML/CSS/JS 基礎。 正確做法&#xff…

深入剖析JavaScript內存泄漏:識別、定位與實戰解決

在JavaScript的世界里&#xff0c;開發者通常不必像使用C那樣手動管理內存的分配和釋放&#xff0c;這得益于JavaScript引擎內置的垃圾回收&#xff08;Garbage Collection, GC&#xff09;機制。然而&#xff0c;這并不意味著我們可以完全忽視內存管理。“自動"不等于&qu…

2025-04-19 Python 強類型編程

文章目錄 1 方法標注1.1 參數與返回值1.2 變參類型1.3 函數類型 2 數據類型2.1 內置類型2.2 復雜數據結構2.3 類別選擇2.4 泛型 3 標注方式3.1 注釋標注3.2 文件標注 4 特殊情形4.1 前置引用4.2 函數標注擴展4.3 協變與逆變4.4 dataclass 5 高級內容5.1 接口5.2 泛型的協變/逆變…

ETF價格相關性計算算法深度分析

1. 引言 在金融市場中&#xff0c;相關性就像是資產之間“跳舞”的默契程度。想象一下兩位舞者&#xff08;ETF&#xff09;&#xff0c;有時步伐一致&#xff0c;有時各跳各的。對于管理大規模資金的投資組合而言&#xff0c;準確理解ETF之間的“舞步同步性”對于風險管理、資…

上海人工智能實驗室:LLM無監督自訓練

&#x1f4d6;標題&#xff1a;Genius: A Generalizable and Purely Unsupervised Self-Training Framework For Advanced Reasoning &#x1f310;來源&#xff1a;arXiv, 2504.08672 &#x1f31f;摘要 &#x1f538;推進LLM推理技能引起了廣泛的興趣。然而&#xff0c;當前…

【WPF】 在WebView2使用echart顯示數據

文章目錄 前言一、NuGet安裝WebView2二、代碼部分1.xaml中引入webview22.編寫html3.在WebView2中加載html4.調用js方法為Echarts賦值 總結 前言 為了實現數據的三維效果&#xff0c;所以需要使用Echarts&#xff0c;但如何在WPF中使用Echarts呢&#xff1f; 一、NuGet安裝WebV…

2025年3月 Python編程等級考試 2級真題試卷

2025年3月青少年軟件編程Python等級考試&#xff08;二級&#xff09;真題試卷 題目總數&#xff1a;37 總分數&#xff1a;100 選擇題 第 1 題 單選題 老師要求大家記住四大名著的作者&#xff0c;小明機智地想到了可以用字典進行記錄&#xff0c;以下哪個選項的字典…

6. 話題通信 ---- 使用自定義msg,發布方和訂閱方cpp,python文件編寫

1)在功能包下新建msg目錄&#xff0c;在msg目錄下新建Person.msg,在Person.msg文件寫入&#xff1a; string name uint16 age float64 height 2)修改配置文件 2.1) 功能包下package.xml文件修改 <build_depend>message_generation</build_depend><exec_depend…

多線程使用——線程安全、線程同步

一、線程安全 &#xff08;一&#xff09;什么是線程安全問題 多個線程&#xff0c;同時操作同一個共享資源的時候&#xff0c;可能會出現業務安全的問題。 &#xff08;二&#xff09;用程序摹擬線程安全問題 二、線程同步 &#xff08;一&#xff09;同步思想概述 解決線…

4. 話題通信 ---- 發布方和訂閱方cpp文件編寫

本節對應趙虛左ROS書籍的2.1.2 以10hz,發布消息和消息的訂閱 1) 在功能包的src文件夾下&#xff0c;新建cpp文件&#xff0c;并且寫入 #include "ros/ros.h" #include "std_msgs/String.h" int main(int argc, char *argv[]) {setlocale(LC_ALL,"&…

有哪些哲學流派適合創業二

好的&#xff0c;讓我們更深入地探討如何將?哲學與數學?深度融合&#xff0c;構建一套可落地的創業操作系統。以下從?認知框架、決策引擎、執行算法?三個維度展開&#xff0c;包含具體工具和黑箱拆解&#xff1a; ?一、認知框架&#xff1a;用哲學重構商業本質? 1. ?本體…

【后端】【python】Python 爬蟲常用的框架解析

一、總結 Python 爬蟲常用的框架主要分為 三類&#xff1a; 輕量級請求庫&#xff1a;如 requests、httpx&#xff0c;用于快速發請求。解析與處理庫&#xff1a;如 BeautifulSoup、lxml、pyquery。爬蟲框架系統&#xff1a;如 Scrapy、pyspider、Selenium、Playwright 等&am…

力扣-hot100(無重復字符的最長子串)

3. 無重復字符的最長子串 中等 給定一個字符串 s &#xff0c;請你找出其中不含有重復字符的 最長 子串 的長度。 示例 1: 輸入: s "abcabcbb" 輸出: 3 解釋: 因為無重復字符的最長子串是 "abc"&#xff0c;所以其長度為 3。暴力直觀解法一&#xff1…

六邊形棋盤格(Hexagonal Grids)的坐標

1. 二位坐標轉六邊形棋盤的方式 1-1這是“波動式”的 這種就是把【方格子坐標】“左右各錯開半個格子”做到的 具體來說有如下幾種情況 具體到廟算平臺上&#xff0c;是很巧妙的用一個4位整數&#xff0c;前兩位為x、后兩位為y來進行表示 附上計算距離的代碼 def get_hex_di…

C++之虛函數 Virtual Function

1. 普通虛函數&#xff08;Virtual Function&#xff09; 定義&#xff1a;基類中用 virtual 聲明&#xff0c;允許派生類 覆蓋&#xff08;Override&#xff09;。特點&#xff1a; 基類可提供默認實現。派生類可選擇性覆蓋&#xff08;若不覆蓋&#xff0c;則調用基類版本&a…

基于尚硅谷FreeRTOS視頻筆記——15—系統配制文件說明與數據規范

目錄 配置函數 INCLUDE函數 config函數 數據類型 命名規范 函數與宏 配置函數 官網上可以查找 最核心的就是 config和INCLUDE INCLUDE函數 這些就是裁剪的函數 它們使用一個ifndef。如果定義了&#xff0c;就如果定義了這個宏定義&#xff0c;那么代碼就生效。 通過ifn…

HAL庫配置RS485+DMA+空閑中斷收發數據

前言&#xff1a; &#xff08;1&#xff09;DMA是單片機集成在芯片內部的一個數據搬運工&#xff0c;它可以代替單片機對數據進行傳輸、存儲&#xff0c;節約CPU資源。一般應用場景&#xff0c;ADC多通道采集&#xff0c;串口收發&#xff08;頻繁進入接收中斷&#xff09;&a…

從零開始解剖Spring Boot啟動流程:一個Java小白的奇幻冒險之旅

大家好呀&#xff01;今天我們要一起探索一個神奇的話題——Spring Boot的啟動流程。我知道很多小伙伴一聽到"啟動流程"四個字就開始頭疼&#xff0c;別擔心&#xff01;我會用最通俗易懂的方式&#xff0c;帶你從main()方法開始&#xff0c;一步步揭開Spring Boot的…