决战秋招 -- 经典面试题集锦

更新中…

文章目录


一、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.

在这里插入代码片

六、String


1. Permutation

def perm_str(s=''):
    if len(s) <= 1:
        return [s]
    str_list = []
    for i in range(len(s)):
        for j in perm_str(s[0:i] + s[i + 1:]):
            str_list.append(s[i] + j)
    return str_list

2. Combinations

# -*- coding: utf-8 -*-
"""
@Time    : 2019/8/22 2:35 PM
@Author  : ddlee
@File    : combinations.py
"""
import itertools


# itertools.combinations()
def combinations(iterable, r):
    pool = tuple(iterable)
    n = len(pool)
    if r > n:
        return

    indices = list(range(r))
    yield tuple(pool[i] for i in indices)
    while True:
        for i in reversed(range(r)):
            if indices[i] != i + n - r:
                break
        else:
            return

        indices[i] += 1
        for j in range(i + 1, r):
            indices[j] = indices[j - 1] + 1
        yield tuple(pool[i] for i in indices)


def combine(n, k):
    def backtrack(first=1, curr=[]):
        # if the combination is done
        if len(curr) == k:
            res.append(curr.copy())
        for i in range(first, n + 1):
            # add i into the current combination
            curr.append(i)
            # use next integers to complete the combination
            backtrack(i + 1, curr)
            # backtrack
            curr.pop()

    res = []
    backtrack()
    return res


if __name__ == '__main__':
    # combinations('ABCD', 2) --> AB AC AD BC BD CD
    # combinations(range(4), 3) --> 012 013 023 123
    for i in combinations(range(1, 5), 2):
        print(i)

    print(combine(n=4, k=2))


3. Josephus

def josephus_formula(n, m):
    """
    :param n: 总人数
    :param m: 每数到m去除
    :return: 返回最后一个人的下标
    """

    if n == 1:
        return 0
    else:
        return (josephus_formula(n - 1, m) + m) % n


def josephus_mimic(n, k):
    link = list(range(1, n + 1))
    ind = 0

    for loop_i in range(n - 1):
        ind = (ind + k) % len(link)
        ind -= 1

        print('Kill:', link[ind])
        del link[ind]
        if ind == -1:  # the last element of link
            ind = 0

    print('survice :', link[0])


def josephus(n, m):
    """

    Args:
        n: n people
        m: count m del

    Returns: last survive

    """
    people = list(range(1, n + 1))
    idx = 0

    last = 0
    for i in range(n):
        cnt = 0
        while cnt < m:
            if people[idx] != -1:
                cnt += 1
            if cnt == m:
                # print(people[idx])
                last = people[idx]
                people[idx] = -1
            idx = (idx + 1) % n

    return last


if __name__ == '__main__':
    n = 3
    m = 2
    ans = josephus(n, m)
    print(ans)

七、面试题


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 lis2(s):
    """
    最长上升子序列,一维存储
    :param s:
    :return:
    """
    length = len(s)
    if length < 2:
        return length
    # 以j结尾的最长上升子序列长度
    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. 企鹅合体问题(dp问题)


def penguin_merge_near(arr):
    """
    企鹅合体,只能合体一次,只能和左右其中的一个合体,合体即乘积,有可能为负数,求最大值
    :param arr:
    :return:
    """
    length = len(arr)
    if length < 1:
        return 0
    elif length == 1:
        return arr[0]
    elif length == 2:
        return max(arr[0] + arr[1], arr[0] * arr[1])

    dp = [0 for i in range(length + 1)]
    dp[1] = arr[0]
    dp[2] = max(arr[0] + arr[1], arr[0] * arr[1])
    if length > 2:
        for i in range(3, length + 1):
            dp[i] = max(dp[i - 1] + arr[i - 1], dp[i - 2] + arr[i - 1] * arr[i - 2])
    return dp[-1]


