LC打怪錄 選擇排序 215.Kth Largest Element in an Array

題目鏈接:力扣

選擇排序知識

  1. 設第一個元素為比較元素,依次和后面的元素比較,比較完所有元素并找到最小元素,記錄最小元素下標,和第0個下表元素進行交換。
  2. 在未排序區域中,重復上述操作,以此類推找出剩余最小元素將它換到前面,即完成排序。

解析

現在讓我們思考一下,冒泡排序和選擇排序有什么異同?

相同點:都是兩層循環,時間復雜度都為?O(n?2?); 都只使用有限個變量,空間復雜度?O(1)。
不同點:冒泡排序在比較過程中就不斷交換;而選擇排序增加了一個變量保存最小值 / 最大值的下標,遍歷完成后才交換,減少了交換次數。
事實上,冒泡排序和選擇排序還有一個非常重要的不同點,那就是:冒泡排序法是穩定的,選擇排序法是不穩定的。

排序算法的穩定性
假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,r[i] = r[j],且 r[i] 在 r[j] 之前,而在排序后的序列中,r[i] 仍在 r[j] 之前,則稱這種排序算法是穩定的;否則稱為不穩定的。

理解了穩定性的定義后,我們就能分析出:冒泡排序中,只有左邊的數字大于右邊的數字時才會發生交換,相等的數字之間不會發生交換,所以它是穩定的。

選擇排序算法如何實現穩定排序呢

實現的方式有很多種,這里給出一種最簡單的思路:新開一個數組,將每輪找出的最小值依次添加到新數組中,選擇排序算法就變成穩定的了。

二元選擇排序

選擇排序算法也是可以優化的,既然每輪遍歷時找出了最小值,何不把最大值也順便找出來呢?這就是二元選擇排序的思想。

我們使用 minIndex 記錄最小值的下標,maxIndex 記錄最大值的下標。每次遍歷后,將最小值交換到首位,最大值交換到末尾,就完成了排序。

由于每一輪遍歷可以排好兩個數字,所以最外層的遍歷只需遍歷一半即可。

二元選擇排序中有一句很重要的代碼,它位于交換最小值和交換最大值的代碼中間:


def selectionSort(arr):for i in range(len(arr) - 1):minIndex = i  # 記錄最小元素的索引# 找出最小元素for j in range(i + 1, len(arr)):  if arr[j] < arr[minIndex]:minIndex = j# i不是最小元素時,將i和最小元素進行交換if i != minIndex:arr[i], arr[minIndex] = arr[minIndex], arr[i]return arr
if __name__=="__main__":nums = [1, 42, 65, 876, 34, 656, 4, 6757, 89, 24, 65, 42]print("start:", nums)

?方法1: bf排序

class Solution:def findKthLargest(self, nums: List[int], k: int) -> int:random.shuffle(nums)#將數組從大到小排序nums.sort(reverse=True)return nums[k-1]

執行用時:208 ms

時間復雜:O(nlogn)

方法2: 快速選擇 quick select

快排的改進,快排是一種分治思想的實現,沒做一層快排可以將數組分成兩份并確定一個數的位置。分析題目可以知道,要找到第 k 個最大的元素,找到這個元素被劃分在哪邊就可以了。

快速排序(英語:Quicksort),又稱分區交換排序(partition-exchange sort),簡稱快排,一種排序算法,最早由東尼·霍爾(Tony Hoare )提出。在平均狀況下,排序?n 個項目要??O(nlogn) 次比較。在最壞狀況下則需要?O(n?2?) 次比較,但這種狀況并不常見。事實上,快速排序Θ(nlogn) 通常明顯比其他演算法更快,因為它的內部循環(inner loop)可以在大部分的架構上很有效率地達成。

快速排序使用分治法(Divide and conquer)策略來把一個序列(list)分為較小和較大的 2 個子序列,然后遞歸地排序兩個子序列。

以「升序排列」為例,其基本步驟為 [摘自@維基百科]:

1. 挑選基準值:從數列中挑出一個元素,稱為“基準”(pivot);
2.? 分割(partition):重新排序數列,所有比基準值小的元素擺放在基準前面,所有比基準值大的元素擺在基準后面(與基準值相等的數可以到任何一邊)。在這個分割結束之后,對基準值的排序就已經完成;
3. 遞歸排序子序列:遞歸地將小于基準值元素的子序列和大于基準值元素的子序列排序。

遞歸到最底部的判斷條件是數列的大小是零或一,此時該數列顯然已經有序.

class Solution:def findKthLargest(self, nums: List[int], k: int) -> int:def partition(arr: List[int], low: int, high: int) -> int:pivot = arr[low]                                        # 選取最左邊為pivotleft, right = low, high     # 雙指針while left < right:while left<right and arr[right] >= pivot:          # 找到右邊第一個<pivot的元素right -= 1arr[left] = arr[right]                             # 并將其移動到left處while left<right and arr[left] <= pivot:           # 找到左邊第一個>pivot的元素left += 1arr[right] = arr[left]                             # 并將其移動到right處arr[left] = pivot           # pivot放置到中間left=right處return leftdef randomPartition(arr: List[int], low: int, high: int) -> int:pivot_idx = random.randint(low, high)                   # 隨機選擇pivotarr[low], arr[pivot_idx] = arr[pivot_idx], arr[low]     # pivot放置到最左邊return partition(arr, low, high)                        # 調用partition函數def topKSplit(arr: List[int], low: int, high: int, k: int) -> int:# mid = partition(arr, low, high)                   # 以mid為分割點【非隨機選擇pivot】mid = randomPartition(arr, low, high)               # 以mid為分割點【隨機選擇pivot】if mid == k-1:                                      # 第k小元素的下標為k-1return arr[mid]                                 #【找到即返回】elif mid < k-1:return topKSplit(arr, mid+1, high, k)           # 遞歸對mid右側元素進行排序else:return topKSplit(arr, low, mid-1, k)            # 遞歸對mid左側元素進行排序n = len(nums)return topKSplit(nums, 0, n-1, n-k+1)                   # 第k大元素即為第n-k+1小元素

這個代碼實現了快速選擇算法的一個變種,用來找出數組中第 k 大的元素。這個實現采用了“快速排序”的分區思想,并通過隨機選擇軸點(pivot)來提高算法的效率和避免最壞情況的發生。以下是代碼的逐步解析:

partition 函數

  • 這個函數接受一個數組 arr 和兩個指針 lowhigh 作為參數,用來確定數組的操作區間。
  • 它首先選擇 low 索引處的元素作為軸點(pivot)。
  • 使用兩個指針 leftright 從數組的兩端開始,向中間移動,并根據元素與軸點的比較結果進行交換,直到兩個指針相遇。
  • 最終,軸點元素被放置在其最終位置上,該位置左邊的所有元素都不大于軸點,右邊的所有元素都不小于軸點。
  • 函數返回軸點的最終位置

這個代碼實現了快速選擇算法的一個變種,用來找出數組中第 k 大的元素。這個實現采用了“快速排序”的分區思想,并通過隨機選擇軸點(pivot)來提高算法的效率和避免最壞情況的發生。以下是代碼的逐步解析:

randomPartition 函數

  • 為了避免在特定的數組順序下陷入最壞情況(如已排序的數組),該函數首先在 lowhigh 范圍內隨機選擇一個軸點索引 pivot_idx
  • 然后,它將選定的軸點與區間的第一個元素交換,確保隨機選擇的軸點被移到了區間的開頭。
  • 最后,調用 partition 函數執行實際的分區操作。

