决战秋招 -- 手撕代码

更新中…


一、Sort

1. Bubble Sort

时间复杂度O(n^2)

def bubble_sort(arr):
    length = len(arr)
    if length < 2:
        return arr
    for i in range(length):
        for j in range(i, length):
            if arr[j] < arr[i]:
                arr[j], arr[i] = arr[i], arr[j]
    return arr

2. Quick Sort

时间复杂度O(nlogn), 最差O(n^2)(如何避免最差?最差是因为每次递归没能平均二分,那么需要选定合适的pivot,使得每次递归尽量二分,可以采取random方式)
空间复杂度O(nlogn)

quick_sort_lam = lambda array: array if len(array) <= 1 else \
    quick_sort_lam([item for item in array[1:] if item <= array[0]]) \
    + [array[0]] + \
    quick_sort_lam([item for item in array[1:] if item > array[0]])


def quick_sort(array, left, right):
    if left >= right:
        return
    low = left
    high = right
    key = array[low]
    while left < right:
        while left < right and array[right] >= key:
            right -= 1
        array[left], array[right] = array[right], array[left]
        while left < right and array[left] <= key:
            left += 1
        array[right], array[left] = array[left], array[right]
    quick_sort(array, low, left - 1)
    quick_sort(array, left + 1, high)

3. Heap Sort

堆调整时间复杂度O(logn)
堆排序时间复杂度O(nlogn)
应用1: 大数据区topk
应用2: 数据流求中位数

"""
堆排序算法的步骤:
1. 把无序数组构建成二叉堆。
2. 循环删除堆顶元素,移到集合尾部,调节堆产生新的堆顶。

大根堆
"""
def downAdjustBig(array, parentIndex, length):
    temp = array[parentIndex]
    childIndex = 2 * parentIndex + 1
    while (childIndex < length):
        if childIndex + 1 < length and array[childIndex + 1] > array[childIndex]:
            childIndex += 1
        if temp >= array[childIndex]:
            break
        array[parentIndex] = array[childIndex]
        parentIndex = childIndex
        childIndex = 2 * childIndex + 1
    array[parentIndex] = temp


def heapSort(array):
    # 1.构建二叉堆
    for i in range(int(len(array) / 2))[::-1]:
        downAdjustBig(array, i, len(array))
    print(array)

    # 2.循环删除堆顶元素,移到集合尾部,调节堆产生新的堆顶
    for i in range(len(array))[::-1]:
        array[i], array[0] = array[0], array[i]
        downAdjustBig(array, 0, i)
    print(array)

二、Tree

1. Traversal

广度 & 深度

# 广度优先遍历算法
def tree_level_traversal(root):
    if root is None:
        return
    my_queue = collections.deque()
    node = root
    my_queue.append(node)
    while my_queue:
        node = my_queue.popleft()
        print(node.val)
        if node.left:
            my_queue.append(node.left)
        if node.right:
            my_queue.append(node.right)


# 深度优先遍历算法
def tree_dfs_traversal(tree_node):
    if tree_node:
        print(tree_node.val)
        if tree_node.left:
            tree_dfs_traversal(tree_node.left)
        if tree_node.right:
            tree_dfs_traversal(tree_node.right)

二叉树 的 三种遍历方式(前中后) 的 非递归解法


"""
当前结点curr不为None时,每一次循环将当前结点curr入栈;
当前结点curr为None时,则出栈一个结点,且打印出栈结点的value值。
整个循环在stack和curr皆为None的时候结束。
"""


def inorderTraversal(root):
    stack = []
    res = []
    curr = root
    while stack or curr:
        if curr:
            stack.append(curr)
            curr = curr.left
        else:
            curr = stack.pop()
            res.append(curr.val)
            curr = curr.right
    return res