def penguin_merge(arr):
    """
    企鹅合体,只能合体一次,合体即乘积,有可能为负数,求最大值
    由于不指定顺序,因此可以先排序,-5, -3, 0, 1, 3, 5
    :param arr:
    :return:
    """
    length = len(arr)

    arr = sorted(arr)
    if length < 1:
        return 0
    elif length == 1:
        return arr[0]
    elif length == 2:
        return max(arr[0] + arr[1], arr[0] * arr[1])

    ans = 0
    if length > 2:
        l = 0
        r = length - 1
        while r > 0:
            if arr[r - 1] > 1:
                ans += arr[r] * arr[r - 1]
                r -= 2
            else:
                break
        while l < r:
            if arr[l + 1] <= 0:
                ans += arr[l] * arr[l + 1]
                l += 2
            else:
                break
        while l <= r:
            ans += arr[l]
            l += 1
    return ans

11. leetcode845 – 最长山脉问题(dp问题)

# -*- coding: utf-8 -*-
"""
@Time    : 2019/5/11 3:06 PM
@Author  : ddlee
@File    : 845longest_mountain.py
"""


class Solution():
    def longestMountain1(self, arr):
        """
        求数组中的最长山脉
            - B.length >= 3
            - 存在 0 < i < B.length - 1 使得 B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]
        输入:[2,1,4,7,3,2,5]
        输出:5
        解释:最长的 “山脉” 是 [1,4,7,3,2],长度为 5。
        """

        # j结尾的递增序列
        def lis(arr):
            length = len(arr)
            dp = [0 for i in range(length + 1)]
            for i in range(1, length + 1):
                for j in range(i, length + 1):
                    if arr[j - 1] > arr[i - 1]:
                        dp[j] = max(dp[i] + 1, dp[j])
            return dp

        # i开头的递减序列
        def lds(arr):
            length = len(arr)
            dp = [0 for i in range(length + 1)]
            for i in range(1, length + 1)[::-1]:
                for j in range(i, length + 1)[::-1]:
                    if arr[j - 1] < arr[i - 1]:
                        dp[i] = max(dp[j] + 1, dp[i])
            return dp

        lis_dp = lis(arr)
        print(lis_dp)
        lds_dp = lds(arr)
        print(lds_dp)

        res = []
        for i in range(len(lis_dp)):
            if lis_dp[i] and lds_dp[i]:
                res.append(lis_dp[i] + lds_dp[i] + 1)
            else:
                res.append(0)
        return max(res) if res else 0

    def longestMountain(self, arr):
        """
        求数组中的最长山脉
            - B.length >= 3
            - 存在 0 < i < B.length - 1 使得 B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]
        输入:[2,1,4,7,3,2,5]
        输出:5
        解释:最长的 “山脉” 是 [1,4,7,3,2],长度为 5。
        """

        # i结尾的递增序列
        def lis(arr):
            length = len(arr)
            dp = [0 for i in range(length)]
            for i in range(1, length):
                if arr[i] > arr[i - 1]:
                    dp[i] = max(dp[i - 1] + 1, dp[i])
            return dp

        # i开头的递减序列
        def lds(arr):
            length = len(arr)
            dp = [0 for i in range(length)]
            for i in range(length - 1)[::-1]:
                if arr[i] > arr[i + 1]:
                    dp[i] = max(dp[i + 1] + 1, dp[i])
            return dp

        lis_dp = lis(arr)
        # print(lis_dp)
        lds_dp = lds(arr)
        # print(lds_dp)

        res = []
        for i in range(len(lis_dp)):
            if lis_dp[i] and lds_dp[i]:
                res.append(lis_dp[i] + lds_dp[i] + 1)
            else:
                res.append(0)
        return max(res) if res else 0


if __name__ == '__main__':
    arr = [2, 1, 4, 7, 3, 2, 5]
    arr = list(range(5))
    arr = []
    print(Solution().longestMountain(arr))

12. 分隔数组以得到最大和 (dp问题)

