搜索
一、顺序查找
def search(num_list, val): # If empty if num_list == None: return -1 for i in range(0, len(num_list)): if (num_list[i] == val): return i return -1
二、折半查找(二分查找)
(1)递归
def bi_search_re(num_list, val): def bi_search(l, h): # Not found if l > h: return -1 # Check mid mid = (l + h) // 2 if (num_list[mid] == val): return mid; elif (num_list[mid] < val): return bi_search(mid + 1, h) else: return bi_search(l, mid - 1) return bi_search(0, len(num_list))
(2)迭代
def bi_search_iter(alist, item): left, right = 0, len(alist) - 1 while left <= right: mid = (left + right) // 2##注意此处建议写为 mid = left + (right - left)//2 在其他语言中可防止溢出 if alist[mid] < item: left = mid + 1 elif alist[mid] > item: right = mid - 1 else: # alist[mid] = item return mid return -1
另,写测试用例的一种方法
import unittest class TestBinarySearch1(unittest.TestCase): def setUp(self): self._f = bi_search_iter def test_empty(self): alist = [] r = self._f(alist, 5) self.assertEqual(-1, r) def test_one(self): alist = [1] r = self._f(alist, 0) self.assertEqual(-1, r) r = self._f(alist, 1) self.assertEqual(0, r) def test_two(self): alist = [1,10] r = self._f(alist, 0) self.assertEqual(-1, r) r = self._f(alist, 1) self.assertEqual(0, r) r = self._f(alist, 2) self.assertEqual(-1, r) r = self._f(alist, 10) self.assertEqual(1, r) r = self._f(alist, 11) self.assertEqual(-1, r) def test_multiple(self): alist = [1,2,3,4,5] r = self._f(alist, 5) self.assertEqual(4, r) r = self._f(alist, 4) self.assertEqual(3, r) r = self._f(alist, 2) self.assertEqual(1, r) r = self._f(alist, 1) self.assertEqual(0, r) r = self._f(alist, 6) self.assertEqual(-1, r) r = self._f(alist, 0) self.assertEqual(-1, r) def test_duplicate(self): alist = [1,1,1,2,3,3,3,3,3,3,4,5,5,5] r = self._f(alist, 5) self.assertEqual(5, alist[r]) r = self._f(alist, 4) self.assertEqual(4, alist[r]) r = self._f(alist, 2) self.assertEqual(2, alist[r]) r = self._f(alist, 3) self.assertEqual(3, alist[r]) r = self._f(alist, 1) self.assertEqual(1, alist[r]) r = self._f(alist, 6) self.assertEqual(-1, -1) r = self._f(alist, 0) self.assertEqual(-1, -1)
运行,jupyter中:
if __name__ == '__main__': unittest.main(argv=['first-arg-is-ignored'], exit=False)
排序
一、冒泡排序
def bubble_sort_mod(array): import time start = time.time() for i in range(len(array)): # n pass is_sorted = True # initialize is_sorted for j in range(1, len(array) - i): if (array[j] < array[j - 1]): # swap array[j], array[j - 1] = array[j - 1], array[j] is_sorted = False if (is_sorted): break t = time.time() - start return len(array), t
属性: 时间复杂度O(N^2) 空间复杂度O(1) 在数组有序时,时间接近O(N)
二、选择排序
def selection_sort(items): start = time.time() for i in range(len(items)): # n pos_min = i #idx for j in range(i + 1, len(items)): # n if (items[j] < items[pos_min]): pos_min = j items[i], items[pos_min] = items[pos_min], items[i] t = time.time() - start return len(items), t
属性:
三、插入排序
def insert_sort(items): start = time.time() for sort_inx in range(1,len(items)): unsort_inx = sort_inx while unsort_inx > 0 and items[unsort_inx-1] > items[unsort_inx]: items[unsort_inx-1], items[unsort_inx] = items[unsort_inx], items[unsort_inx-1] unsort_inx = unsort_inx-1 t = time.time() - start return len(items), t
属性:
当插入操作使用二分时,插入时任然需要移动数组,所以并不能带来优化。
四、希尔排序
简述:插入排序的简单扩展,通过允许相隔很远的元素交换来获得速度。
例,带间隙5,3,1
希尔排序的运行时间很大程度上取决于它使用的间隙顺序。对于实际的一些变量,它的时间复杂度的确定仍然是问题。
def shell_sort(nums): start = time.time() gap = len(nums) length = len(nums) while (gap > 0): for i in range(gap, length): for j in range(i, gap - 1, -gap): if (nums[j - gap] > nums[j]): nums[j], nums[j - gap] = nums[j - gap], nums[j] if (gap == 2): gap = 1 else: gap = gap // 2 t = time.time() - start return len(nums), t
五、计数排序
def count_sort(items): start = time.time() mmax, mmin = items[0], items[0] for i in range(1, len(items)): if (items[i] > mmax): mmax = items[i] elif (items[i] < mmin): mmin = items[i] print(mmax) nums = mmax - mmin + 1 counts = [0] * nums for i in range (len(items)): counts[items[i] - mmin] = counts[items[i] - mmin] + 1 pos = 0 for i in range(nums): for j in range(counts[i]): items[pos] = i + mmin pos += 1 t = time.time() - start return len(items), t
属性:
六、归并排序
简述:1.分:递归地拆分数组,直到它分为两对单个元素为止。然后将这些单个元素中的每一个与它的对合并,然后将这些对与他们的对等合并,直到整个列表按照排序顺序合并。
2.治:将2个排序链表合并为一个是很容易的,简单地比较两个列表的头,删除小的,添加到新排序的列表。O(N)操作
def _merge(a: list, b: list) -> list: """Merge two sorted list""" c = [] while len(a) > 0 and len(b) > 0: if a[0] < b[0]: c.append(a[0]) a.remove(a[0]) else: c.append(b[0]) b.remove(b[0]) if len(a) == 0: c += b else: c += a return c def _merge_sorted(nums: list) -> list: # Won't sort in place if len(nums) <= 1: return nums m = len(nums) // 2 a = _merge_sorted(nums[:m]) b = _merge_sorted(nums[m:]) return _merge(a, b)
属性:
改进:
1.对小型子阵列使用插入排序,可将典型合并排序执行的运行时间提高10%到15%。
2.测试数组是否已经按顺序排列:如果[mid]小于或等于[mid+1],我们可以通过添加一个测试来跳过merge的调用,从而将运行时间减少到已经按顺序排列的数组。
七、快速排序
递归写法:
def _quick_sorted(nums: list) -> list: if len(nums) <= 1: return nums pivot = nums[0] left_nums = _quick_sorted([x for x in nums[1:] if x < pivot]) right_nums = _quick_sorted([x for x in nums[1:] if x >= pivot]) return left_nums + [pivot] + right_nums
优化:三点中值算法寻找哨兵,这样做可以稍微改善分区,但是增加了计算中位数的代价。
总结: