排序復習/上(C語言版)

目錄

1.排序概念

2.冒泡排序

效率性能測試代碼:

?性能分析:

3.直接插入排序

單趟:

整體:

性能分析:

4.希爾排序(基于插入排序的優化)

單趟單組:

?單趟多組:

降低循環層次的辦法:

整體:

性能分析:

5.選擇排序

6.堆排序


1.排序概念

讓所有數據從小到大/從大到小,就是排序的概念

常見的排序算法:

1.直接插入排序 2.希爾排序

3.選擇排序 4.堆排序

5.冒泡排序 6.快速排序

7.歸并排序

2.冒泡排序

效率性能測試代碼:


void TestOP()
{srand(time(0));const int N = 50000;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);int* a8 = (int*)malloc(sizeof(int) * N);int* a9 = (int*)malloc(sizeof(int) * N);int* a10 = (int*)malloc(sizeof(int) * N);for (int i = 0; i < N; ++i){//重復數據多(函數特性,隨機值范圍在0~32767)a1[i] = rand();//重復數據不多//a1[i] = rand() + i;a2[i] = a1[i];a3[i] = a1[i];a4[i] = a1[i];a5[i] = a1[i];a6[i] = a1[i];a7[i] = a1[i];a8[i] = a1[i];a9[i] = a1[i];a10[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();HPSort(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();int begin8 = clock();QuickSortNON(a8, 0, N - 1);int end8 = clock();int begin9 = clock();MergeSortNON(a9, N);int end9 = clock();int begin10 = clock();CountSort(a10, N);int end10 = 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("QuickSortNON:%d\n", end8 - begin8);printf("MergeSort:%d\n", end6 - begin6);printf("MergeSortNON:%d\n", end9 - begin9);printf("BubbleSort:%d\n", end7 - begin7);printf("CountSort:%d\n", end10 - begin10);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);free(a8);free(a9);free(a10);
}int main()
{TestOP();return 0;
}

rand()范圍在0~32767,如果有32769個數據那么就肯定有至少一個數據是重復的