给出整数数组 A,将该数组分隔为长度最多为 K 的几个(连续)子数组。
分隔完成后,每个子数组的中的值都会变为该子数组中的最大值。
返回给定数组完成分隔后的最大和。

输入:A = [1,15,7,9,2,5,10], K = 3
输出:84
解释:A 变为 [15,15,15,9,10,10,10]

class Solution:
    def maxSumAfterPartitioning2(self, A, K):
        l = len(A)
        dp = [0 for i in range(l+1)]

        for i in range(l):
            maxi = 0
            rb = min(i + K, l)  # right bound
            for j in range(i, rb):
                maxi = max(maxi, A[j])
                dp[j + 1] = max(dp[j + 1], dp[i] + (j - i + 1) * maxi)
        # print(dp)
        return dp[l]

13. leetcode221 – 最大正方形(dp问题)

在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。
示例:
输入:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
输出: 4

class Solution:
    def maximalSquare1(self, matrix):
        if len(matrix) < 1:
            return 0

        matrix = [list(map(int, row)) for row in matrix]
        m = len(matrix)
        n = len(matrix[0])

        dp = [[0 for j in range(n)] for i in range(m)]

        for i in range(m):
            dp[i][0] = matrix[i][0]
        for j in range(n):
            dp[0][j] = matrix[0][j]

        for i in range(1, m):
            for j in range(1, n):
                if matrix[i][j] == 1:
                    if matrix[i - 1][j - 1] == 1:
                        for ind in range(1, int(math.sqrt(dp[i - 1][j - 1]) + 1)):
                            if matrix[i - ind][j] == 1 and matrix[i][j - ind] == 1:
                                dp[i][j] = int(pow(ind + 1, 2))
                            else:
                                dp[i][j] = int(pow(ind + 1 - 1, 2))
                                break
                    else:
                        dp[i][j] = 1
        return max(map(max,dp))

    """
    计算边长,比上一种方案好
    """
    def maximalSquare(self, matrix):
        if len(matrix) < 1:
            return 0

        matrix = [list(map(int, row)) for row in matrix]
        m = len(matrix)
        n = len(matrix[0])

        dp = [[0 for j in range(n)] for i in range(m)]

        for i in range(m):
            dp[i][0] = matrix[i][0]
        for j in range(n):
            dp[0][j] = matrix[0][j]

        for i in range(1, m):
            for j in range(1, n):
                if matrix[i][j] == 1:
                    dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
                else:
                    min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
        for i in dp:
            print(i)
        return pow(max(map(max,dp)), 2)

14. 最后剩一块石头重量(dp问题)

class Solution:
    """
    1049. 最后一块石头的重量 II  显示英文描述

    有一堆石头,每块石头的重量都是正整数。

    每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

    如果 x == y,那么两块石头都会被完全粉碎;
    如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
    最后,最多只会剩下一块石头。返回此石头最小的可能重量。如果没有石头剩下,就返回 0。

    示例:
    输入:[2,7,4,1,8,1]
    输出:1
    解释:
    组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
    组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
    组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
    组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。
    """

    def lastStoneWeightII(self, stones):
        '''动态规划 背包问题'''
        l = len(stones)
        if l < 1:
            return 0

        half = sum(stones) // 2  # 背包最大放的重量
        dp = [[0 for j in range(half + 1)] for i in range(l + 1)]
        for i in range(1, l + 1):
            for j in range(1, half + 1):
                if stones[i - 1] > j:
                    dp[i][j] = dp[i - 1][j]
                else:
                    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - stones[i - 1]] + stones[i - 1])

        for i in dp:
            print(i)
        return sum(stones) - (2 * dp[-1][-1])

15. 最长等差序列(dp问题)