"""
由于前序遍历的顺序是中左右,所以我们每次先打印当前结点curr,并将右子结点push到栈中,然后将左子结点设为当前结点。
入栈和出栈条件(当前结点curr不为None时,每一次循环将当前结点curr入栈;
当前结点curr为None时,则出栈一个结点)以及循环结束条件
(整个循环在stack和curr皆为None的时候结束)与中序遍历一模一样。
"""


def preorderTraversal(root):  ## 前序遍历
    stack = []
    res = []
    curr = root
    while stack or curr:
        if curr:
            res.append(curr.val)
            stack.append(curr.right)
            curr = curr.left
        else:
            curr = stack.pop()
    return res


"""
代码的主体部分基本就是.right和.left交换了顺序,
且后序遍历在最后输出的时候进行了反向(因为要从 中右左 变为 左右中 )
"""


def postorderTraversal(root):  ## 后序遍历
    stack = []
    res = []
    curr = root
    while stack or curr:
        if curr:
            res.append(curr.val)
            stack.append(curr.left)
            curr = curr.right
        else:
            curr = stack.pop()
    return res[::-1]

2. 前序中序 --> 构建树/求后序

# 前序 中序 构建树
def getTreeFromPreMid(pre, mid):
    if len(pre) == 0:
        return None
    if len(pre) == 1:
        return TreeNode(pre[0])
    root = TreeNode(pre[0])
    root_index = mid.index(pre[0])
    root.left = getTreeFromPreMid(pre[1:root_index + 1], mid[:root_index])
    root.right = getTreeFromPreMid(pre[root_index + 1:], mid[root_index + 1:])
    return root


# 前序 中序 构建后序
def getAfterFromPreMid(pre, mid, res):
    if len(pre) == 0:
        return
    if len(pre) == 1:
        res.append(pre[0])
        return
    root = pre[0]
    root_index = mid.index(root)
    getAfterFromPreMid(pre[1:root_index + 1], mid[:root_index], res)
    getAfterFromPreMid(pre[root_index + 1:], mid[root_index + 1:], res)
    res.append(root)
    return res

3. 公共祖先 lowest common ancestor (LCA)

lowest common ancestor (LCA)

def lca(root, p, q):
    if p is None or q is None:
        return root
    
    # dfs查找根节点到两个节点的路径
    def dfs(node, visited, res):
        if node is None:
            return
        path = visited + [node.val]
        if node == p or node == q:
            res.append(path)
        dfs(node.left, path, res)
        dfs(node.right, path, res)
    
    res = []
    visited = []
    dfs(root, visited, res)
    
    # 得到两条路径 --> res[0] res[1], 找最近的公共父节点
    i = 0
    for i in range(min(len(res[0]), len(res[1]))):
        if res[0][i] == res[1][i]:
            i += 1
        else:
            return res[0][i-1]
    return res[0][i-1]

BST的LCA


"""
Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.

        _______6______
       /              \
    ___2__          ___8__
   /      \        /      \
   0      _4       7       9
         /  \
         3   5
For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. Another example is LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.

"""
"""
注意这里是一个二叉搜索树,根结点的右子树上所有的点的值都比根结点大,左子树上所有点的值都比根结点的值小
因此分为四种情况,
1、如果两个节点一个值比根节点大,一个比根节点小,那么二者的公共节点肯定是根结点,
2、如果两个节点中有一个与根结点的值同样大,那么二者的公共节点同样是根结点
3、如果两个节点的值都比根结点小,那么二者的公共节点出现在根结点的左子树中,递归查询
4、如果两个节点的值都比根结点大,那么二者的公共节点出现在根结点的右子树中,递归查询
"""


def lowestCommonAncestor_BST(root, p, q):
    """
    :type root: TreeNode
    :type p: TreeNode
    :type q: TreeNode
    :rtype: TreeNode
    """
    if (p.val - root.val) * (q.val - root.val) <= 0:
        return root
    if p.val < root.val and q.val < root.val:
        return lowestCommonAncestor_BST(root.left, p, q)
    if p.val > root.val and q.val > root.val:
        return lowestCommonAncestor_BST(root.right, p, q)


