数据结构和算法

  • 算法:解决问题的方法和步骤

  • 评价算法的好坏:渐近时间复杂度和渐近空间复杂度。

  • 渐近时间复杂度的大O标记:

    • 数据结构和算法 - 图1 - 常量时间复杂度 - 布隆过滤器 / 哈希存储
    • 数据结构和算法 - 图2 - 对数时间复杂度 - 折半查找(二分查找)
    • 数据结构和算法 - 图3 - 线性时间复杂度 - 顺序查找 / 计数排序
    • 数据结构和算法 - 图4 - 对数线性时间复杂度 - 高级排序算法(归并排序、快速排序)
    • 数据结构和算法 - 图5 - 平方时间复杂度 - 简单排序算法(选择排序、插入排序、冒泡排序)
    • 数据结构和算法 - 图6 - 立方时间复杂度 - Floyd算法 / 矩阵乘法运算
    • 数据结构和算法 - 图7 - 几何级数时间复杂度 - 汉诺塔
    • 数据结构和算法 - 图8 - 阶乘时间复杂度 - 旅行经销商问题 - NPC

    数据结构和算法 - 图9

    数据结构和算法 - 图10

  • 排序算法(选择、冒泡和归并)和查找算法(顺序和折半)

    1. def select_sort(items, comp=lambda x, y: x < y):
    2. “””简单选择排序”””
    3. items = items[:]
    4. for i in range(len(items) - 1):
    5. min_index = i
    6. for j in range(i + 1, len(items)):
    7. if comp(items[j], items[min_index]):
    8. min_index = j
    9. items[i], items[min_index] = items[min_index], items[i]
    10. return items
    1. def bubble_sort(items, comp=lambda x, y: x > y):
    2. “””冒泡排序”””
    3. items = items[:]
    4. for i in range(len(items) - 1):
    5. swapped = False
    6. for j in range(len(items) - 1 - i):
    7. if comp(items[j], items[j + 1]):
    8. items[j], items[j + 1] = items[j + 1], items[j]
    9. swapped = True
    10. if not swapped:
    11. break
    12. return items
    1. def bubble_sort(items, comp=lambda x, y: x > y):
    2. “””搅拌排序(冒泡排序升级版)”””
    3. items = items[:]
    4. for i in range(len(items) - 1):
    5. swapped = False
    6. for j in range(len(items) - 1 - i):
    7. if comp(items[j], items[j + 1]):
    8. items[j], items[j + 1] = items[j + 1], items[j]
    9. swapped = True
    10. if swapped:
    11. swapped = False
    12. for j in range(len(items) - 2 - i, i, -1):
    13. if comp(items[j - 1], items[j]):
    14. items[j], items[j - 1] = items[j - 1], items[j]
    15. swapped = True
    16. if not swapped:
    17. break
    18. return items

    ```Python def merge(items1, items2, comp=lambda x, y: x < y):

    1. “””合并(将两个有序的列表合并成一个有序的列表)”””
    2. items = []
    3. index1, index2 = 0, 0
    4. while index1 < len(items1) and index2 < len(items2):
    5. if comp(items1[index1], items2[index2]):
    6. items.append(items1[index1])
    7. index1 += 1
    8. else:
    9. items.append(items2[index2])
    10. index2 += 1
    11. items += items1[index1:]
    12. items += items2[index2:]
    13. return items

def merge_sort(items, comp=lambda x, y: x < y):

  1. return _merge_sort(list(items), comp)</p>

def _merge_sort(items, comp):

  1. “””归并排序”””
  2. if len(items) &lt; 2:
  3. return items
  4. mid = len(items) // 2
  5. left = _merge_sort(items[:mid], comp)
  6. right = _merge_sort(items[mid:], comp)
  7. return merge(left, right, comp)</p>
  1. Python</span></code></li><li class="L2"><code><span class="str"> def seq_search(items, key):</span></code></li><li class="L3"><code><span class="str"> """顺序查找"""</span></code></li><li class="L4"><code><span class="str"> for index, item in enumerate(items):</span></code></li><li class="L5"><code><span class="str"> if item == key:</span></code></li><li class="L6"><code><span class="str"> return index</span></code></li><li class="L7"><code><span class="str"> return -1</span></code></li></ol></pre><pre class="prettyprint linenums prettyprinted" style=""><button class="btn btn-danger btn-sm btn-copy"><i class="fa fa-copy"></i> 复制代码</button><ol class="linenums"><li class="L0"><code class="lang-Python"><span class="pln"> </span><span class="kwd">def</span><span class="pln"> bin_search</span><span class="pun">(</span><span class="pln">items</span><span class="pun">,</span><span class="pln"> key</span><span class="pun">):</span></code></li><li class="L1"><code class="lang-Python"><span class="pln"> </span><span class="str">"""折半查找"""</span></code></li><li class="L2"><code class="lang-Python"><span class="pln"> start</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">end</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">items</span><span class="pun">)</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span></code></li><li class="L3"><code class="lang-Python"><span class="pln"> </span><span class="kwd">while</span><span class="pln"> start </span><span class="pun">&lt;=</span><span class="pln"> </span><span class="kwd">end</span><span class="pun">:</span></code></li><li class="L4"><code class="lang-Python"><span class="pln"> mid </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">start </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">end</span><span class="pun">)</span><span class="pln"> </span><span class="com">// 2</span></code></li><li class="L5"><code class="lang-Python"><span class="pln"> </span><span class="kwd">if</span><span class="pln"> key </span><span class="pun">&gt;</span><span class="pln"> items</span><span class="pun">[</span><span class="pln">mid</span><span class="pun">]:</span></code></li><li class="L6"><code class="lang-Python"><span class="pln"> start </span><span class="pun">=</span><span class="pln"> mid </span><span class="pun">+</span><span class="pln"> </span><span class="lit">1</span></code></li><li class="L7"><code class="lang-Python"><span class="pln"> </span><span class="kwd">elif</span><span class="pln"> key </span><span class="pun">&lt;</span><span class="pln"> items</span><span class="pun">[</span><span class="pln">mid</span><span class="pun">]:</span></code></li><li class="L8"><code class="lang-Python"><span class="pln"> </span><span class="kwd">end</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> mid </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span></code></li><li class="L9"><code class="lang-Python"><span class="pln"> </span><span class="kwd">else</span><span class="pun">:</span></code></li><li class="L0"><code class="lang-Python"><span class="pln"> </span><span class="kwd">return</span><span class="pln"> mid</span></code></li><li class="L1"><code class="lang-Python"><span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span></code></li></ol></pre> <ul> <li><p>常用算法:</p> <ul> <li>穷举法 - 又称为暴力破解法,对所有的可能性进行验证,直到找到正确答案。</li><li>贪婪法 - 在对问题求解时,总是做出在当前看来</li><li>最好的选择,不追求最优解,快速找到满意解。</li><li>分治法 - 把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题,直到可以直接求解的程度,最后将子问题的解进行合并得到原问题的解。</li><li>回溯法 - 回溯法又称为试探法,按选优条件向前搜索,当搜索到某一步发现原先选择并不优或达不到目标时,就退回一步重新选择。</li><li>动态规划 - 基本思想也是将待求解问题分解成若干个子问题,先求解并保存这些子问题的解,避免产生大量的重复运算。</li></ul> <p>穷举法例子:百钱百鸡和五人分鱼。</p> <pre class="prettyprint linenums prettyprinted" style=""><button class="btn btn-danger btn-sm btn-copy"><i class="fa fa-copy"></i> 复制代码</button><ol class="linenums"><li class="L0"><code class="lang-Python"><span class="com"># 公鸡5元一只 母鸡3元一只 小鸡1元三只</span></code></li><li class="L1"><code class="lang-Python"><span class="com"># 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只</span></code></li><li class="L2"><code class="lang-Python"><span class="kwd">for</span><span class="pln"> x </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">20</span><span class="pun">):</span></code></li><li class="L3"><code class="lang-Python"><span class="pln"> </span><span class="kwd">for</span><span class="pln"> y </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">33</span><span class="pun">):</span></code></li><li class="L4"><code class="lang-Python"><span class="pln"> z </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100</span><span class="pln"> </span><span class="pun">-</span><span class="pln"> x </span><span class="pun">-</span><span class="pln"> y</span></code></li><li class="L5"><code class="lang-Python"><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> x </span><span class="pun">+</span><span class="pln"> </span><span class="lit">3</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> y </span><span class="pun">+</span><span class="pln"> z </span><span class="com">// 3 == 100 and z % 3 == 0:</span></code></li><li class="L6"><code class="lang-Python"><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">x</span><span class="pun">,</span><span class="pln"> y</span><span class="pun">,</span><span class="pln"> z</span><span class="pun">)</span></code></li><li class="L7"><code class="lang-Python"></code></li><li class="L8"><code class="lang-Python"><span class="com"># A、B、C、D、E五人在某天夜里合伙捕鱼 最后疲惫不堪各自睡觉</span></code></li><li class="L9"><code class="lang-Python"><span class="com"># 第二天A第一个醒来 他将鱼分为5份 扔掉多余的1条 拿走自己的一份</span></code></li><li class="L0"><code class="lang-Python"><span class="com"># B第二个醒来 也将鱼分为5份 扔掉多余的1条 拿走自己的一份</span></code></li><li class="L1"><code class="lang-Python"><span class="com"># 然后C、D、E依次醒来也按同样的方式分鱼 问他们至少捕了多少条鱼</span></code></li><li class="L2"><code class="lang-Python"><span class="pln">fish </span><span class="pun">=</span><span class="pln"> </span><span class="lit">6</span></code></li><li class="L3"><code class="lang-Python"><span class="kwd">while</span><span class="pln"> </span><span class="kwd">True</span><span class="pun">:</span></code></li><li class="L4"><code class="lang-Python"><span class="pln"> total </span><span class="pun">=</span><span class="pln"> fish</span></code></li><li class="L5"><code class="lang-Python"><span class="pln"> enough </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">True</span></code></li><li class="L6"><code class="lang-Python"><span class="pln"> </span><span class="kwd">for</span><span class="pln"> _ </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="lit">5</span><span class="pun">):</span></code></li><li class="L7"><code class="lang-Python"><span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">total </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> </span><span class="lit">5</span><span class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="lit">0</span><span class="pun">:</span></code></li><li class="L8"><code class="lang-Python"><span class="pln"> total </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">total </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">)</span><span class="pln"> </span><span class="com">// 5 * 4</span></code></li><li class="L9"><code class="lang-Python"><span class="pln"> </span><span class="kwd">else</span><span class="pun">:</span></code></li><li class="L0"><code class="lang-Python"><span class="pln"> enough </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">False</span></code></li><li class="L1"><code class="lang-Python"><span class="pln"> </span><span class="kwd">break</span></code></li><li class="L2"><code class="lang-Python"><span class="pln"> </span><span class="kwd">if</span><span class="pln"> enough</span><span class="pun">:</span></code></li><li class="L3"><code class="lang-Python"><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="pln">fish</span><span class="pun">)</span></code></li><li class="L4"><code class="lang-Python"><span class="pln"> </span><span class="kwd">break</span></code></li><li class="L5"><code class="lang-Python"><span class="pln"> fish </span><span class="pun">+=</span><span class="pln"> </span><span class="lit">5</span></code></li></ol></pre> <p>贪婪法例子:假设小偷有一个背包,最多能装20公斤赃物,他闯入一户人家,发现如下表所示的物品。很显然,他不能把所有物品都装进背包,所以必须确定拿走哪些物品,留下哪些物品。</p> <p>| 名称 | 价格(美元) | 重量(kg) | | :——: | :—————: | :————: | | 电脑 | 200 | 20 | | 收音机 | 20 | 4 | | 钟 | 175 | 10 | | 花瓶 | 50 | 2 | | 书 | 10 | 1 | | 油画 | 90 | 9 |</p> <p>Python “”” 贪婪法:在对问题求解时,总是做出在当前看来是最好的选择,不追求最优解,快速找到满意解。 输入: 20 6 电脑 200 20 收音机 20 4 钟 175 10 花瓶 50 2 书 10 1 油画 90 9 “”” class Thing(object):

    1. “””物品”””
    2. def init(self, name, price, weight):
    3. self.name = name
    4. self.price = price
    5. self.weight = weight
    6. @property
    7. def value(self):
    8. “””价格重量比”””
    9. return self.price / self.weight
  2. def input_thing():

    “””输入物品信息”””
    name_str, price_str, weight_str = input().split()
    return name_str, int(price_str), int(weight_str)</p>
    

    def main():

    “””主函数”””
    max<em>weight, num_of_things = map(int, input().split())
    all_things = []
    for </em> in range(num_of_things):
        all_things.append(Thing(*input_thing()))
    all_things.sort(key=lambda x: x.value, reverse=True)
    total_weight = 0
    total_price = 0
    for thing in all_things:
        if total_weight + thing.weight &lt;= max_weight:
            print(f’小偷拿走了{thing.name}’)
            total_weight += thing.weight
            total_price += thing.price
    print(f’总价值: {total_price}美元’)</p>
    

    if name == ‘main‘:

    main()</p>
    
    1. 分治法例子:快速排序
    2. ```Python
    3. “””
    4. 快速排序 - 选择枢轴对元素进行划分,左边都比枢轴小右边都比枢轴大
    5. “””
    6. def quicksort(items, comp=lambda x, y: x <= y):
    7. items = list(items)[:]
    8. _quick_sort(items, 0, len(items) - 1, comp)
    9. return items
    10. def _quick_sort(items, start, end, comp):
    11. if start < end:
    12. pos = _partition(items, start, end, comp)
    13. _quick_sort(items, start, pos - 1, comp)
    14. _quick_sort(items, pos + 1, end, comp)
    15. def _partition(items, start, end, comp):
    16. pivot = items[end]
    17. i = start - 1
    18. for j in range(start, end):
    19. if comp(items[j], pivot):
    20. i += 1
    21. items[i], items[j] = items[j], items[i]
    22. items[i + 1], items[end] = items[end], items[i + 1]
    23. return i + 1

    回溯法例子:骑士巡逻

    1. “””
    2. 递归回溯法:叫称为试探法,按选优条件向前搜索,当搜索到某一步,发现原先选择并不优或达不到目标时,就退回一步重新选择,比较经典的问题包括骑士巡逻、八皇后和迷宫寻路等。
    3. “””
    4. import sys
    5. import time
    6. SIZE = 5
    7. total = 0
    8. def print_board(board):
    9. for row in board:
    10. for col in row:
    11. print(str(col).center(4), end=‘’)
    12. print()
    13. def patrol(board, row, col, step=1):
    14. if row >= 0 and row < SIZE and \
    15. col >= 0 and col < SIZE and \
    16. board[row][col] == 0:
    17. board[row][col] = step
    18. if step == SIZE SIZE:
    19. global total
    20. total += 1
    21. print(f‘第{total}种走法: ‘)
    22. print_board(board)
    23. patrol(board, row - 2, col - 1, step + 1)
    24. patrol(board, row - 1, col - 2, step + 1)
    25. patrol(board, row + 1, col - 2, step + 1)
    26. patrol(board, row + 2, col - 1, step + 1)
    27. patrol(board, row + 2, col + 1, step + 1)
    28. patrol(board, row + 1, col + 2, step + 1)
    29. patrol(board, row - 1, col + 2, step + 1)
    30. patrol(board, row - 2, col + 1, step + 1)
    31. board[row][col] = 0
    32. def main():
    33. board = [[0] SIZE for in range(SIZE)]
    34. patrol(board, SIZE - 1, SIZE - 1)
    35. if name == main:
    36. main()

    动态规划例子:子列表元素之和的最大值。

    说明:子列表指的是列表中索引(下标)连续的元素构成的列表;列表中的元素是int类型,可能包含正整数、0、负整数;程序输入列表中的元素,输出子列表元素求和的最大值,例如:

    输入:1 -2 3 5 -3 2

    输出:8

    输入:0 -2 3 5 -1 2

    输出:9

    输入:-9 -2 -3 -5 -3

    输出:-2

    1. def main():
    2. items = list(map(int, input().split()))
    3. overall = partial = items[0]
    4. for i in range(1, len(items)):
    5. partial = max(items[i], partial + items[i])
    6. overall = max(partial, overall)
    7. print(overall)
    8. if name == main:
    9. main()

    说明:这个题目最容易想到的解法是使用二重循环,但是代码的时间性能将会变得非常的糟糕。使用动态规划的思想,仅仅是多用了两个变量,就将原来$O(N^2)$复杂度的问题变成了$O(N)$。