class Solution:
    """
    输入:[3,6,9,12]
    输出:4
    解释:
    整个数组是公差为 3 的等差数列。
    """
    '''dp加缓存'''
    def longestArithSeqLength(self, A) -> int:
        if A == None or A == []:
            return 0

        n, res = len(A), 1
        dp = [{0: 1} for _ in range(n)]

        for j in range(n):
            for i in range(j):
                step = A[j] - A[i]
                if step in dp[i]:
                    if step in dp[j]:
                        dp[j][step] = max(dp[i][step] + 1, dp[j][step])
                    else:
                        dp[j][step] = dp[i][step] + 1
                else:
                    dp[j][step] = 2
            res = max(res, max(dp[j].values()))

        print(dp)
        return res

    '''暴力超时'''
    def las(self, A):
        if A == None or A == []:
            return 0

        l = len(A)
        if l < 2:
            return l

        res = 2
        for i in range(l):
            for j in range(i + 1, l):
                tmp = 2
                gap = A[j] - A[i]
                next = A[j] + gap
                for k in range(j + 1, l):
                    if A[k] == next:
                        tmp += 1
                        next += gap
                res = max(res, tmp)
        return res

16. leetcoe115 – 不同的子序列(dp问题)


"""
不同的子序列
给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。
一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。
(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)

示例 1:

输入: S = "rabbbit", T = "rabbit"
输出: 3
解释:
如下图所示, 有 3 种可以从 S 中得到 "rabbit" 的方案。
(上箭头符号 ^ 表示选取的字母)

rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^
"""


class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        ls = len(s) + 1
        lt = len(t) + 1

        dp = [[0 for j in range(lt)] for i in range(ls)]
        if not t:
            return 1
        for i in range(ls):
            dp[i][0] = 1
            dp[i][1] = s[:i].count(t[0])

        for i in range(1, ls):
            for j in range(1, lt):
                if s[i - 1] == t[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]
                    # 相等的情况:s用相等这一位的方案 +  s不用相等这一位的方案
                else:
                    dp[i][j] = dp[i - 1][j]
        for i in dp:
            print(i)
        return dp[-1][-1]

17. leetcoe279 – 完全平方数(dp问题)

解决方案:动态规划,定义一个dp数组,长度为N+1,初始化为系统最大值,0的位置初始化为0
从1开始刷新整个数组:
1的平方是1,所以1……N中填1……N,更新dp
2的平方是4,所以从4的位置开始更新,dp[i] = min(dp[i], dp[i-j^2]+1)
……


'''
279. 完全平方数

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

示例 1:
输入: n = 12
输出: 3 
解释: 12 = 4 + 4 + 4.

示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9.
'''


class Solution:
    def numSquares(self, n: int) -> int:
        if n < 1:
            return 0

        dp = [sys.maxsize for i in range(n + 1)]
        dp[0] = 0

        rnd = int(pow(n, 0.5)) + 1

        for i in range(1, rnd):
            for j in range(pow(i, 2), n + 1):
                dp[j] = min(dp[j], dp[j - pow(i, 2)] + 1)

        # print(dp)
        return dp[-1]

    def numSquares1(self, n):
        """
        :type n: int
        :rtype: int
        """

        def help(N, size):
            if (N == 0): return True;
            if (size == 0): return False;
            k = int(pow(N, 0.5));
            for i in range(k, 0, -1):
                if (help(N - i * i, size - 1)):
                    return True
            return False

        f = False
        ans = 0;
        while (f == 0):
            ans = ans + 1;
            f = help(n, ans)
        return ans


if __name__ == '__main__':
    print(Solution().numSquares2(6175))

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

# -*- 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())

22. 判断矩阵中是否存在指定路径(递归)