為了降低重復數量,因此可以+i 來優化??

	for (int j = 0;j < n;j++){//單趟for (int i = 0;i < n - j - 1;i++){if (a[i] > a[i + 1]) Swap(&a[i], &a[i + 1]);}}

冒泡排序:前后比較往后交換,交換時可以把大的往后挪,因此每次交換完可以把一個位置的元素確定好位置

最開始的一次要冒泡到n-1位置,為防止越界需要考慮清楚循環推出條件;當 j = 0 時,n - 1 = i 的話會出現a[n]越界,因此循環退出條件還需要 - 1


優化:若[0,j]位置沒有發生任何交換,說明已經有序了,直接停止循環

void BubbleSort(int* a, int n)
{for (int j = 0;j < n;j++){//單趟int flag = 1;for (int i = 0;i < n - j - 1;i++){if (a[i] > a[i + 1]) Swap(&a[i], &a[i + 1]);flag = 0;}if (flag) break;}}

?性能分析:

?時間復雜度:
?最壞:N^2
?最好:N,數據已經有序

3.直接插入排序

概念:對一個數組[3,9,10,21,5,4],假設要升序排序;5往前移動,先和10、21比較,無法插入;再往前移動,和9、10比較,無法插入;再往前移動,和3、9比較,可以插入(斗地主整理手牌用到的就是插入排序的思想)

排序思想:以上圖為例,先把end下標后面一個元素(假設為tmp)保存下來,然后依次比較end下標元素和tmp大小,如果不合適就把從end往后覆蓋1位,直到tmp到了合適的位置,把保存下來的值覆蓋原來end+1下標元素(這樣end+1下標元素才不會在數組中重復兩次)

整體邏輯:[0,end]排序完畢,[end+1,size] 需要排序,因此直接直接在數組中從頭到尾對每個下標元素往前做一次插入排序,即能完成排序

極端情況:當tmp是整個數組最小的,此時end下標會變成-1,需要解決

在處理復雜函數時,我們可以先從單趟排序寫起,然后拓展到整體排序

單趟:

	int end;int temp = a[end + 1];while (end >= 0){if (temp < a[end]){a[end + 1] = a[end]; end--;}else break; }a[end + 1] = temp;

??? 假設[0,end]有序,end+1位置的值往前比較插入
?? ?為防止覆蓋后,原值不在,因此需要有一個temp存儲end+1位置的值

? ? "a[end + 1] = a[end];" 要想把數據往后挪,通過覆蓋后一個值的方法

? ? 當temp比end下標值大,則比[0,end]位置的值都大;可以直接覆蓋掉此時end+1下標位置的元素(因為已經將原來temp位置的元素已經覆蓋掉了,[end+1,end+2]位置的元素相同了)

整體:

void InsertSort(int* a, int n)
{for (int i = 0;i < n - 1;i++){int end = i;int temp = a[end + 1];while (end >= 0){if (temp < a[end]){a[end + 1] = a[end]; end--;}else break; }a[end + 1] = temp;}
}

對每個數進行一次插入排序,從[0,end]有序到[0,n]有序
只能遍歷到n-2,temp = a[n] 會導致數組越界

通過循環語句的推出條件,直接解決了極限情況;出現極限情況時end為-1,a[end+1] = a[0] = temp

性能分析:

?? ?時間復雜度:
?? ? 最壞:N^2,逆序時是最壞情況(一次break語句都不會執行)
?? ? 最好:N,順序時是最好情況(每個temp進入循環就break)


?? ?為什么與冒泡排序時間復雜度相同,但實際運行起來插入排序快那么多?
?? ?首先,我們剛剛考慮的只是最好、最壞的情況,沒有考慮普遍情況
?? ?對于插入排序,只要temp比[0,end]位置中的某一數據大時,就可以直接break;每一個數據往前排的時候大概率都能進行break,或多或少跳過了幾個數據沒有運行,積少成多,運行時間就大大縮減了
?? ?對于冒泡排序,只有[0,j]位置完全有序才能break,break的條件比較苛刻;同時,只有完全有序時才能停止循環,無法像插入排序那樣積少成多

4.希爾排序(基于插入排序的優化)

?? ?希爾排序:
?? ?1.預排序(讓數據接近有序)
?? ?2.對已經預排序完的數組進行插入排序
?? ?插入排序的不足之處:每個數都有概率往前移動非常多次才能break;希爾排序就是基于插入排序優化,用來解決插入排序的這一不足


? ? 1.單組預排序
?? ?預排序的方法:將n個數分為gap組,兩數間隔為gap的為一組(如上圖),然后對每一組分別進行插入排序;此時假設有一個大的數在數組最后位置,(升序排序時)往前跳得比較快,能一次跳gap個數
?? ?把插入排序的 1 全部改成 gap 即可,因為是對兩數相隔為gap的組進行插入排序(從相隔為1 -> 相隔為gap)


?? ?2.多組預排序
?? ?總共有分別以[0,gap-1]下標為開頭元素的gap組,因此外面套一層[0,gap-1]的循環


?? ?3.整體排序:
?? ?gap == 1 時,相當于插入排序了;gap > 1 時,相當于預排序
?? ?因此我們可以使用這樣的一個思想:用預排序組成整體排序
?? ?gap越大,每組中的元素跳得越快,數組越無序;gap越小,每組中的元素跳得越慢,數組越有序
?? ?gap怎么變化最好? 科學研究每次 gap/3 是最好的
?? ?gap = gap/3 ,假設gap為2時,最后結果為0,gap為0就不會對數組元素進行排序,因此需要在末尾 +1 來保證最小的gap值為1

單趟單組:

	int gap = 3;//假設gap為3for (int i = 0;i < n - gap;i += gap) //為防止數組越界,遍歷到n-gap-1{int end = i;int temp = a[end + gap];while (end >= 0){if (temp < a[end]){a[end + gap] = a[end];end -= gap;}else break;}a[end + gap] = temp;}

?單趟多組:

	int gap = 3;//假設gap為3for (int j = 0;j < gap;j++){for (int i = j;i < n - gap;i += gap) //為防止數組越界,遍歷到n-gap-1{int end = i;int temp = a[end + gap];while (end >= 0){if (temp < a[end]){a[end + gap] = a[end];end -= gap;}else break;}a[end + gap] = temp;}}

降低循環層次的辦法:

上面是一組一組來排,下面代碼是每組并發的排序;如果有gap組,那么就從每組的首元素開始遍歷至末尾

	int gap = 3;//假設gap為3for (int i = 0;i < n - gap;i ++) //為防止數組越界,遍歷到n-gap-1{int end = i;int temp = a[end + gap];while (end >= 0){if (temp < a[end]){a[end + gap] = a[end];end -= gap;}else break;}a[end + gap] = temp;}

整體:

void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;for (int j = 0;j < gap;j++){for (int i = j;i < n - gap;i += gap) //為防止數組越界,遍歷到n-gap-1{int end = i;int temp = a[end + gap];while (end >= 0){if (temp < a[end]){a[end + gap] = a[end];end -= gap;}else break;}a[end + gap] = temp;}}}
}

性能分析:

?? ?效率如何被提升的?
?? ?最開始,每個數跳得都很快,這時正好數組最無序;最后一次排序(gap == 1),每個數都跳得很慢,但基本上都不怎么需要動了,都可以break掉
?? ?正是因為希爾排序的這種特性,數據量越大、重復的數據越多(重復數據多,break多挪動少)希爾排序就越是有優勢


?? ?時間復雜度 O(N^1.3)
?? ?gap組數據,假設忽略掉 +1 (方便運算),gap = n/3 同時 每組有3個元素(第一組有4個元素,也看作是3個)
?? ?最壞情況下第一趟預排序的消耗:逆序,(1+2)* n / 3 = n (每組的當中元素向前移動1位,后一個元素向前移動2位,總共有n/3組)
?? ?最壞情況下第二趟預排序(gap/3/3 = gap/9)的消耗:逆序,(1+2+3+……+8)* n / 9 = 4n
?? ?然而,第一趟預排序結束以后,就大概率無法形成最壞情況逆序了;所以第二次排序的消耗肯定小于4n,具體是多少此處省略(復變函數相關內容)
?? ?總共進行了log3(N)次這樣的預排序( +1 依舊省略)

5.選擇排序

選擇排序:每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到全部待排序的 數據元素排完 。

優化:定義一個begin,再定義一個end;每次找到最小的放在begin,找到最大的放在end;放完之后begin++,end--

	int begin = 0, end = n - 1;while (begin < end){//單趟int mini = begin, maxi = begin;for (int i = begin + 1;i <= end;i++){if (a[i] > a[maxi]) maxi = i;if (a[i] < a[mini])mini = i;}Swap(&a[begin], &a[mini]);Swap(&a[end], &a[maxi]);begin++;end--;}

循環起止條件:最大值最小值初始化時都是begin下標元素,因此無需再次進行比較;end位置元素沒被用來初始化,因此需要遍歷到end進行判斷比較大小

代碼問題:如果maxi在begin位置,mini在begin+1位置;那么在第一個交換語句后,maxi位置的值會被覆蓋為最小值;所以需要在 maxi == begin 時,把maxi用mini覆蓋掉(此時mini位置存著最大值,maxi位置存著最小值)

void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;while (begin < end){//單趟int mini = begin, maxi = begin;for (int i = begin + 1;i <= end;i++){if (a[i] > a[maxi]) maxi = i;if (a[i] < a[mini])mini = i;}Swap(&a[begin], &a[mini]);if (maxi == begin) maxi = mini;Swap(&a[end], &a[maxi]);begin++;end--;}
}

6.堆排序

詳見文章:堆復習(C語言版)

void Swap(int* a, int* b)
{int c = *a;*a = *b;//把a地址處的元素變為b位置處的元素*b = c;
}void AdjustDown(int* a, int n, int parent)
{//先假設左孩子小int child = parent * 2 + 1;//如果child >= size(n),說明已經到了葉子節點,停止循環while (child < n){//找出小的那個孩子(先得判斷有沒有右孩子,這也就是假設左孩子小,不假設右孩子小的原因)if (child + 1 < n && a[child + 1] > a[child])child++;//右孩子比較小,改為右孩子//交換or不交換位置,父節點小于小的子節點時,說明找到了該在的位置if (a[parent] >= a[child])break;else{Swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}}
}void HPSort(int* a, int n)
{//for (int i = 1; i < n; i++)//Adjustup(a, i); //向上調整建立小堆操作,時間復雜度為nlognfor (int i = (n - 1 - 1) / 2; i >= 0; i--)AdjustDown(a, n, i);//從最后一個父節點開始,依次向下調整,直到調整到根節點為止,時間復雜度為nint end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}
}

?

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

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

相關文章

程序編輯器快捷鍵總結

程序編輯器快捷鍵總結 函數跳轉 函數跳轉 Creator : F2VSCode : F12visual Studio : F12

【LUT技術專題】極小尺寸LUT算法:TinyLUT

TinyLUT: Tiny Look-Up Table for Efficient Image Restoration at the Edge&#xff08;2024 NeurIPS&#xff09; 專題介紹一、研究背景二、TinyLUT方法2.1 Separable Mapping Strategy2.2 Dynamic Discretization Mechanism 三、實驗結果四、總結 本文將從頭開始對TinyLUT: …

解決:VMware 虛擬機 Ubuntu 系統共享文件夾無法訪問問題

以下是解決 VMware 虛擬機 Ubuntu 系統共享文件夾無法訪問 問題的完整過程總結&#xff0c;按關鍵步驟和邏輯順序梳理&#xff1a; 系統版本&#xff1a;Ubuntu 22.04.5 1. 確認 VMware Tools 已安裝 驗證方法&#xff1a;通過 ps -ef | grep vmtoolsd 檢查是否存在 vmtools…

YOLOv8 的雙 Backbone 架構:解鎖目標檢測新性能

一、開篇&#xff1a;為何踏上雙 Backbone 探索之路 在目標檢測的領域中&#xff0c;YOLOv8 憑借其高效與精準脫穎而出&#xff0c;成為眾多開發者和研究者的得力工具。然而&#xff0c;傳統的單 Backbone 架構&#xff0c;盡管已經在諸多場景中表現出色&#xff0c;但仍存在一…

k8s網絡架構

Kubernetes 網絡架構的設計目標是為 Pod 提供一個高效、靈活且可擴展的網絡環境&#xff0c;同時確保 Pod 之間的通信簡單直接&#xff0c;類似于在同一個物理網絡中。以下是 Kubernetes 網絡架構的原理和核心組件的詳細解析&#xff1a; 一、Kubernetes 網絡模型的基本原則 Ku…

C++高頻面試考點 -- 智能指針

C高頻面試考點 – 智能指針 C11中引入智能指針的概念&#xff0c;方便堆內存管理。這是因為使用普通指針&#xff0c;容易造成堆內存泄漏&#xff0c;二次釋放&#xff0c;程序發生異常時內存泄漏等問題。 智能指針在C11版本之后提供&#xff0c;包含在頭文件<memory>中…

JavaScript關鍵字完全解析:從入門到精通

前言 JavaScript作為目前最流行的編程語言之一&#xff0c;擁有豐富的關鍵字體系。這些關鍵字是語言的基礎組成部分&#xff0c;理解它們的含義和用法對于掌握JavaScript至關重要。本文將詳細介紹JavaScript中的所有關鍵字&#xff0c;包括ES6的新增關鍵字&#xff0c;幫助開發…

#6 百日計劃第六天 java全棧學習

今天學的啥 上午 算法byd圖論 圖遍歷dfs bfs 沒學懂呵呵 找到兩個良心up 圖碼 labuladong 看算法還好 尚硅谷講的太淺了 那你問我 下午呢 下午 java 看了會廖雪峰的教程 回顧基礎 小林coding Java基礎八股文 還有集合的八股文 有的不是很懂 今天把Java基礎算是完…

(4)ModalAI VOXL

文章目錄 前言 4.1 購買什么 4.2 硬件設置 4.3 VOXL 攝像機配置 4.4 自動駕駛儀配置 4.4.1 使用 OpticalFlow 進行 EKF3 光源轉換 4.5 視頻 前言 本文介紹了如何將 ModalAI VOXL-CAM 與 ArduPilot 配合使用&#xff0c;以替代 GPS&#xff0c;從而實現 Loiter、PosHold…

大模型高效微調方法綜述:P-Tuning軟提示與lora低秩微調附案例代碼詳解

Prompt Tuning 和 P-Tuning 都屬于“軟提示”&#xff08;soft prompt&#xff09;范式&#xff0c;但 P-Tuning 首次提出用小型 LSTM/MLP 對提示嵌入進行編碼生成&#xff0c;而 Prompt Tuning&#xff08;又稱 Soft Prompt Tuning&#xff09;則直接對一段可訓練的嵌入序列做…

圖解深度學習 - 深度學習的工作原理

上一篇&#xff0c;我們已經知道機器學習是將輸入&#xff08;比如圖像&#xff09;映射到目標&#xff08;比如數字“4”&#xff09;的過程。這一過程是通過觀察許多輸入和目標的示例來完成的。 我們還知道&#xff0c;深度神經網絡通過一系列簡單的數據變換&#xff08;層&…

實現圖片自動壓縮算法,canvas壓縮圖片方法

背景&#xff1a; 在使用某些支持webgl的圖形庫&#xff08;eg&#xff1a;PIXI.js&#xff0c;fabric.js&#xff09;場景中&#xff0c;如果加載的紋理超過webgl可處理的最大紋理限制&#xff0c;會導致渲染的紋理缺失&#xff0c;甚至無法顯示。 方案 實現圖片自動壓縮算…

周界安全防護新突破:AI智能分析網關V4周界入侵檢測算法的技術應用

一、方案概述 在安防周界防護領域&#xff0c;傳統紅外對射、電子圍欄等防護系統弊端顯著&#xff0c;其誤報率高&#xff0c;易受飛鳥、樹枝等干擾&#xff0c;且在惡劣天氣、復雜光照下難以精準識別入侵。隨著安全需求升級&#xff0c;基于AI智能分析網關V4的周界翻越入侵檢…

解決服務器重裝之后vscode Remote-SSH無法連接的問題

在你的windows命令窗口輸入&#xff1a; ssh-keygen -R 服務器IPssh-keygen 不是內部或外部命令 .找到Git(安裝目錄)/usr/bin目錄下的ssh-keygen.exe(如果找不到&#xff0c;可以在計算機全局搜索) 2.屬性–>高級系統設置–>環境變量–>系統變量,找到Path變量&#…

leetcode 33. Search in Rotated Sorted Array

題目描述 可以發現的是&#xff0c;將數組從中間分開成左右兩部分的時候&#xff0c;一定至少有一部分的數組是有序的。左部分[left,mid-1]&#xff0c;右部分[mid1,right]。 第一種情況&#xff1a;左右兩部分都是有序的&#xff0c;說明nums[mid]就是整個數組的最大值。此時…

推薦一款滴滴團隊開源流程圖編輯框架logic-flow

LogicFlow 是一款基于 JavaScript 的流程圖編輯框架&#xff0c;提供直觀的可視化界面&#xff0c;幫助用戶輕松創建、編輯和管理復雜的工作流、業務邏輯或流程模型。其核心優勢在于低代碼化、高度可定制和強交互性&#xff0c;適用于業務系統開發、BPMN 流程設計、決策樹建模等…

java 進階 1.0.3

Thread API說明 自己滾去看文檔 CPU線程調度 每一個線程的優先使用權都是系統隨機分配的&#xff0c;人人平等 誰先分配到就誰先用 也可以耍賴&#xff0c;就是賦予某一個線程擁有之高使用權&#xff1a;優先級 這樣的操作就叫做線程調度 最基本的是系統輪流獲得 java的做法是搶…

匯川EasyPLC MODBUS-RTU通信配置和編程實現

累積流量計算(MODBUS RTU通信數據處理)數據處理相關內容。 累積流量計算(MODBUS RTU通信數據處理)_流量積算儀modbus rtu通訊-CSDN博客文章瀏覽閱讀219次。1、常用通信數據處理MODBUS通信系列之數據處理_modbus模擬的數據變化后會在原來的基礎上累加是為什么-CSDN博客MODBUS通…

【機械視覺】Halcon—【二、Halcon算子全面介紹(超詳細版)】

介紹 Halcon 的算子&#xff08;operators&#xff09;按照功能被系統性地劃分為多個類別&#xff0c;官方文檔中目前&#xff08;Halcon 22.11 版本&#xff09;共有 19 個主分類&#xff0c;每個主分類下還有若干子分類。 本人在此對這19個分類的常用核心算子進行了一系列的…

Https流式輸出一次輸出一大段,一卡一卡的-解決方案

【背景】 最近遇到一個奇怪的現象&#xff0c;前端vue&#xff0c;后端python&#xff0c;服務部署在服務器上面后&#xff0c;本來一切正常&#xff0c;但公司說要使用https訪問&#xff0c;想著也沒什么問題&#xff0c;切過去發現在沒有更改任何代碼的情況下&#xff0c;ht…