三、Graph

1. 搜索路径问题 & 遍历

给定 起止节点 求搜索路径问题 & Graph的遍历

# -*- coding:UTF-8 -*-

def find_all_paths_dfs(graph, start, end, path=[]):
    """
    返回所有路径DFS
    :param graph:
    :param start:
    :param end:
    :param path:
    :return:
    """
    path = path + [start]
    if start == end:
        return [path]
    if start not in graph:
        return []
    paths = []
    for node in graph[start]:
        if node not in path:
            newpaths = find_all_paths_dfs(graph, node, end, path)
            for newpath in newpaths:
                paths.append(newpath)
    return paths


def find_all_paths_bfs(graph, start, end):
    """
    返回所有路径BFS
    :param graph:
    :param start:
    :param end:
    :return:
    """
    paths = []
    todo = [[start, [start]]]
    while 0 < len(todo):
        (node, path) = todo.pop(0)
        for next_node in graph[node]:
            if next_node in path:
                continue
            elif next_node == end:
                paths.append(path + [next_node])
            else:
                todo.append([next_node, path + [next_node]])
    return paths


def recursive_dfs(graph, start, path=[]):
    """
    dfs遍历 递归形式
    :param graph:
    :param start:
    :param path:
    :return:
    """
    path = path + [start]
    for node in graph[start]:
        if not node in path:
            path = recursive_dfs(graph, node, path)
    return path


def iterative_dfs(graph, start, path=[]):
    '''
    dfs遍历 非递归形式
    :param graph:
    :param start:
    :param path:
    :return:
    '''
    q = [start]
    while q:
        v = q.pop(0)
        if v not in path:
            path = path + [v]
            q = graph[v] + q
    return path


def iterative_bfs(graph, start, path=[]):
    '''
    bfs遍历 非递归形式
    :param graph:
    :param start:
    :param path:
    :return:
    '''
    q = [start]
    while q:
        v = q.pop(0)
        if not v in path:
            path = path + [v]
            q = q + graph[v]
    return path


if __name__ == '__main__':
    '''
       +---- A
       |   /   \
       |  B--D--C
       |   \ | /
       +---- E
    '''
    graph = {'A': ['B', 'C'],
             'B': ['D', 'E'],
             'C': ['D', 'E'],
             'D': ['E'],
             'E': ['A']}
    print('recursive dfs: ', recursive_dfs(graph, 'A'))
    print('iterative dfs: ', iterative_dfs(graph, 'A'))
    print('iterative bfs: ', iterative_bfs(graph, 'A'))

    print("##" * 20)

    print('find_all_paths: ', find_all_paths_dfs(graph, 'A', 'E'))

    print("##" * 20)

    for path in find_all_paths_bfs(graph, 'A', 'E'):
        print(path)


四、LinkList


1. 反转链表

class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None
class Solution(object):
    def reverseList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head:
            return None
        p = head
        q = head.next
        while q:
            head.next = q.next
            q.next = p
            p = q
            q = head.next
        return p
if __name__ == '__main__':
    head = ListNode(0)
    node1 = ListNode(1)
    node2 = ListNode(2)
    node3 = ListNode(3)
    node4 = ListNode(4)
    node5 = ListNode(5)
    node6 = ListNode(6)
    head.next = node1
    node1.next = node2
    node2.next = node3
    node3.next = node4
    node4.next = node5
    node5.next = node6

    p = Solution().reverseList(head)
    while p:
        print(p.val)
        p = p.next

