搜索
一、顺序查找

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
优化:三点中值算法寻找哨兵,这样做可以稍微改善分区,但是增加了计算中位数的代价。
总结:
