算法图解 读书笔记(上)

最近看了一本算法入门书——算法图解。

封面的插画很好玩儿。最吸引我的还是封面里的一句话:向小说一样有趣的算法入门书。

上个封面,大家感受一下:

算法图解 读书笔记(上)_第1张图片

一、算法简介

1.1 二分查找 

一个有序数组中找一个数的位置(对应该数字所在数组下标index)

def binary_search(list, item):
    low = 0
    high = len(list) - 1
    while low <= high:
        mid = int((low + high) / 2)
        guess = list[mid]
        if guess == item:
            return mid
        if guess > item:
            high = mid - 1
        else:
            low = mid + 1
    return None
 
 
my_list = [1, 3, 5, 7, 9]
 
print(binary_search(my_list, 3)) # => 1
print(binary_search(my_list, -1)) # => None

 

  • 也可用递归实现
  • 操作对象:数组
  • 使用前提:有序的数组
  • 性能方面:时间复杂度O(logn)

1.2 旅行商问题

旅行商前往n个城市,确保旅程最短。求可能的排序:n!种可能。

 

二、选择排序

2.1 数组和链表

  • 数组:连续存储在硬盘中;
  • 链表:分散存储在硬盘中;

2.2 选择排序

将数组元素按照从小到大的顺序排序,每次从数组中取出最小值

def findSmallest(arr):
    smallest = arr[0]
    smallest_index = 0
    for i in range(1, len(arr)):
        if arr[i] < smallest:
            smallest = arr[i]
            smallest_index = i
    return smallest_index
 
def selectionSort(arr):
    newArr = []
    for i in range(len(arr)):
        smallest = findSmallest(arr)
        newArr.append(arr.pop(smallest))
    return newArr
 
 
print(selectionSort([5, 3, 6, 2, 10])) #[2, 3, 5, 6, 10]

三、递归   一种优雅的问题解决方法

适用递归的算法要满足:

  • 基限条件(即返回的条件)
  • 递归条件(调用递归函数)

特点:

  • 自己调用自己,调用栈在内存叠加,如果没有返回条件,将无限循环调用,占用大量内存,最终爆栈终止进程。

还有一种高级一点的递归:

  • 尾递归 (将结果也放入函数参数,内存里面调用栈只有一个当前运行的函数进程)

举个简单的例子: 阶乘f(n) = n!

#递归
def fact(x): 
    if x == 1:
        return 1
    else:
        return x * fact(x-1)  #注意这里跟尾递归不同

#尾递归
def factorial(x,result):  
    if x == 1:
        return result
    else:
        return factorial(x-1,x*result)
 
 
if __name__ == '__main__':
    print(fact(5)) #5*4*3*2*1 = 120
    print(factorial(5,1)) #120

四、快速排序 (分而治之策略)

快速排序

def quicksort(array):
    if len(array) < 2:
        return array
    else:
        pivot = array[0]

        less = [i for i in array[1:] if i <= pivot]
        print(less)

        greater = [i for i in array[1:] if i > pivot]
        print(greater)
    return quicksort(less) + [pivot] + quicksort(greater)

if __name__ == '__main__':
    print(quicksort([7,1,10,5,3,2,6]))
  • 每次选取数组中一个元素x当作分水岭
  • [小于元素x的数组] + [x] + [大于元素x的数组]
  • 然后递归调用,直到最后处理的数组元素只剩下零个或者一个
  • 平均时间复杂度O(nlogn)
  • 最差情况时间复杂度O(n^2)   
  • 出现最差情况是:快排的数组本来就是有序的(顺序/倒序),选取的元素又是开头第一个的话,每次变成只能处理一侧的数组。 改善:可以选取数组中间的元素当作分水岭pivot,只有两边的元素就都能均匀处理了。

归并排序

def mergeSort(array):
    if len(array) < 2:
        return array
    else:
        mid = int(len(array)/2)
        left = mergeSort(array[:mid])
        right = mergeSort(array[mid:])
        return merge(left, right)
 
def merge(left, right): #并两个已排序好的列表,产生一个新的已排序好的列表
    result = []  # 新的已排序好的列表
    i = 0  # 下标
    j = 0
    # 对两个列表中的元素 两两对比
    # 将最小的元素,放到result中,并对当前列表下标加1
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    # 此时left或者right其中一个已经添加完毕,剩下的就全部加到result后面即可      
    result += left[i:] 
    result += right[j:]
    return result
 
array = [9,5,3,0,6,2,7,1,4,8]
result = mergeSort(array)
print('排序后:',result) #排序后: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 归并排序时间复杂度是O(nlogn)

  • 最坏情况也是O(nlogn)

  • 归并排序采用先分后总的方式,先按中间分,分到数组最后只有一个元素为止,最后两两合并。有点mapReduce的味道。

冒泡排序

#冒泡排序,每次寻找最小到元素往前排,就像汽水从下往上冒一样。所以叫冒泡排序
def simpleSort(array):
    for i in range(len(array)-1):
        for j in range(i,len(array)):
            if array[i] > array[j]:
                temp = array[i]
                array[i] = array[j]
                array[j] = temp
    return array

print(simpleSort([9,8,6,7,4,5,3,11,2]))
  • 冒泡排序时间复杂度O(n^2)
  • 两个for循环搞定,每一轮for循环找到一个最小值。for循环两两元素对比交换

你可能感兴趣的:(读书笔记)