2. Entry of Loop

    """
    第一步,找环中相汇点。分别用p1,p2指向链表头部,p1每次走一步,p2每次走二步,直到p1==p2找到在环中的相汇点。
    第二步,找环的入口。接上步,当p1==p2时,p2所经过节点数为2x,p1所经过节点数为x,设环中有n个节点,p2比p1多走一圈有2x=n+x; n=x;
    可以看出p1实际走了一个环的步数,再让p2指向链表头部,p1位置不变,p1,p2每次走一步直到p1==p2; 此时p1指向环的入口。
    """
    def EntryNodeOfLoop(self, pHead):
        # write code here
        if pHead is None:
            return None
        pLeft = pHead
        pRight = pHead
        circle_cnt = 0
        while pRight and pRight.next:
            pLeft = pLeft.next
            pRight = pRight.next.next
            circle_cnt += 1
            if pLeft.val == pRight.val:
                pRight = pHead
                left_cnt = 0
                while pLeft != pRight:
                    pLeft = pLeft.next
                    pRight = pRight.next
                    left_cnt += 1
                if pLeft == pRight:
                    return pLeft, circle_cnt, left_cnt
        return None

五、Dynamic Programming


动态规划

  • 自底向上
    就是已经知道了所有递归边界,把所有可能的状态都算出来。
    从初始已知的状态出发,向外拓展,最后到达目标状态。
  • 自顶向下(“记忆化搜索”)
    从最终状态开始,找到可以到达当前状态的状态,如果该状态还没处理,就先处理该状态。
    自顶向下通常使用递归实现
  • 总结
    自顶向下,自底向上,只是动态规划实现的套路而已。复杂度并没有多大的变化。
    事实上大多时候用自顶向下复杂度会更低,因为可以过滤掉更多无用的状态;
    不过自底向上可以避免爆栈问题,而且实现往往实现更为简单。

1. 凑硬币问题

def coins_min_combine(arr, target):
    if target <= 0 or target > 1024:
        raise ValueError

    dp = [0 for i in range(target + 1)]
    for i in range(1, target + 1):
        c = sys.maxsize
        for coin in arr:
            if i - coin >= 0:
                c = min(c, dp[i - coin] + 1)
        dp[i] = c
    return dp[-1]

print(coins_min_combine([1, 5, 11], 15))

2. 硬币组合数量问题

def coin_ways(arr, target):
    """
    给定一个正数数组arr,arr[i]表示第i种货币的面值,可以使用任意张。
    给定一个正 target,返回组成aim的方法数有多少种?
    动态规划优化状态依赖的技巧
    :return:
    """
    if len(arr) < 1 or target < 0:
        return 0
    return process(arr, 0, target)


def process(arr, index, target):
    res = 0
    if index == len(arr):
        res = 1 if target == 0 else 0
    else:
        i = 0
        while arr[index] * i <= target:
            res += process(arr, index + 1, target - arr[index] * i)
            i += 1
    return res


def coin_ways_dp_compress(arr, target):
    """
    给定一个正数数组arr,arr[i]表示第i种货币的面值,可以使用任意张。
    给定一个正 target,返回组成aim的方法数有多少种?
    动态规划优化状态依赖的技巧
    :return:
    """
    if len(arr) < 1 or target < 0:
        return 0

    dp = [0 for i in range(target + 1)]
    j = 0
    while arr[0] * j <= target:
        dp[arr[0] * j] = 1
        j += 1

    for i in range(1, len(arr)):
        for j in range(1, target + 1):
            dp[j] += dp[j - arr[i]] if j - arr[i] >= 0 else 0

    print(dp)
    return dp[target]

3. 编辑距离 / 最长公共字串 / 最长公共子序列

# 编辑距离
def levenshtein_distance_dp(input_x, input_y):
    xlen = len(input_x) + 1
    ylen = len(input_y) + 1

    # 此处需要多开辟一个元素存储最后一轮的计算结果
    dp = [[0 for i in range(xlen)] for j in range(ylen)]
    for i in range(xlen):
        dp[i][0] = i
    for j in range(ylen):
        dp[0][j] = j

    for i in range(1, xlen):
        for j in range(1, ylen):
            if input_x[i - 1] == input_y[j - 1]:
                dp[i][j] = dp[i - 1][j - 1]
            else:
                dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
    return dp[xlen - 1][ylen - 1]


# 最长公共子串
def longest_common_substr_dp(str1, str2):
    xlen = len(str1) + 1
    ylen = len(str2) + 1
    record = [[0 for i in range(ylen)] for j in range(xlen)]
    maxNum = 0  # 最长匹配长度
    p = 0  # 匹配的起始位

    for i in range(1, xlen):
        for j in range(1, ylen):
            if str1[i - 1] == str2[j - 1]:
                # 相同则累加
                record[i][j] = record[i - 1][j - 1] + 1
                if record[i][j] > maxNum:
                    # 获取最大匹配长度
                    maxNum = record[i][j]
                    # 记录最大匹配长度的终止位置
                    p = i
    for i in record:
        print(i)
    return str1[p - maxNum:p], maxNum


# 最长公共子序列
def longest_common_sequence(input_x, input_y):
    lcsequence_mat, flag = longest_common_sequence_dp(input_x, input_y)
    i = len(input_x)
    j = len(input_y)
    lcs = []
    get_lcs(input_x, input_y, i, j, flag, lcs)
    print((lcsequence_mat[-1][-1], lcs))


def longest_common_sequence_dp(input_x, input_y):
    xlen = len(input_x) + 1
    ylen = len(input_y) + 1
    dp = [([0] * ylen) for i in range(xlen)]
    flag = [([0] * ylen) for i in range(xlen)]
    for i in range(1, xlen):
        for j in range(1, ylen):
            if input_x[i - 1] == input_y[j - 1]:  # 不在边界上,相等就加一
                dp[i][j] = dp[i - 1][j - 1] + 1
                flag[i][j] = 0
            elif dp[i - 1][j] > dp[i][j - 1]:  # 不相等
                dp[i][j] = dp[i - 1][j]
                flag[i][j] = 1
            else:
                dp[i][j] = dp[i][j - 1]
                flag[i][j] = -1
    for dp_line in dp:
        print(dp_line)
    return dp, flag


def get_lcs(input_x, input_y, i, j, flag, lcs):
    if (i == 0 or j == 0):
        return
    if flag[i][j] == 0:
        get_lcs(input_x, input_y, i - 1, j - 1, flag, lcs)
        lcs.append(input_x[i - 1])
    elif (flag[i][j] == 1):
        get_lcs(input_x, input_y, i - 1, j, flag, lcs)
    else:
        get_lcs(input_x, input_y, i, j - 1, flag, lcs)
    return lcs

4.

在这里插入代码片

六、面试题


1. leetcode470 – rand7生成rand10

Given a function rand7 which generates a uniform random integer in the range 1 to 7, write a function rand10 which generates a uniform random integer in the range 1 to 10.
Do NOT use system’s Math.random().

推导:
给定1-a的随机数生成器,产生1-b的随机数生成器

# -*- coding: utf-8 -*-
"""
@Time    : 2019/4/22 9:05 PM
@Author  : ddlee
@File    : 470rand10.py
"""
import random


class Solution:
    def rand10(self, n):
        """
        :rtype: int
        """

        def rand7():
            return random.randint(1, 7)
        res = []
        for i in range(n):
            x = 7 * (rand7() - 1) + rand7()
            while x > 40:
                x = 7 * (rand7() - 1) + rand7()
            rnd = x % 10 + 1
            res.append(rnd)
        return res


if __name__ == '__main__':
    n = 1000000
    res = Solution().rand10(n)
    cnt = [0 for i in range(10)]
    for i in res:
        cnt[i-1] += 1
    print(cnt)


2. leecode273 – 非负整数转换为其对应的英文表示

# -*- coding:UTF-8 -*-

"""
将非负整数转换为其对应的英文表示。可以保证给定输入小于 231 - 1 。
示例 1:

输入: 123
输出: "One Hundred Twenty Three"
示例 2:

输入: 12345
输出: "Twelve Thousand Three Hundred Forty Five"
"""

class Solution(object):
    def numberToWords(self, num):
        """
        :type num: int
        :rtype: str
        """
        d1 = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve',
              'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen', 'Twenty']
        d2 = ['', 'Ten', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety']
        if num == 0: return 'Zero'
        if num <= 20: return d1[num]
        if num < 100:
            t, d = num // 10, num % 10
            return d2[t] + ' ' + d1[d] if d > 0 else d2[t]

        if num < 1000:
            h = num // 100
            if num % 100 == 0:
                return d1[h] + ' Hundred'
            return d1[h] + ' Hundred ' + self.numberToWords(num % 100)

        if num < 10 ** 6:
            th = num // 10 ** 3
            if num % 10 ** 3 == 0:
                return self.numberToWords(th) + ' Thousand'
            return self.numberToWords(th) + ' Thousand ' + self.numberToWords(num % 10 ** 3)

        if num < 10 ** 9:
            mi = num // 10 ** 6
            if num % 10 ** 6 == 0:
                return self.numberToWords(mi) + ' Million'
            return self.numberToWords(mi) + ' Million ' + self.numberToWords(num % 10 ** 6)

        if num < 10 ** 12:
            bi = num // 10 ** 9
            if num % 10 ** 9 == 0:
                return d1[num // 10 ** 9] + ' Billion'
            return self.numberToWords(bi) + ' Billion ' + self.numberToWords(num % 10 ** 9)

if __name__ == '__main__':
    num = 123456
    print(Solution().numberToWords(num))


3. leetcode207 – 拓扑排序检测有向无环图

# -*- coding: utf-8 -*-
"""
@Time    : 2019/4/23 3:11 PM
@Author  : ddlee
@File    : 207toposort.py
"""
import collections

"""
现在你总共有 n 门课需要选,记为 0 到 n-1。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]

给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?

示例 1:

输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。

==> 判断是否为有向无环图
"""


class Solution:
    def canFinish(self, numCourses, prerequisites):
        """
        :type numCourses: int
        :type prerequisites: List[List[int]]
        :rtype: bool
        """
        graph = collections.defaultdict(list)

        indegrees = [0] * numCourses

        for course, pre in prerequisites:
            graph[pre].append(course)
            indegrees[course] += 1

        return self.topologicalSort(graph, indegrees) == numCourses

    def topologicalSort(self, graph, indegrees):
        count = 0
        queue = []
        for i in range(len(indegrees)):
            if indegrees[i] == 0:
                queue.append(i)
        l = []
        while queue:
            course = queue.pop()
            l.append(course)
            count += 1
            for i in graph[course]:
                indegrees[i] -= 1
                if indegrees[i] == 0:
                    queue.append(i)
        print(l)
        return count


if __name__ == '__main__':
    print(Solution().canFinish(4, [[1, 0], [1, 2], [0, 3], [2, 3]]))

4. 最小删减构造回文串(最长公共子序列思想)(dp问题)

给定一个字符串s,你可以从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长

def palindrome_seq(s):
    length = len(s)
    if length < 2:
        return 0

    rs = s[::-1]
    dp = [[0 for i in range(length + 1)] for j in range(length + 1)]
    for i in range(1, length + 1):
        for j in range(1, length + 1):
            if s[i - 1] == rs[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i][j - 1], dp[i - 1][j])
    for i in dp:
        print(i)
    return length - dp[length][length]

5. leetcode300 – 最长上升子序列(dp问题)

def lis(s):
    """
    最长上升子序列,二维存储
    :param s:
    :return:
    """
    length = len(s)
    if length < 2:
        return length
    dp = [[0 for j in range(length + 1)] for i in range(length + 1)]
    for i in range(1, length + 1):
        for j in range(1, length + 1):
            if s[i - 1] < s[j - 1] and i < j:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
    for i in dp:
        print(i)
    return dp


def lis2(s):
    """
    最长上升子序列,一维存储
    :param s:
    :return:
    """
    length = len(s)
    if length < 2:
        return length
    dp = [1 for i in range(length + 1)]
    dp[0] = 0

    for i in range(1, length + 1):
        for j in range(i, length + 1):
            if s[i - 1] < s[j - 1]:
                dp[j] = max(dp[i] + 1, dp[j])
    return dp

6. 剑指Offer 14 – 剪绳子最大乘积(dp问题)

def maxProductAfterCutting(n):
    """
    剪绳子, 求乘积最大
    :param n:
    :return:
    """
    if n < 2:
        return 0
    if n == 2:
        return 1
    max_list = [0, 1, 2]
    for i in range(3, n + 1):
        if i < n:
            m = i
        else:
            m = 0
        for j in range(1, i // 2 + 1):
            tmp = max_list[j] * max_list[i - j]
            m = max(m, tmp)

        max_list.append(m)
    print(max_list)
    return max_list[n]

7. 最多有多少不重叠的非空区间,使得每个区间内数字的 xor等于0(dp问题)

def most_eor(arr):
    """
	给出n个数字 a_1,...,a_n,问最多有多少不重叠的非空区间,使得每个区间内数字的 xor都等于0。
    :param arr:
    :return:
    """
    ans = 0
    xor = 0
    length = len(arr)

    mosts = [0 for i in range(length)]
    map = {}
    map[0] = -1

    for i in range(length):
        xor ^= arr[i]
        if xor in map:
            pre = map[xor]  # 找到那个开头位置
            mosts[i] = 1 if pre == -1 else (mosts[pre] + 1)  # 开头位置的最大值 + 1
        if i > 0:
            mosts[i] = max(mosts[i - 1], mosts[i])  # 只依赖前i-1 和 i 两种情况

        map[xor] = i
        ans = max(ans, mosts[i])
    return ans

8. 给定一个正数n,求裂开的方法数(dp问题)


def _split_process(pre, rest):
    if rest == 0:
        return 1
    if pre > rest:
        return 0
    ways = 0
    for i in range(pre, rest + 1):
        ways += _split_process(i, rest - i)
    return ways


def split_ways(n):
    if n < 1:
        return 0
    return _split_process(1, n)

def split_ways_dp(n):
    """
    给定一个正数1,裂开的方法有一种,(1)
    给定一个正数2,裂开的方法有两种,(1和1)、(2)
    给定一个正数3,裂开的方法有三种,(1、1、1)、(1、2)、(3)
    给定一个正数4,裂开的方法有五种,(1、1、1、1)、(1、1、2)、(1、3)、(2、2)、 (4)
    给定一个正数n,求裂开的方法数。
    动态规划优化状态依赖的技巧
    :param n:
    :return:
    """
    if n < 1:
        return 0
    dp = [[0 for j in range(n + 1)] for i in range(n + 1)]
    for i in range(1, n + 1):
        dp[i][0] = 1
    for i in range(1, n + 1):
        dp[i][i] = 1
    for pre in range(1, n)[::-1]:
        for rest in range(pre + 1, n + 1):
            dp[pre][rest] = dp[pre + 1][rest] + dp[pre][rest - pre]
    for i in dp:
        print(i)
    return dp[1][n]

9. 消消乐问题(dp问题)

问题描述:
打气球,一下 能 连续打爆一串回文串,或者打爆一个
求打爆所有的最少需要几次?

# -*- coding:UTF-8 -*-
import sys

def archer(n, nums):
    """
    动态规划
    :param n:
    :param nums:
    :return:
    """
    dp = [[sys.maxsize for j in range(n)] for i in range(n)]

    for i in range(n):
        for j in range(i, n):
            if i == j:
                dp[i][j] = 1
                continue
            if j - i == 1:
                dp[i][j] = 1 if nums[i] == nums[j] else 2
                break

    for i in range(n):
        for j in range(i, n):
            for k in range(i, j):
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j])
            if nums[i] == nums[j] and i + 1 < j - 1:
                dp[i][j] = min((dp[i][j], dp[i + 1][j - 1]))
    for i in dp:
        print(i)
    return dp[0][n - 1]


def dfs(l, r):
    """
    动态规划
    递归形式
    :param l:
    :param r:
    :return:
    """

    if dp[l][r] != -1:
        return dp[l][r]
    if l == r:
        dp[l][r] = 1
        return dp[l][r]
    if r - l == 1:
        dp[l][r] = 1 if nums[l] == nums[r] else 2
        return dp[l][r]

    ans = sys.maxsize
    for k in range(l, r):
        ans = min(ans, dfs(l, k) + dfs(k + 1, r))
    if nums[l] == nums[r]:
        ans = min(ans, dfs(l + 1, r - 1))
    dp[l][r] = ans
    return dp[l][r]


if __name__ == '__main__':
    """
    问题描述:
    打气球,一下 能 连续打爆一串回文串,或者打爆一个
    求打爆所有的最少需要几次?
    """
    n = 4
    nums = [1, 4, 3, 1]
    dp = [[-1 for j in range(n)] for i in range(n)]
    print(dfs(0, n - 1))
    for i in dp:
        print(i)

    print("--" * 10)
    print(archer(n, nums))

10. 数据流求中位数(大根堆问题)

# -*- coding:UTF-8 -*-
        """
        想法:
            构建两个堆,大根堆,小根堆,保证大根堆的最大值 比 小根堆的最小值 小,保证两个堆相差不超过2
            这样中位数就可以通过计算堆顶元素获得,时间复杂度O(1)
            堆调整的复杂度O(logn)
        """
