各種“排序”的方法

文章目錄

  • 插入排序
    • 1. 直接插入排序(O(n^2))
      • 舉例1:
      • 舉例2:
      • 直插排序的"代碼"
      • 直插排序的“時間復雜度”
    • 2. 希爾排序(O(n^1.3))
        • 方法一
        • 方法二(時間復雜度更優)
  • 選擇排序
    • 堆排序
    • 直接選擇排序

我們學過冒泡排序,堆排序等等。(回顧一下:排升序,建大堆;排降序,建小堆。)

排序:所謂排序,就是使?串記錄,按照其中的某個或某些關鍵字的??,遞增或遞減的排列起來的
操作。

在生活中也會遇到很多排序:購物篩選排序(按照價格、綜合、銷量、距離等排序)、百度熱搜(根據熱搜程度)、院校排名等等。

在這里插入圖片描述

插入排序

1. 直接插入排序(O(n^2))

基本思想:將(待排序的記錄)按照(關鍵碼值的大小)逐個插入到(已經排好序的有序隊列)中。直到所有的記錄插入完為止,得到一個新的有序序列。

先將arr[end]指向:(有序隊列的最后一個數據),然后將(需要和有序數據進行比較的數據:即arr[end+1])暫時存儲在tmp中,接下來將arr[end]和tmp里的數據進行比較。
在這里插入圖片描述
注意在一次循環中,tmp的值是不變的(除非end++,才會使tmp的值改變(tmp=arr[end]))

舉例1:

【假設想要排升序】
在這里插入圖片描述

  • 現在arr[end]的值大于tmp,應該排在后面,所以現在將end的值放在end+1的位置,這個時候會發現end+1位置的值被覆蓋了,所以我們需要提前將它的值存儲在tmp中(這就是將值存儲在tmp中的原因)。接下來需要將tmp的值放在end的位置(這個是arr[end]的前面沒有其他值的情況,我們可以直接將tmp的值賦給arr[end])