topKSplit 函數

  • 這個函數是快速選擇算法的核心,它遞歸地在數組的一個子區間內查找第 k 小(或第 k 大)的元素。
  • 它首先調用 randomPartition 對當前考慮的數組區間進行分區,然后根據分區后軸點的位置與 k 的關系決定下一步的操作。
  • 如果軸點恰好是第 k-1 個元素(因為數組索引從0開始),那么就找到了第 k 小的元素,直接返回。
  • 如果軸點的位置小于 k-1,說明第 k 小的元素位于軸點右側的區間內,因此對右側區間遞歸調用 topKSplit
  • 如果軸點的位置大于 k-1,說明第 k 小的元素位于軸點左側的區間內,因此對左側區間遞歸調用 topKSplit

主函數 findKthLargest

  • 最后,findKthLargest 函數通過調用 topKSplit 并傳入整個數組、起始索引 0、結束索引 n-1n-k+1(因為第 k 大元素是第 n-k+1 小元素)來找到第 k 大的元素。

3 partiton

class Solution:def findKthLargest(self, nums: List[int], k: int) -> int:def quick_select(nums, k):pivot = random.choice(nums)big, equal, small = [], [], []# 將大于、小于、等于 pivot 的元素劃分至 big, small, equal 中for num in nums:if num > pivot:big.append(num)elif num < pivot:small.append(num)else:equal.append(num)if k <= len(big):# 第 k 大元素在 big 中,遞歸劃分return quick_select(big, k)if len(nums) - len(small) < k:# 第 k 大元素在 small 中,遞歸劃分return quick_select(small, k - len(nums) + len(small))# 第 k 大元素在 equal 中,直接返回 pivotreturn pivotreturn quick_select(nums, k)
  1. 快速選擇函數 quick_select

這是一個內部定義的輔助函數,用于實現快速選擇算法。它接受當前考慮的數組 nums 和目標 k 作為參數。

2. 選擇軸點

pivot = random.choice(nums)nums 中隨機選擇一個元素作為軸點(Pivot)。這種隨機化策略有助于提高算法的平均性能,避免在特定情況下的性能退化。

3. 分區

算法遍歷數組 nums,根據元素與軸點的大小關系,將其分配到三個列表中:big(存儲所有大于軸點的元素)、equal(存儲所有等于軸點的元素)、small(存儲所有小于軸點的元素)。

4. 遞歸選擇

  • 如果 k 小于等于 big 列表的長度,說明第 k 大的元素在 big 中,因此遞歸地在 big 中尋找第 k 大的元素。
  • n-1-k+1
  • 如果 k 大于 nums 減去 small 列表長度的結果(即 k 在減去所有小于軸點的元素后仍大于 bigequal 的總長度),說明第 k 大的元素在 small 中。此時,需要在 small 中尋找新的第 k - (len(nums) - len(small)) 大的元素,因為我們已經排除了一部分更大的元素。
  • 如果上述兩種情況都不滿足,說明第 k 大的元素在 equal 中,由于 equal 中的所有元素都等于軸點值 pivot,因此直接返回 pivot

5.?返回結果

  • 最終,通過調用 quick_select(nums, k) 執行快速選擇邏輯,并返回找到的第 k 大的元素。

時間復雜度:快速選擇算法的平均時間復雜度為 O(n),但在最壞情況下可能會達到 O(n2)。

通過隨機選擇軸點,快速選擇算法能夠在大多數情況下避免最壞情況的發生,從而保持較高的效率。

精講:. - 力扣(LeetCode)

. - 力扣(LeetCode)

空間復雜度 O(logN)?:?劃分函數的平均遞歸深度為O(logN)?

方法3: 堆

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

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

相關文章

力扣每日一題 用隊列實現棧 模擬

Problem: 225. 用隊列實現棧 文章目錄 思路復雜度Code 思路 &#x1f468;?&#x1f3eb; 力扣官解 輔助隊列存棧頂元素主隊列存逆序序列 復雜度 時間復雜度: 添加時間復雜度, 示例&#xff1a; O ( n ) O(n) O(n) 空間復雜度: 添加空間復雜度, 示例&#xff1a; O ( …

js監聽網頁iframe里面元素變化其實就是監聽iframe變化

