搜索与排序

搜索

一、顺序查找


 

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

另:

def partition(nums):
    l,r = 0,len(nums)-1
    if len(nums)<=1:
        return nums
    low = l
    while l < r:
        if nums[l] < nums[r]:
            nums[l], nums[low] = nums[low], nums[l]
            low += 1
        l += 1
    nums[low], nums[r] = nums[r], nums[low]
    left = partition(nums[:low])
    right = partition(nums[low+1:])
    return left + [nums[low]] +right

优化:三点中值算法寻找哨兵,这样做可以稍微改善分区,但是增加了计算中位数的代价。

总结:

 

 python的多级排序:

#默认的sort函数会先对第一个比较,如果第一个相等再比较第二个
print(sorted(list_num))
//OUTPUT:[[8, 34], [12, 3], [12, 45], [18, 10], [18, 10], [18, 34]]
#先用第二个数排序,若相等再用第一个数排序
print(list_num, key = lambda x:(int(x[0]),int(x[1])))

//output:[[12, 3], [18, 10], [18, 10], [8, 34], [18, 34], [12, 45]]

 

posted @ 2019-11-22 18:25  oldby  阅读(290)  评论(0编辑  收藏  举报