class Solution:
    def hasPath(self, matrix, rows, cols, path):
        # write code here
        for i in range(rows):
            for j in range(cols):
                if matrix[i * cols + j] == path[0]:
                    if self.find(list(matrix), rows, cols, path[1:], i, j):
                        return True
        return False

    def find(self, matrix, rows, cols, path, i, j):
        if not path:
            return True
        matrix[i * cols + j] = '0'
        if j + 1 < cols and matrix[i * cols + j + 1] == path[0]:
            return self.find(matrix, rows, cols, path[1:], i, j + 1)
        elif j - 1 >= 0 and matrix[i * cols + j - 1] == path[0]:
            return self.find(matrix, rows, cols, path[1:], i, j - 1)
        elif i + 1 < rows and matrix[(i + 1) * cols + j] == path[0]:
            return self.find(matrix, rows, cols, path[1:], i + 1, j)
        elif i - 1 >= 0 and matrix[(i - 1) * cols + j] == path[0]:
            return self.find(matrix, rows, cols, path[1:], i - 1, j)
        else:
            return False

23. sqrt

def sqrt(x):
    if x < 2:
        return x
    left, right = 0, x
    while left <= right:
        mid = left + (right - left) // 2
        if mid * mid < x:
            # left=mid
            left = mid + 1
            lstmid = mid  # 关键步骤
        elif mid * mid > x:
            # right=x
            right = mid - 1
        else:
            return mid
    return lstmid

24. 约瑟夫环

"""
Josephuse环
n个人,编号0 到 n-1,每走step步移除一个人,最后剩的编号是?

method-1: 用环去模拟
method-2:分析规律  计算(有公式)
"""


def last_remain(length, step):
    if length < 1 or step < 1:
        return -1
    nums = [i for i in range(length)]
    step_cnt = 0
    remain_cnt = length
    i = -1
    while remain_cnt > 0:
        i += 1
        if i >= length:
            i = 0
        if nums[i] == -1:
            continue
        step_cnt += 1
        if step_cnt == step:
            nums[i] = -1
            step_cnt = 0
            remain_cnt -= 1
    return i

# 公式版本
def last_remain_2(length, step):
    if length < 1 or step < 1:
        return -1
    last = 0
    for i in range(2, length + 1):
        last = (last + step) % i
    return last


if __name__ == '__main__':
    for i in range(1,50):
        print(last_remain(i, 3), last_remain_2(i, 3))

25. 2sum 3sum

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

def sum_2(arr, target):
    hash_table = {}
    res = []
    for i in range(len(arr)):
        if target - arr[i] in hash_table:
            res.append([hash_table[target - arr[i]], i])
        hash_table[arr[i]] = i
    return res


def sum_3(arr, target):
    arr.sort()
    len1 = len(arr)
    res = []
    if len1 < 3:
        print(res)
    for i in range(len1 - 1):
        left, right = i + 1, len1 - 1  # 以下思路与2sum中的快速排序思想一样
        while left < right:
            sum = arr[i] + arr[left] + arr[right]
            if sum == target and [arr[i], arr[left], arr[right]] not in res:
                res.append([arr[i], arr[left], arr[right]])
                left += 1
                right -= 1
            elif sum < target:
                left += 1
            else:
                right -= 1
    print(res)


def sum_3_2(arr, target):  # 3sum问题 解 2
    res = []
    for i, value1 in enumerate(arr):
        for j, value2 in enumerate(arr[i + 1:]):
            if (target - value1 - value2) in arr[i + 2:]:
                minV = min(value1, value2, target - value1 - value2)
                maxV = max(value1, value2, target - value1 - value2)
                midV = target - minV - maxV
                res.append((minV, midV, maxV))
    print(list(set(res)))

if __name__ == '__main__':
    nums = [1, 4, 3, 2, 6, 5]
    target = 6

    print(sum_2(nums, target))
    sum_3(nums, target)
    sum_3_2(nums, target)

    print(continuous_pos_sum(15))

26. leetcode40 – 和为target的组合(递归)

class Solution:
    """
    leetcode 40
    给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

    candidates 中的每个数字在每个组合中只能使用一次。
    """
    def combinationSum2(self, candidates, target):
        # 下面有一个目标值小于某一元素就break,所以先排序
        candidates.sort()
        # 储存返回的二维列表
        res, length = [], len(candidates)

        # 递归,目标值,起始位置,当前组合
        def dfs(target, start, vlist):
            # 目标值为0,表明当前递归完成,把当前递归结果加入res并返回
            if target == 0:
                return res.append(vlist)
            # 从开始下标循环
            for i in range(start, length):
                # candidates有序,只要当前大于目标后面都大于,直接break
                if target < candidates[i]:
                    break
                # 这个判断保证不重复例如1,1,2,5,6,7,10,第二个1就会被跳过
                if i > start and candidates[i] == candidates[i - 1]:
                    continue
                # 否则目标值减当前值,i+1为新的起始位置(不用重复数字),把当前值加入当前组合
                else:
                    dfs(target - candidates[i], i + 1, vlist + [candidates[i]])

        dfs(target, 0, [])
        return res

    """
    leetcode 39
    给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

    candidates 中的每个数字在每个组合中次数不限。
    """
    def combinationSum(self, candidates, target):
        # 下面有一个目标值小于某一元素就break,所以先排序
        candidates.sort()
        # 储存返回的二维列表
        res, length = [], len(candidates)

        # 递归,目标值,起始位置,当前组合
        def dfs(target, start, vlist):
            # 目标值为0,表明当前递归完成,把当前递归结果加入res并返回
            if target == 0:
                return res.append(vlist)
            # 从开始下标循环
            for i in range(start, length):
                # candidates有序,只要当前大于目标后面都大于,直接break
                if target < candidates[i]: break
                # 这个判断保证不重复例如1,1,2,5,6,7,10,第二个1就会被跳过
                if i > start and candidates[i] == candidates[i - 1]:
                    continue
                # 否则目标值减当前值,i为用重复数字,把当前值加入当前组合
                else:
                    dfs(target - candidates[i], i, vlist + [candidates[i]])

        dfs(target, 0, [])
        return res

27. leetcode372 – 超级次方 (power的复杂度是log(n))

class Solution:
    def myPow(self, x: float, n: int) -> float:
        # return pow(x, n)
        i = n
        if i < 0:
            i = -i

        res = 1
        while i != 0:
            if i % 2 != 0:
                res *= x
            x *= x
            i = i // 2
        return res if n > 0 else 1 / res

    def superPow(self, a, b):
        """
        你的任务是计算 ab 对 1337 取模,a 是一个正整数,b 是一个非常大的正整数且会以数组形式给出。

        示例 1:
        输入: a = 2, b = [3]
        输出: 8
        示例 2:
        
        输入: a = 2, b = [1,0]
        输出: 1024
        """
        def modn(a, b, c):
            res = 1
            while b != 0:
                if (b & 1):
                    res *= a
                    res %= c
                a *= a
                a %= c
                b >>= 1
            return res

        power = 0
        for i in b:
            power = power * 10 + i
        a = a % 1337
        res = modn(a, power, 1337)
        return res % 1337


if __name__ == '__main__':
    a = 2
    b = [3, 0, 0]
    print(Solution().superPow(a, b))

28. leetcode668 – 给定高度m 、宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k 小的数字(二分查找,逆向思维)

给定高度m 、宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k 小的数字。
例 1:
输入: m = 3, n = 3, k = 5
输出: 3
解释:
乘法表:
1 2 3
2 4 6
3 6 9
第5小的数字是 3 (1, 2, 2, 3, 3).

class Solution:
    def findKthNumber(self, m: int, n: int, k: int) -> int:
        def cnt_before_x(x):
            cnt = 0
            for i in range(1, m + 1):
                cnt += min(x // i, n)
            return cnt

        def binary_search(left, right, k):
            while left < right:
                mid = (left + right) >> 1
                cnt = cnt_before_x(mid)
                if cnt >= k:
                    right = mid
                else:
                    left = mid + 1
            return left

        left = 1
        right = m * n + 1
        kth = binary_search(left, right, k)
        return kth




  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值