想要監聽網頁里面iframe標簽內容變化&#xff0c;需要通過監聽網頁dom元素變化&#xff0c;然后通過查詢得到iframe標簽&#xff0c;再通過iframe.contentWindow.document得到ifram內的document&#xff0c;然后再使用選擇器得到body元素&#xff0c;有了body元素&#xff0c;就…

2024年華為OD機試真題-貪吃的猴子-Python-OD統一考試(C卷)

題目描述: 一只貪吃的猴子,來到一個果園,發現許多串香蕉排成一行,每串香蕉上有若干根香蕉。每串香蕉的根數由數組numbers給出。猴子獲取香蕉,每次都只能從行的開頭或者末尾獲取,并且只能獲取N次,求猴子最多能獲取多少根香蕉。 輸入描述: 第一行為數組numbers的長度 第二…

Java和JavaScript之間的主要區別與聯系

目錄 概況 主要區別 聯系 總結 概況 Java和JavaScript&#xff0c;盡管名字相似&#xff0c;但它們在編程世界中卻扮演著截然不同的角色。Java&#xff0c;一種強類型、面向對象的編程語言&#xff0c;廣泛應用于企業級應用和安卓應用開發。它的設計理念是一次編寫&#x…

使用協程庫httpx并發請求

httpx和aiohttp都是比較常用的異步請求庫&#xff0c;當然requests多線程或requestsgevent也是不錯的選擇。 一個使用httpx進行并發請求的腳本如下&#xff1a; import functools import sys import timeimport anyio import httpxasync def fetch(client, results, index) -…

詳解 JavaScript 中的數組

詳解 JavaScript 中的數組 創建數組 注&#xff1a;在JS中的數組不要求元素的類型&#xff0c;元素類型可以一樣&#xff0c;也可以不一樣 1.使用 new 關鍵字創建 let array new Array()2.使用字面量方式創建(常用) let array1 [1,2,3,"4"]獲取數組元素 使用下…

西安-騰訊云-Python面試經驗--一面涼經

自我介紹手撕鏈表排序操作系統 a. 線程和進程區別 b. 線程安全 c. 如何保證線程安全 d. 線程崩潰&#xff0c;會不會影響所在的進程 e. 什么是守護進程&#xff0c;僵尸進程&#xff0c;孤兒進程 f. 如何產生一個守護進程 g. 如何避免僵尸進程或者孤兒進程redis a. 持久化方式有…

【STK】手把手教你利用STK進行仿真-STK軟件簡介05 STK部分第三方分析模塊介紹

