成为一个合格程序员所必备的三种常见LeetCode排序算法
排序算法是一種通過特定的算法因式將一組或多組數據按照既定模式進行重新排序的方法。通過排序,我們可以得到一個新的序列,該序列遵循一定的規則并展現出一定的規律。經過排序處理后的數據可以更方便地進行篩選和計算,從而大大提高了計算效率。因此,掌握排序算法是每個程序員的基本功之一。
今天我們將詳細講解一些與冒泡排序、快速排序和插入排序相關的leetcode真題。
冒泡排序
字如其名,冒泡排序是一種算法,它類似于水中的泡泡逐漸上升,通過逐輪比較和交換,最終使每個元素按照順序排列。
看一下今天的題目:給定一個數組 nums,編寫一個函數將所有 0 移動到數組的末尾,同時保持非零元素的相對順序。請注意 ,必須在不復制數組的情況下原地對數組進行操作。進階:你能盡量減少完成的操作次數嗎?
示例 1:
輸入: nums = [0,1,0,3,12]
輸出: [1,3,12,0,0]
首先,這道題屬于簡單題目,一眼基本就可以看出來如何實現。只要遇到零就往后移動,并且冒泡排序最常見的就是雙層for循環即可,然后維護兩個變量隨時進行數據交換。但是這種都知道的解決方案,我們不去實現。我們來實現一下進階要求,即盡可能減少操作次數來完成數據的轉換。
我們可以考慮一下,因為nums數組中不一定只有一個零,所以在操作數據時,我們將所有零都看作一個整體,只移動第一個零即可,其他的零都不用動。下面是圖解:
我們在這里寫一下相關的Python實現代碼。在這段代碼中,我們使用了三元表達式的一種變種:(假,真)[條件]
from typing import List
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
i = 0
j = 0
while j < len(nums) :
if nums[j] == 0 :
i = (j,i)[nums[i] == 0]
elif nums[i] == 0 :
nums[i] = nums[j]
nums[j] = 0
i = i + 1
j = j + 1
solution = Solution()
nums = [0,1,0,3,12]
solution.moveZeroes(nums)
print(nums)
快速排序
快速排序的重要之處在于選擇合適的標準數字,通過將大于和小于該數字的元素分成兩部分。隨后,我們不斷重復這個操作。通常情況下,我傾向于使用遞歸方法,每次分割完后再調用以基準數字為標準的方法,直到只剩下一個元素為止。在今天的例題中,我們將探討快速排序的應用。
給定整數數組 nums 和整數 k,請返回數組中第 k 個最大的元素。請注意,你需要找的是數組排序后的第 k 個最大的元素,而不是第 k 個不同的元素。你必須設計并實現時間復雜度為 O(n) 的算法解決此問題。
通常情況下,快速排序的時間復雜度是無法達到O(n)的,而且在最壞情況下可能會達到O(n2)的時間復雜度。因此,為了滿足題目要求,我們需要對選擇排序進行一些改進。
我們先來看下簡單實現:
from typing import List
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
return sorted(nums)[len(nums) - k]
solution = Solution()
nums = [3,2,3,1,2,4,5,5,6]
k = 4
print(solution.findKthLargest(nums, k))
然而,這樣的實現肯定無法滿足O(n)的時間復雜度要求。因此,我們需要進行進一步優化。如果我第一反應是降低時間復雜度,我可能會考慮犧牲空間復雜度,然后逐步進行優化。根據這個,我寫出來了遞歸。
from typing import List
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
def quickSelect(nums,k) -> int:
# 選擇一個作為基準
result = nums[0]
# 將數組分為三部分,大于、等于、小于
big ,equal,small = [],[],[]
# for循環不要排序,一進行排序就會增加時間復雜度。
for num in nums:
if num > result:
big.append(num)
elif num < result:
small.append(num)
else :
equal.append(num)
# 說明在big中
if k <= len(big):
return quickSelect(big,k)
if k > len(big) + len(equal):
return quickSelect(small,k - len(big) - len(equal))
return result
return quickSelect(nums,k)
solution = Solution()
nums = [3,2,3,1,2,4,5,5,6]
k = 4
print(solution.findKthLargest(nums, k))
為了更加方便大家理解,我還特意制作了一個簡單的圖例,以便于更直觀地說明。
插入排序
插入排序(Insertion Sort)是一種簡單直觀的排序算法。其基本思想是將待排序序列分為已排序和未排序兩部分。每次從未排序部分取出一個元素,將其插入到已排序部分的合適位置,直到所有元素都被插入到已排序部分為止。這種排序方法相對其他復雜的排序算法而言,實現簡單、容易理解,適用于小規模數據的排序。
我們來看下正題:
給你一個整數數組 nums,請你將該數組升序排列。
輸入:nums = [5,1,1,2,0,0]
輸出:[0,0,1,1,2,5]
我們先來簡單實現一個插入排序:
from typing import List
class Solution:
def insertSort(self,index: int,nums: List[int]):
temp = nums[index]
while index > 0 and temp < nums[index-1] :
nums[index] = nums[index-1]
index = index - 1
nums[index] = temp
def sortArray(self, nums: List[int]) -> List[int]:
for i in range(1,len(nums)):
self.insertSort(i,nums)
return nums
insert = Solution();
nums = [5,1,1,2,0,0]
print(insert.sortArray(nums))
為了更好地說明,我簡單地繪制了一個圖例。請注意,數字的順序應根據實際情況而定,而不是根據圖中的顯示順序來確定。圖中主要展示了交換和比較的過程。
然而,插入排序明顯不是最優的算法,因為它的運行時間超過了限制。主要原因是插入排序的時間復雜度仍然偏高。那么,我來提出一種簡單的優化方法,主要是減少比較和交換操作的消耗。我們知道,如果數組的前面部分已經是有序的,那么我們可以首先考慮使用二分查找來減少比較次數。我們來實現一下:
from typing import List
class Solution:
def findIndex(self,begin:int, end: int,temp: int,nums: List[int]):
if begin <= end :
return begin
mid = (begin + end ) / 2
if nums[mid] > temp:
findIndex(begin,mid,temp,nums)
else :
findIndex(mid,end,temp,nums)
def insertSort2(self,index: int,nums: List[int]):
temp = nums[index]
small = self.findIndex(0,index,temp,nums)
while index > small and temp < nums[index-1] :
nums[index] = nums[index-1]
index = index - 1
nums[index] = temp
def sortArray(self, nums: List[int]) -> List[int]:
for i in range(1,len(nums)):
self.insertSort2(i,nums)
return nums
insert = Solution();
nums = [5,1,1,2,0,0]
print(insert.sortArray(nums))
本來我覺得這次的任務應該能夠順利通過,但是卻沒能滿足時間限制要求,結果還是超時了。接下來,為了優化交換次數,我需要考慮使用插入排序的高級變體——希爾排序。
希爾排序
希爾排序是一種優化的插入排序算法,它的重點是通過增加間隔來減少元素之間的比較次數。相比于傳統的插入排序一次比較一個元素,希爾排序通過間隔的設定,可以一次比較多個元素。為了更好地理解這個過程,我簡單地畫了一張圖。
如果采用之前的插入排序算法,將數字0移動到前面將需要進行多次比較和交換操作,這將導致效率較低。而如果使用希爾排序并增加間隔,可以避免對中間數字進行比較和交換操作,從而有效減少了所需的比較和交換次數。
我們來簡單實現一下算法:
from typing import List
class Solution:
def insertSort3(self,index: int,nums: List[int],gap: int):
temp = nums[index]
while index-gap >= 0 and temp < nums[index-gap] :
nums[index] = nums[index-gap]
index = index - gap
nums[index] = temp
def sortArray(self, nums: List[int]) -> List[int]:
gap = len(nums) // 2
while gap > 1 :
for i in range(gap,len(nums)):
self.insertSort3(i,nums,gap)
gap = gap // 2
return nums
insert = Solution();
nums = [5,1,1,2,0,0]
print(insert.sortArray(nums))
終于通過了這道題目,從代碼實現上來看,與插入排序相比,它多了一些變量,但仍然很容易理解。然而,由于數組前面不再是絕對有序的,我們需要放棄使用二分查找。
總結
以上是生活随笔為你收集整理的成为一个合格程序员所必备的三种常见LeetCode排序算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python飞船游戏(四)
- 下一篇: 操作suspect_pages 表