class MedianFinder(object):
    def __init__(self):
        self.arr = []
        self.max_heap = []
        self.min_heap = []

    def add_num(self, num):
        self.max_heap.append(num)
        self.down_ajust_max(self.max_heap)

        self.min_heap.append(self.max_heap.pop(0))
        self.down_ajust_min(self.min_heap)

        if len(self.max_heap) < len(self.min_heap):
            self.max_heap.append(self.min_heap.pop(0))
            # self.down_ajust_min(self.min_heap)
            self.down_ajust_max(self.max_heap)

    def down_ajust_max(self, arr):
        length = len(arr)
        if length < 2:
            return arr

        parent_idx = 0
        tmp = arr[parent_idx]
        child_idx = 2 * parent_idx + 1
        while child_idx < length:
            if child_idx + 1 < length and arr[child_idx + 1] > arr[child_idx]:
                child_idx += 1
            if tmp > arr[child_idx]:
                break
            arr[parent_idx] = arr[child_idx]
            parent_idx = child_idx
            child_idx = 2 * child_idx + 1
        arr[parent_idx] = tmp

    def down_ajust_min(self, arr):
        length = len(arr)
        if length < 2:
            return arr

        parent_idx = 0
        tmp = arr[parent_idx]
        child_idx = 2 * parent_idx + 1
        while child_idx < length:
            if child_idx + 1 < length and arr[child_idx + 1] < arr[child_idx]:
                child_idx += 1
            if tmp < arr[child_idx]:
                break
            arr[parent_idx] = arr[child_idx]
            parent_idx = child_idx
            child_idx = 2 * child_idx + 1
        arr[parent_idx] = tmp

    def find_median(self):
        if len(self.max_heap) == len(self.min_heap):
            return (self.max_heap[0] + self.min_heap[0]) / 2
        else:
            return self.max_heap[0]


if __name__ == '__main__':
    mf = MedianFinder()
    mf.add_num(5)
    mf.add_num(4)
    mf.add_num(9)
    mf.add_num(7)
    mf.add_num(3)
    print(mf.min_heap)
    print(mf.max_heap)
    print(mf.find_median())




猜你喜欢

转载自blog.csdn.net/Dooonald/article/details/89471879