1.導彈建模工具MMT 導彈建模工具MMT(Missile Modeling Tools)是STK在導彈分析領域的擴展分析應用,它是由四個獨立的應用程序組成的相互支持與關聯的系統,由第三方研究機構開發,能夠與STK基本航天分析環境進行聯合仿真分析。MMT主要用于導彈總體設計(包括彈道導彈、巡航導彈…

python進階:可迭代對象和迭代器

一、Iterable&#xff08;可迭代對象&#xff09; 1、可迭代對象&#xff1a;能夠進行迭代操作的對象。 可以理解為&#xff1a;能夠使用for循環遍歷的都是可迭代對象&#xff1b;**所有的可迭代對象&#xff0c;偶可以用內置函數iter轉換為迭代器** 2、可迭代對象包括&…

藍橋杯題練習:平地起高樓

題目要求 function convertToTree(regions, rootId "0") {// TODO: 在這里寫入具體的實現邏輯// 將平鋪的結構轉化為樹狀結構&#xff0c;并將 rootId 下的所有子節點數組返回// 如果不存在 rootId 下的子節點&#xff0c;則返回一個空數組}module.exports convert…

網絡防御保護——課堂筆記

一.內容安全 攻擊可能只是一個點&#xff0c;防御需要全方面進行 IAE引擎 DFI和DPI技術 --- 深度檢測技術 DPI ---深度包檢測技術 ---主要針對完整的數據包&#xff08;數據包分片&#xff0c;分段需要重組&#xff09;&#xff0c;之后對數據包的內容進行識別。&#xff08;應…

ifcplusplus 示例 函數中英文 對照分析以及流程圖

有需求&#xff0c;需要分析 ifc c渲染&#xff0c;分析完&#xff0c;有 230個函數&#xff0c;才能完成一個加載&#xff0c;3d加載真的是大工程&#xff01; 示例代碼流程圖 函數中英文對照表&#xff0c;方便 日后開發&#xff0c;整理思路順暢&#xff01;&#xff01;&am…

C++三級專項 digit函數

在程序中定義一函數dight(n,k),他能分離出整數n從右邊數第k個數字。 輸入 正整數n和k。 輸出 一個數字。 輸入樣例 31859 3 輸出樣例 8解析&#xff1a;遞歸&#xff0c;詳情看code. 不準直接抄&#xff01;&#xff01;&#xff01; #include <iostream> usin…

包裝類和綜合練習

包裝類 基本數據類型對應的應用類型。 jdk5以后對包裝類新增了&#xff1a;自動拆箱、自動裝箱 我們以后如何獲取包裝類對象&#xff1a; 不需要new,不需要調用方法&#xff0c;直接賦值即可 package MyApi.a09jdkdemo;public class A_01IntergerDemo1 {public static voi…

C語言——指針的進階——第1篇——(第26篇)

堅持就是勝利 文章目錄 一、字符指針1、面試題 二、指針數組三、數組指針1、數組指針的定義2、&數組名 VS 數組名3、數組指針的使用&#xff08;1&#xff09;二維數組傳參&#xff0c;形參是 二維數組 的形式&#xff08;2&#xff09;二維數組傳參&#xff0c;形參是 指針…

【RT-Thread應用筆記】英飛凌PSoC 62 + CYW43012 WiFi延遲和帶寬測試

文章目錄 一、安裝SDK二、創建項目三、編譯下載3.1 編譯代碼3.2 下載程序 四、WiFi測試4.1 掃描測試4.2 連接測試 五、延遲測試5.1 ping百度5.2 ping路由器 六、帶寬測試6.1 添加netutils軟件包6.2 iperf命令參數6.3 PC端的iperf6.4 iperf測試準備工作6.5 進行iperf帶寬測試6.6…

未來三年AI的深度發展:AIGC、視頻AI與虛擬世界構建

人工智能&#xff08;AI&#xff09;正站在科技演進的前沿&#xff0c;未來三年將見證其在多領域實現更深層次的突破。以下是對AI發展方向的深度探討以及其對各行業的深遠影響&#xff1a; 1. AIGC的演進與全面提升&#xff1a; AIGC&#xff0c;即AI通用性能力&#xff0c;將…

AI前沿-YOLOV9算法

AI前沿-YOLOV9算法 關注B站查看更多手把手教學&#xff1a; 肆十二-的個人空間-肆十二-個人主頁-嗶哩嗶哩視頻 (bilibili.com) 今天我們來一起說下最近剛出的YOLOV9算法 論文和源碼 該算法的原始論文地址為&#xff1a;https://arxiv.org/abs/2402.13616 該算法的原始代碼地…

Muduo庫編譯學習(1)

1.muduo庫簡介 muduo是由Google大佬陳碩開發&#xff0c;是一個基于非阻塞IO和事件驅動的現代C網絡庫&#xff0c;原生支持one loop per thread這種IO模型&#xff0c;該庫只支持Linux系統&#xff0c;網上大佬對其褒貶不一&#xff0c;作為小白用來學習就無可厚非了。 git倉庫…

b站小土堆pytorch學習記錄——P14 torchvision中的數據集使用

文章目錄 一、前置知識如何查看torchvision的數據集 二、代碼&#xff08;附注釋&#xff09;及運行結果 一、前置知識 如何查看torchvision的數據集 &#xff08;1&#xff09;打開官網 https://pytorch.org/ pytorch官網 &#xff08;2&#xff09;打開torchvision 在Do…