Python資料結構與演算法_第6節_排序 & 搜尋

IrisEpode發表於2020-12-02

排序(sorting algorithm)

歸併排序(merge sorting)

  • 歸併排序(merge sorting):是採用分治法的一個非常典型的應用。先遞迴分解陣列,再合併陣列。
  • 歸併排序演算法的思路:
    • 將陣列分解最小。
    • 合併兩個有序陣列,基本思路是比較兩個陣列的最前面的數,誰小就先取誰。
    • 取了後相應的指標就往後移一位。然後再比較,直至一個陣列為空,最後把另一個陣列的剩餘部分複製過來即可。
    • 重複這個步驟直至合併出來原序列長度的新序列。

歸併排序的分析

用遞迴分解序列至singleton。然後將每對相鄰singleton中大的元素放到合併出來的序列(長度為2)第一位。設定left和right遊標。
在這裡插入圖片描述
把遊標所指的元素中更小的那個放到新合併的序列(長度為4)的第一位。然後把更小的那個元素的遊標(此處為right)往後移動一位。
在這裡插入圖片描述
比較遊標新指向元素的大小,然後把更小的那個放到新合併序列(長度為4)的第二位,重複這個步驟至新序列排列好。
在這裡插入圖片描述
用同樣的方式合成新的序列(長度為8)。

歸併排序的實現

# coding:utf-8
def merge_sort(alist):
	"""歸併排序"""
	# 先拆分序列
	n = len(alist)
	# 如果序列拆分至singleton則停止recursion
	if n <= 1:
		return alist
	mid = n // 2

	# left採用歸併排序後形成的有序的新的序列
	left_li = merge_sort(alist[:mid])
	# right採用歸併排序後形成的有序的新的序列
	right_li = merge_sort(alist[mid:])
	# 將兩個有序的子序列合併為一個新的整體
	# merge(left_li, right_li)
	
	left_pointer, right_pointer = 0, 0
	result = []
	# 每次構建新合併的序列時,把上層序列跑完
	while left_pointer < len(left_li) and right_pointer < len(right_li) :
	# 小於等於,以此來使該排序演算法穩定(同大小元素,原列表左邊的排在sorted列表的左邊)
	if left_li[left_pointer] <= right_li[right_pointer]:
		result.append(left_li[left_pointer])
		left_pointer += 1
	else:
		result.append(right_li[right_pointer])
		right_pointer += 1
	# 把上層序列中剩餘的最後一個元素放到新合併序列中的最後
	result += left_li[left_pointer:]
	result += right_li[right_pointer:]
	return result

時間複雜度

  • 最優時間複雜度: O ( n ∗ log ⁡ n ) O(n*\log n) O(nlogn)
  • 最壞時間複雜度: O ( n ∗ log ⁡ n ) O(n*\log n) O(nlogn)
  • 穩定性:穩定

常見排序演算法效率比較

在這裡插入圖片描述

  • 必須掌握快速排序
  • 面試的是時候可能會問列幾個熟知的排序演算法,並且寫出來他們。

搜尋

  • 搜尋是在一個專案集合中找到一個特定專案的演算法過程。搜尋通常的答案是真的或假的,因為該專案是否存在。
  • 搜尋的幾種常見方法:順序查詢、二分法查詢、二叉樹查詢、雜湊查詢。

二分法查詢(binary searching)

  • 二分查詢/折半查詢(binary search):適用於不經常變動而查詢頻繁的有序列表。
    • 優點:優點是比較次數少,查詢速度快,平均效能好
    • 缺點:要求待查表為有序順序表(不能是連結串列),且插入刪除困難
  • 二分法查詢演算法的思路:
    • 假設表中元素是按升序排列,將表中間位置記錄的關鍵字與查詢關鍵字比較,如果兩者相等,則查詢成功.
    • 否則利用中間位置記錄將表分成前、後兩個子表,如果中間位置記錄的關鍵字大於查詢關鍵字,則進一步查詢前一子表,否則進一步查詢後一子表。
    • 重複以上過程,直到找到滿足條件的記錄,使查詢成功,或直到子表不存在為止,此時查詢不成功。
      在這裡插入圖片描述

二分查詢實現

# coding:utf-8

def binary_search_recursion(alist, item):
	"""二分查詢,遞迴版本"""
	n = len(alist)
	# 遞迴的終止條件
	if n > 0:
		mid = n // 2
		if alist[mid] == item:
			return True
		elif item < alist[mid]:
			binary_search_recursion(alist[:mid], item)
		else:
			binary_search_recursion(alist[mid+1:, item])
	return False

def binary_search_non_recursion(alist, item):
	"""二分查詢,非遞迴版本"""
	n = len(alist)
	first = 0
	last = n-1
	while first <= last:
		mid = (first + last) //2
		if alist[mid] == item:
			return True
		elif item < alist[mid]:
			last = mid - 1
		else:
			first = mid + 1
	return False

時間複雜度

  • 最優時間複雜度: O ( 1 ) O(1) O(1)
  • 最壞時間複雜度: O ( log ⁡ n ) O(\log n) O(logn)

相關文章