在交換值之后,將end++,tmp的值為arr[end+1] (它不用修改,因為end已經修改了)
在這里插入圖片描述

  • 如果arr[end]<tmp,并不需要交換呢?(比如5和9)那就直接end++,緊接著tmp的值也變啦。

  • 緊接著又是將9和6交換,按照之前的方法(將arr[end]的值賦給arr[end+1],然后將tmp的值賦給arr[end]
    在這里插入圖片描述

  • 為什么我們還需要將交換之后的arr[end]的值和這個序列前面的值比較呢?------防止它比前面已經排好序的值小

接下來按照前面的方法,再將9和2進行比較

舉例2:

在這里插入圖片描述

直插排序的"代碼"

我們需要一個循環來控制end++,往后走;
還需要一個循環來控制:當把arr[end]賦值給arr[end+1]之后,需要將end- -,將前面的數值和tmp繼續比較。

在這里插入代碼片`void InsertSort(int* arr, int n)
{for (int i = 0; i < n - 1; i++)  //為什么是i<n-1?{int end = i;int tmp = arr[end + 1];while (end >= 0){if (arr[end] > tmp){arr[end + 1] = arr[end];end--;}else{break;}}arr[end + 1] = tmp;}
}`

在這里插入圖片描述

  • 為什么是i<n-1呢?
    因為tmp存儲的是arr[end+1],我們要確保它是最后一個不會越界,即end+1<n,又因為end=i,所以i+1<n,所以i<n-1

直插排序的“時間復雜度”

直接插入排序的時間復雜度是O(n^2)

不過O(n^2)是最差的情況。
在這里插入圖片描述

最差:想將降序----->升序 O(n^2)
最好:升序------->升序 O(n)

2. 希爾排序(O(n^1.3))

希爾排序就是對直接插入排序的一種優化。想要改善降序---->升序的時間復雜度。

希爾排序分為兩步:1.預排序 2.直接插入排序

我們可以將很長的一段數組序列變為n段較短的序列,然后對每段“單獨”進行直接插入排序(這個叫做預排序)。預排結束之后,小的數據大部分都在前半部分了(只是不按順序),接著我們再對整體的數組(已經趨于有序的數組)序列進行一次直插排序,這樣不僅完成了排序,還優化了時間復雜度。

方法一
  • 首先我們需要決定一個值:gap,然后將大段分為n個小段(每段的首和尾相隔gap個元素,即第一個值往后數gap個,那個數據是這段的最后一個元素)。

什么情況下預排:當gap>1的時候
什么情況下整段直插:當gap==1的時候

在這里插入圖片描述

  1. 從上圖可以知曉:要將一大段數據分為很多組數據,在尋找第二組數據時,需要將上次的首元素下標++。這一步由外層循環控制【遍歷每個子序列的起始位置(從 0 到 gap-1)】for(i=0;i<gap;i++)(為什么i<gap?)

  2. 接下來是每組數據中,知道了首元素,然后找這組數據中的剩余數據,即end+gap,這個由第二層循環控制:for(int j=0;j<n-gap;j+=gap);(為什么內層的j<n-gap?)

  3. 再里面的元素之間比較就沒有什么特別了,使用的是直接插入排序(它也有循環)。

【gap不能太大(若太大的話,會導致每組的數據比較少,但組數比較多),也不能太小(那樣的話每組數據會有很多,那這樣的話時間復雜度又變高了)】我們可以讓gap由元素個數決定,gap=n/3+1,為什么最后還要+1呢?(如果遇到2/3的情況,那商就是0,也就是gap=0,那間距為0,誰和誰比呢?)而我們最后需要讓gap=1,讓已經預排之后的整段數據進行直插排序,所以需要+1.

【gap等于多少,這段數據最終就會被分為幾組數據】如果n特別特別大,n/3之后也特大,先將+1給忽略掉

  • 之前我們說到為什么外層循環中i<gap而不是i<n呢?
    在這里插入圖片描述
    我們第一組數據的第二個元素是arr[end+gap]也就是arr[0+gap],既然arr[gap]在第一組數據中已經進行比較過一次,那之后就沒必要再將它比較一次了。大家觀察上圖會發現每個數據都進行比較過,如果外層用 i < n,會導致重復處理同一子序列,效率降低。

  • 還有一個問題是:為什么第二層循環需要j<n-gap,按照方法一個組中,每個數據它們都相鄰gap個(n=8,gap=3第一組元素下標:0,3,6,下標為6的元素后面還有1個元素,但是剩余元素個數不夠gap個,那就只能在這次的循環中將后面的元素舍棄。所以在找每組的數據時(即在第二層循環),循環結束的條件是最后一個元素后面的元素個數小于gap個(有剩余的元素或者剩余個數為0(也就是剛好后面沒有元素了)),也就是它的下標小于n-gap

在這里插入圖片描述

第一組的數據:7,4,1
第二組的數據:6,3
第三組的數據:5,2

  • 然后,先排第一組的數據,再排2,3組的數據

(1)初始情況:arr[end]指向第一個元素,tmp指向(與第一個相隔gap的元素).即tmp=arr[end+gap]

  • arr[end]>tmp,那么將arr[end]的值賦給tmp所指向的那個值,即arr[end+gap]=arr[end],賦值之后,將end=end-gap
  • arr[end]>tmp,不用修改值。直接將end+=gap
    對于每組數據的值比較的注釋
    (2)排完第二組的數據之后,外層的i++,到第二個數據【下標為1】(它作為第二組的首元素),然后進入第二層循環,在進行比較。

到此為止,這個進行了一趟比較

void ShellSort(int* arr, int n)
{int gap = n / 3 + 1;for (int i = 0; i < gap; i++){int j = i;for (j =i; j < n - gap; j += gap){int end = j;int tmp = arr[end + gap];while (end>=0)  //將這組數據的每個數據比較,while結束的條件:end>=0;{if (arr[end] > tmp){arr[end + gap] = arr[end];//想繼續和前面有序的數據比較,將end-gapend -= gap;}//到這里,arr[end]<tmp,那就不用賦值,直接將下標+gap,tmp的值隨之改變,繼續比較else {break;}}//在循環中,我們只是將大的數據往后賦值,那當初那個比較小的tmp賦給誰呢?//這組數據如何能比較結束(break)呢?肯定是此時的arr[end]并沒有tmp大,不需要交換了//所以,我們把tmp值賦值給之前一個比較的值(由于是end-=gap,所以上一個是end+gap)arr[end + gap] = tmp;}}
}

在這里插入圖片描述

在這里插入圖片描述

之后我們將gap不斷縮小,繼續比較,直至最后gap==1時,進行最后的,對整段進行直插
在這里插入圖片描述

void ShellSort(int* arr, int n)
{int gap = n;while (gap>1){gap = gap / 3 + 1;for (int i = 0; i < gap; i++){int j = i;for (j = i; j < n - gap; j += gap){int end = j;int tmp = arr[end + gap];while (end >= 0)  //將這組數據的每個數據比較,while結束的條件:end>=0;{printf("%d ", gap);if (arr[end] > tmp){arr[end + gap] = arr[end];//想繼續和前面有序的數據比較,將end-gapend -= gap;}//到這里,arr[end]<tmp,那就不用賦值,直接將下標+gap,tmp的值隨之改變,繼續比較else {break;}}//在循環中,我們只是將大的數據往后賦值,那當初那個比較小的tmp賦給誰呢?//這組數據如何能比較結束(break)呢?肯定是此時的arr[end]并沒有tmp大,不需要交換了//所以,我們把tmp值賦值給之前一個比較的值(由于是end-=gap,所以上一個是end+gap)arr[end + gap] = tmp;}}}
}

注意:為什么gap>1還可以對整段進行直插排序呢?
大家先想一想gap==1的條件:肯定是上一個gap>1因而進入了循環,進行了gap=gap/3+1,而gap/3=0,gap/3+1=1,接下來進行了直插。不會有其他意外。

n=10:
第一次,gap=n=10,大于1,進入循環之后,gap=gap/3+1=4,之后進行排序。
第二次,gap=4,大于1,進入循環,gap=gap/3+1=4/3+1=2,之后進行排序
注意gap=2的時候它仍然大于1 ,進入循環了,然后才進行的gap=gap/3+1,得到了gap=1,進行最后的直插

方法二(時間復雜度更優)

在上面的基礎上進行優化:
之前是將一組一組比,現在仍然是相隔gap個的兩個元素進行比較,但是當它倆比較完之后,我們不用找arr[0+gap]再隔gap的值。我們直接找arr[0]的下一個:arr[1],再找arr[1+gap],將他倆比較。那到什么時候停止呢?當i<n-gap時。
在這里插入圖片描述

void ShellSort(int* arr, int n)
{int gap = n;while (gap>1){gap = gap / 3 + 1;for (int i=0; i < n - gap; i++){int end = i;int tmp = arr[end + gap];while (end >= 0)  //將這組數據的每個數據比較,while結束的條件:end>=0;{if (arr[end] > tmp){arr[end + gap] = arr[end];//想繼續和前面有序的數據比較,將end-gapend -= gap;}//到這里,arr[end]<tmp,那就不用賦值,直接將下標+gap,tmp的值隨之改變,繼續比較else {break;}}//在循環中,我們只是將大的數據往后賦值,那當初那個比較小的tmp賦給誰呢?//這組數據如何能比較結束(break)呢?肯定是此時的arr[end]并沒有tmp大,不需要交換了//所以,我們把tmp值賦值給之前一個比較的值(由于是end-=gap,所以上一個是end+gap)arr[end + gap] = tmp;}}
}

在這里插入圖片描述
希爾排序的時間復雜度是:O(n^1.3)
在這里插入圖片描述

選擇排序

堆排序

這是堆排序的文章,點擊鏈接即可跳轉

直接選擇排序

這個名字中的“選擇”已經暴露了它的方法,直接選擇排序是從整個數組中(arr[0]到arr[n-1])選出來min或者max的元素,然后放在整個數組的第一個位置。然后再從arr[1]到arr[n-1]選出來min或max,放在第二個位置。【將min/max放在第一個位置,那原本的數據怎么辦,所以不是直接將這個值放在第一個位置,而是將兩個值交換】

  • 思路:

    1. 先設定兩個變量,begin和mini(它倆是下標),begin作為min或者max放置的位置的下標。mini作為遍歷數組時此時位置的下標。[需要知道跳出循環的條件,即放的位置<n]
      在這里插入圖片描述
  • 但是又一個bug是:每找一次最大/最小值,就需要將數組遍歷一遍(和冒泡排序一樣費時間O(n^2)),時間復雜度很大,我們進行優化。

在遍歷一遍數組的時候,我們可以將min和max同時找出來,將它們放在數組的兩端。這樣就可以省略一半的時間。【但需要注意的是,這個上面那個的循環結束條件就不一樣了,我們不用再遍歷后半部分,就是已經放好特定值的那部分】

void SelectSort(int* arr, int n)
{int begin=0;for (begin = 0; begin < n/2; begin++) //為什么begin<n/2呢?因為begin是前半部分的下標,只用占整個數組的一半{int maxi = begin;int mini = begin;//確定了這趟mini要放的位置,接下來這個循環用來遍歷找最小值/最大值,用j來遍歷int j = begin;for (j = begin+1; j < n-begin; j++)  //為什么j<n-begin//第一遍遍歷的時候,需要遍歷所有的//第二次遍歷,就不用遍歷最后一個,已經放了特定的值//第三次遍歷,倒數兩個都不用遍歷了{if (arr[j] < arr[mini])mini=j;if (arr[j] > arr[maxi])maxi=j;}//這一步是什么作用?if (maxi == begin){maxi = mini;}Swap(&arr[begin], &arr[mini]);Swap(&arr[n - 1 - begin], &arr[maxi]);}
}

其中這一步的作用是什么呢?

if (maxi == begin)
{maxi = mini;
}

舉個例子:
在這里插入圖片描述

這個的問題就在于:maxi剛好和begin重合了,而arr[begin]先一步和arr[mini]交換了,所以我們需要在交換之前處理一下:如果它倆重合了,那就先讓maxi走到mini的位置。
在這里插入圖片描述

if(maxi==begin)maxi=mini;

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

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

相關文章

【Linux網絡與網絡編程】08.傳輸層協議 UDP

傳輸層協議負責將數據從發送端傳輸到接收端。 一、再談端口號 端口號標識了一個主機上進行通信的不同的應用程序。在 TCP/IP 協議中&#xff0c;用 "源IP"&#xff0c;"源端口號"&#xff0c;"目的 IP"&#xff0c;"目的端口號"&…

python求π近似值

【問題描述】用公式π/4≈1-1/31/5-1/7..1/(2*N-1).求圓周率PI的近似值。 從鍵盤輸入一個整數N值&#xff0c;利用上述公式計算出π的近似值&#xff0c;然后輸出π值&#xff0c;保留小數后8位。 【樣例輸入】1000 【樣例輸出】3.14059265 def countpi(N):p0040nowid0for i i…

第十六屆藍橋杯省賽JavaB組題解

A 逃離高塔 第一道填空題很簡單&#xff0c;根據題意跑一邊循環即可&#xff0c;一共是202個符合條件的數 public static void main(String[] args) {Scanner scanner new Scanner(System.in);int ans0;for(long i0;i<2025;i){if((i*i*i)%103)ans;}System.out.println(ans)…

汽車車窗升降系統全生命周期耐久性驗證方案研究

隨著汽車行業的快速發展&#xff0c;消費者對于汽車品質和安全性的要求日益提高。汽車車窗升降系統作為汽車電子系統中的重要組成部分&#xff0c;其可靠性和耐久性直接影響到用戶的使用體驗和行車安全。車窗升降系統在日常使用中頻繁操作&#xff0c;承受著各種復雜的工況&…

嵌入式Linux——8 串口

目錄 1.終端&#xff08;tty&#xff09; /dev/tty*&#xff1a;物理/虛擬終端 /dev/pts/*&#xff1a;偽終端 /dev/tty&#xff1a;當前進程的控制終端 /dev/tty0&#xff1a;當前活動的虛擬控制臺 2.行規程模式&#xff08;line discipline&#xff09; 比較行規程和原…

Docker日志查看與資源監控指令全解:從基礎到高階運維實踐

Docker日志查看與資源監控指令全解&#xff1a;從基礎到高階運維實踐 一、日志管理&#xff1a;穿透容器內部的眼睛1.1 基礎日志操作核心命令&#xff1a;docker logs日志驅動配置 1.2 高級日志處理JSON日志解析多容器日志聚合 二、資源監控&#xff1a;掌握容器生命體征2.1 實…

初學STM32之編碼器測速以及測頻法的實現

資料來著江協科技 這篇是編碼器測速&#xff0c;江科大的源碼在測速的時候&#xff0c;定時器TIM2是一直在跑的&#xff0c;不受其它控的&#xff0c;它就一直隔1S讀一次CNT的值。它也不管是否有輸入信號。源碼程序修改一下是可以實現對PWM信號以測頻法的方式讀取。 筆者稍微改…

oracle怎么查看是否走了索引

SELECT * FROM CRM_STATION_APPEAL_RESULT WHERE COMPLAINT_ID ce1a1d8f-e2a2-4126-8cb7-14384cb24468; 這是查詢語句&#xff0c;怎么看這個查詢是否走了索引呢 EXPLAIN PLAN FOR SELECT * FROM CRM_STATION_APPEAL_RESULT WHERE COMPLAINT_ID ce1a1d8f-e2a2-4126-8cb7-14…

C++進階——C++11_{ }初始化_lambda_包裝器

目錄 1、{ }初始化 1.1 C98的{ } 1.2 C11的{ } 1.3 C11中的std::initializer_list 總結一下&#xff1a; 2、lambda 2.1 lambda的語法 2.2 捕捉列表 2.3 lambda的應用 2.4 lambda的原理 3、包裝器 3.1 function 3.2 bind 1、{ }初始化 1.1 C98的{ } C98中一般數組…

【微知】Mellanox網卡網線插入后驅動的幾個日志?(Cable plugged;IPv6 ... link becomes ready)

概要 本文是一個簡單的信息記錄。記錄的是當服務器網卡的光模塊插入后內核的日志打印。通過這種日志打印&#xff0c;可以在定位分析問題的時候&#xff0c;知道進行過一次模塊插拔。 日志 截圖版&#xff1a; 文字版&#xff1a; [32704.121294] mlx5_core 0000:01:00.0…

單片機Day05---靜態數碼管

目錄 一、原理圖&#xff1a;?編輯 二、思路梳理&#xff1a; 三&#xff1a;一些說明&#xff1a; 1.點亮方式&#xff1a; 2.數組&#xff1a; 3.數字與段碼對應&#xff1a; 四&#xff1a;程序實現&#xff1a; 一、原理圖&#xff1a; 二、思路梳理&#xff1a; …

Cesium.js(6):Cesium相機系統

Camera表示觀察場景的視角。通過操作攝像機&#xff0c;可以控制視圖的位置、方向和角度。 幫助文檔&#xff1a;Camera - Cesium Documentation 1 setView setView 方法允許你指定相機的目標位置和姿態。你可以通過 Cartesian3 對象來指定目標位置&#xff0c;并通過 orien…

【Python技術生態全景:十大核心應用領域深度解析】

目錄 前言&#xff1a;Python的統治力一、基礎應用領域1. Web開發數據科學 二、前沿技術領域機器學習深度學習 三、行業解決方案量化金融生物信息 四、創新應用方向物聯網開發區塊鏈開發 五、效率工具生態自動化運維游戲開發 結語&#xff1a;Python的邊界與突破技術局限未來演…

leetcode 2787. Ways to Express an Integer as Sum of Powers

題目描述 這道題是0-1背包問題。可以理解為&#xff0c;有一個最大容量是n的背包&#xff0c;有n個物品&#xff0c;第i個物品的重量是i^x&#xff0c;問裝滿背包有多少種裝法。題目要求必須是互不相同的數的x次冪的和等于n&#xff0c;那就表示每個數只能用一次&#xff0c;也…

面試經驗分享 | 成都滲透測試工程師二面面經分享

可以看看我的置頂文章和專欄找我哦 概況 面試過程 面試官的問題 問題1、你覺得當前OAuth2.0下的攻擊手段有哪些&#xff1f;結合具體案例詳細講講 問題2、php/java反序列化漏洞的原理?程序員/運維如何避免此類漏洞或如何防御? 問題3、如果一臺服務器被入侵后,你會如何做應急…

模仿axios的封裝效果來封裝fetch,實現baseurl超時等

因為要在cocos游戲項目里面發送網絡請求獲取數據&#xff0c;并且還有可能用到websocket發送請求&#xff0c;所以這里封裝一個fetch放便使用&#xff1a; // fetch封裝// 基礎配置 const BASE_URL 你的url const TIMEOUT 5000// 請求封裝 const http async (url: string, …

小米運維面試題及參考答案(80道面試題)

請講解一下 linux top 后進程的狀態 在 Linux 系統中,使用top命令可以查看系統中正在運行的進程的相關信息,進程通常有以下幾種狀態: 運行(R):表示進程正在 CPU 上運行或者正在運行隊列中等待運行。處于運行狀態的進程正在積極地使用 CPU 資源來執行其任務。睡眠(S):進…

a sort.py demo

這份代碼展示了如何使用 sort.py。注意&#xff0c;此處&#xff0c;我將文件名改為 my_sort.py。 你并不能直接 copy 使用&#xff0c;因為環境&#xff0c;包&#xff0c;還有模型。 此處使用 SSD-MobileNetv2 進行物體檢測&#xff0c;將結果傳入以 np 數組的形式傳入sort…

使用Redis解決:集群的Session共享問題

使用Redis解決&#xff1a;集群的Session共享問題 session共享問題&#xff1a;多臺Tomcat并不共享session存儲空間&#xff0c;當請求切換到不同的tomcat服務時導致數據丟失的問題。 問題背景 ?無狀態HTTP協議&#xff1a;HTTP協議本身是無狀態的&#xff0c;服務器無法直接識…

Linux 內核知識體系[1]

1 Linux內核知識體系 2.Linux內核學習路線 2.1基礎知識準備 操作系統基礎&#xff1a;了解操作系統的概念和基本原理&#xff0c;包括進程管理、內存管理、文件系統、輸入輸出等。 書籍&#xff1a;《操作系統&#xff1a;設計與實現》&#xff08;Andrew S. Tanenbaum&…