剑指Offer刷题(Python)

矩阵中的路径  

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。

路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。

如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。

注意:

  • 输入的路径不为空;
  • 所有出现的字符均为大写英文字母;

样例

matrix=
[
  ["A","B","C","E"],
  ["S","F","C","S"],
  ["A","D","E","E"]
]

str="BCCE" , return "true" 

str="ASAE" , return "false"

算法:

经典DFS

O(mnk)

class Solution(object):
    def __init__(self):
        self.direction = [[-1,0],[1,0],[0,-1],[0,1]]
    
    def hasPath(self, matrix, string):
        """
        :type matrix: List[List[str]]
        :type string: str
        :rtype: bool
        """
        if not len(matrix) or not len(matrix[0]):
            return False
            
        self.lenX = len(matrix)
        self.lenY = len(matrix[0])
        self.matrix = matrix
        self.string = string
        self.isVisit = [[0 for j in range(self.lenY)] for i in range(self.lenX)]
        
        for x in range(self.lenX):
            for y in range(self.lenY):
                if self.dfs(x, y, 0):
                    return True
                    
        return False
        
    
    def dfs(self, x, y, n):
        if n == len(self.string):
            return True
            
        if x >= self.lenX or x < 0 or y < 0 or y >= self.lenY or self.isVisit[x][y] or self.matrix[x][y] != self.string[n]:
            return False
            
        self.isVisit[x][y] = 1
        for i,j in self.direction:
            if self.dfs(x+i, y+j, n+1):
                    return True
                    
        self.isVisit[x][y] = 0
        return False

机器人的运动范围

地上有一个 mm 行和 nn 列的方格,横纵坐标范围分别是 0∼m−10∼m−1 和 0∼n−10∼n−1。

一个机器人从坐标 (0,0)(0,0) 的格子开始移动,每一次只能向左,右,上,下四个方向移动一格。

但是不能进入行坐标和列坐标的数位之和大于 kk 的格子。

请问该机器人能够达到多少个格子?

样例1

输入:k=7, m=4, n=5

输出:20

样例2

输入:k=18, m=40, n=40

输出:1484

解释:当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。
      但是,它不能进入方格(35,38),因为3+5+3+8 = 19。

注意:

  1. 0<=m<=50
  2. 0<=n<=50
  3. 0<=k<=100


算法

(BFS)

O(nm)
这是一个典型的宽度优先搜索问题,我们从 (0, 0) 点开始,每次朝上下左右四个方向扩展新的节点即可。

扩展时需要注意新的节点需要满足如下条件:

之前没有遍历过,这个可以用个bool数组来判断;
没有走出边界;
横纵坐标的各位数字之和小于 k;
最后答案就是所有遍历过的合法的节点个数。

时间复杂度
每个节点最多只会入队一次,所以时间复杂度不会超过方格中的节点个数。
最坏情况下会遍历方格中的所有点,所以时间复杂度就是 O(nm)。

class Solution(object):
    def __init__(self):
        self.dirs = [[1,0],[-1,0],[0,1],[0,-1]]

    def movingCount(self, threshold, rows, cols):
        """
        :type threshold: int
        :type rows: int
        :type cols: int
        :rtype: int
        """
        if threshold < 0:
            return 0

        self.rows = rows
        self.cols = cols
        self.threshold = threshold
        self.reachCnt = 0
        self.visited = [[0 for y in range(cols)] for x in range(rows)]

        self.bfs(0,0)

        return self.reachCnt

    def bfs(self,i,j):
        if i < 0 or i >= self.rows or j < 0 or j >= self.cols or self.visited[i][j]:
            return

        self.visited[i][j] = 1
        res = self.getSum(i) + self.getSum(j)
        if res <= self.threshold:
            self.reachCnt += 1
            for x,y in self.dirs:
                self.bfs(i+x,j+y)

    def getSum(self,num):
        res = 0
        while num:
            res += num%10
            num = num//10
        return res

剪绳子*

给你一根长度为 nn 绳子,请把绳子剪成 mm 段(mm、nn 都是整数,2≤n≤582≤n≤58 并且 m≥2m≥2)。

每段的绳子的长度记为 k[1]、k[2]、……、k[m]k[1]、k[2]、……、k[m]。

k[1]k[2]…k[m]k[1]k[2]…k[m] 可能的最大乘积是多少?

例如当绳子的长度是 88 时,我们把它剪成长度分别为 2、3、32、3、3 的三段,此时得到最大的乘积 1818。

样例

输入:8

输出:18

 尽量多出现3, 但是当只剩下4时,2*2 > 3*1,此时需要出现2个2

class Solution(object):
    def maxProductAfterCutting(self, length):
        """
        :type length: int
        :rtype: int
        """
        if length < 3:
            return length-1
        left = length%3
        if left == 0:
            return pow(3,length//3)
        elif left == 1:
            return pow(3,length//3-1)*4
        else:
            return pow(3,length//3)*2

二进制中1的个数

输入一个 3232 位整数,输出该数二进制表示中 11 的个数。

注意

  • 负数在计算机中用其绝对值的补码来表示。

样例1

输入:9
输出:2
解释:9的二进制表示是1001,一共有2个1。

样例2 

输入:-2
输出:31
解释:-2在计算机里会被表示成11111111111111111111111111111110,
      一共有31个1。

算法
(位运算) O(logn)
迭代进行如下两步,直到 n变成0为止:

如果 nn 在二进制表示下末尾是1,则在答案中加1;
将 nn 右移一位,也就是将 nn 在二进制表示下的最后一位删掉;
这里有个难点是如何处理负数。
在C++中如果我们右移一个负整数,系统会自动在最高位补1,这样会导致 n 永远不为0,就死循环了。(负数的二进制是正数每位取反(反码),再加一(即补码))
解决办法是把 n强制转化成无符号整型,这样 nn 的二进制表示不会发生改变,但在右移时系统会自动在最高位补0。

时间复杂度
每次会将 n 除以2,最多会除 logn次,所以时间复杂度是 O(logn)。

class Solution(object):
    def NumberOf1(self,n):
        """
        :type n: int
        :rtype: int
        """
        res = 0
        cur = 1
        cnt = 0
        while cnt < 32:
            if n & cur:
                res += 1
            cur = cur << 1
            cnt += 1
        return res

数值的整数次方(快速幂)

实现函数double Power(double base, int exponent),求base的 exponent次方。

不得使用库函数,同时不需要考虑大数问题。

只要输出结果与答案的绝对误差不超过 10−210−2 即视为正确。

注意:

  • 不会出现底数和指数同为0的情况
  • 当底数为0时,指数一定为正

样例1

输入:10 ,2

输出:100

样例2

输入:10 ,-2  

输出:0.01

思路:

快速幂,把十进制转成二进制

class Solution(object):
    def Power(self, base, exponent):
        """
        :type base: float
        :type exponent: int
        :rtype: float
        """
        if base == 0 and exponent == 0:
            raise IOError("Base and exponent can not be both 0")
        if base == 0:
            return 0.0
        if exponent == 0:
            return 1.0

        op = exponent < 0
        exp = abs(exponent)
        result = 1.0
        while exp:
            if exp & 1:
                result *= base
            base *= base
            exp = exp >> 1

        if op:
            return 1.0 / result
        else:
            return result

在O(1)时间删除链表结点

给定单向链表的一个节点指针,定义一个函数在O(1)时间删除该结点。

假设链表一定存在,并且该节点一定不是尾节点。

样例

输入:链表 1->4->6->8
      删掉节点:第2个节点即6(头节点为第0个节点)

输出:新链表 1->4->8

思路:

(链表) O(1)
由于是单链表,我们不能找到前驱节点,所以我们不能按常规方法将该节点删除。
我们可以换一种思路,将下一个节点的值复制到当前节点,然后将下一个节点删除即可

时间复杂度
只有常数次操作,所以时间复杂度是 O(1)。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void 
        """
        node.val = node.next.val
        node.next = node.next.next

删除链表中重复的节点

在一个排序的链表中,存在重复的节点,请删除该链表中重复的节点,重复的节点不保留。

样例1

输入:1->2->3->3->4->4->5

输出:1->2->5

样例2

输入:1->1->1->2->3

输出:2->3

数据范围

链表中节点 val 值取值范围 [0,100]。

思路:

难度不大,O(N)遍历删除,使用一个虚拟头节点

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def deleteDuplication(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        dummy = ListNode(-1)
        dummy.next = head
        pre = dummy

        while pre and pre.next:
            cur = pre.next
            nextNode = cur.next
            if nextNode and cur.val == nextNode.val:
                while nextNode and nextNode.val == cur.val:
                    nextNode = nextNode.next
                pre.next = nextNode
            else:
                pre = pre.next

        return dummy.next

正则表达式匹配(hard)

请实现一个函数用来匹配包括'.''*'的正则表达式。

模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。

在本题中,匹配是指字符串的所有字符匹配整个模式。

例如,字符串"aaa"与模式"a.a""ab*ac*a"匹配,但是与"aa.a""ab*a"均不匹配。

样例

输入:

s="aa"
p="a*"

输出:true

思路:

注意.*这种情况

递归:

class Solution(object):
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        if len(p) == 0:
            return len(s) == 0
        if len(s) == 0:
            return len(p) > 1 and p[1] == "*" and self.isMatch(s,p[2:])
        if len(p) == 1:
            return len(s) == 1 and (s[0] == p[0] or p[0] == ".")
        if p[0] == "." and p[1] != "*":
            return self.isMatch(s[1:],p[1:])
        if p[1] == "*":
            if p[0] == s[0]:
                i = 1
                while i < len(s) and s[i] == s[0]:
                    i += 1
                return self.isMatch(s,p[2:]) or self.isMatch(s[i:],p[2:])
            elif p[0] == ".":
                if len(s) == 1:
                    return len(p) == 2 or self.isMatch(s,p[2:])
                if len(p) > 2:
                    return self.isMatch(s[1:], p) or self.isMatch(s[1:],p[2:]) or self.isMatch(s,p[2:])
                else:
                    return self.isMatch(s[1:], p)
            else:
                return self.isMatch(s, p[2:])
        if s[0] == p[0]:
            return self.isMatch(s[1:], p[1:])
        else:
            return False

动态规划:

class Solution(object):
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        dp = [[0 for i in range(len(p)+1)] for j in range(len(s)+1)]
        dp[0][0] = 1
        for i in range(2,len(p)+1,2):
            if p[i-1] == "*":
                dp[0][i] = 1
            else:
                break
        for i in range(len(s)):
            for j in range(i,len(p)):
                if s[i] == p[j] or p[j] == ".":
                    dp[i+1][j+1] = dp[i][j]
                elif p[j] == "*":
                    dp[i+1][j+1] = dp[i+1][j+1] or dp[i+1][j-1] or ((p[j-1] == s[i] or p[j-1] == ".") and dp[i][j-1]) or (p[j-1] == "." and dp[i][j+1])
                    k = i+1
                    while k < len(s) and s[k] == s[i]:
                        dp[k+1][j+1] = dp[i+1][j+1]
                        k += 1
        return dp[len(s)][len(p)]

表示数值的字符串

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。

例如,字符串"+100","5e2","-123","3.1416""-1E-16"都表示数值。

但是"12e","1a3.14","1.2.3","+-5""12e+4.3"都不是。

注意:

  1. 小数可以没有整数部分,例如.123等于0.123;
  2. 小数点后面可以没有数字,例如233.等于233.0;
  3. 小数点前面和后面可以有数字,例如233.666;
  4. 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
  5. 当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4;

样例:

输入: "0"

输出: true

思路:

写规则穷举,然而果然还是没穷举完,这题得仔细

穷举规则:

+/-:可以出现在首,或者e之后,但不能出现在尾

e:前后必须有数字(重点)

. : 前后必须有一个数字,注意只有一个点的情况(重点)

所以主要要判断的状态是:是否有数字,有e,有dot已经出现过

class Solution(object):
    def isNumber(self, s):
        """
        :type s: str
        :rtype: bool
        """
        symbols = ["+", "-"]
        exp = ["e", "E"]
        dot = "."

        hasExp = False
        hasDot = False
        hasNum = False

        if not s:
            return False
        if len(s) == 1 and (s in symbols or s in exp or s == dot):
            return False

        for i in range(len(s)):
            if s[i] in symbols:
                if (i != 0 and s[i - 1] not in exp) or i == len(s) - 1:
                    return False
            elif s[i] in exp:
                if i == 0 or i == len(s) - 1 or not hasNum or hasExp:
                    return False
                else:
                    hasExp = True
            elif s[i] == dot:
                if hasDot or hasExp or (i == len(s)-1 and not hasNum):
                    return False
                else:
                    hasDot = True
            elif not s[i].isdigit():
                return False
            else:
                hasNum = True
        return True

这题也可以用AC自动机做,具体可以见我写字符串的那篇博文

调整数组顺序使奇数位于偶数前面

输入一个整数数组,实现一个函数来调整该数组中数字的顺序。

使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分。

样例

输入:[1,2,3,4,5]

输出: [1,3,5,2,4]

思路:

不要求保持顺序

直接双指针交换,O(n)时间

class Solution(object):
    def reOrderArray(self, array):
        """
        :type array: List[int]
        :rtype: void
        """
        if len(array) < 2:
            return

        i,j = 0,len(array)-1
        while i < j:
            while array[i] & 1 and i <= j:
                i += 1
            while not array[j] & 1 and i <= j:
                j -= 1
            if i < j:
                array[i],array[j] = array[j],array[i]

保持顺序:

O(N)空间,O(N)时间,用一个辅助列表

class Solution(object):
    def reOrderArray(self, array):
        """
        :type array: List[int]
        :rtype: void
        """
        if len(array) < 2:
            return

        newArray = [0 for i in range(len(array))]

        oddCnt = 0
        for num in array:
            if num & 1:
                oddCnt += 1
        
        l,r = 0,oddCnt
        for num in array:
            if num & 1:
                newArray[l] = num
                l += 1
            else:
                newArray[r] = num
                r += 1
              
        return newArray

O(N^2)时间,O(1)空间

class Solution(object):
    def reOrderArray(self, array):
        """
        :type array: List[int]
        :rtype: void
        """
        if len(array) < 2:
            return

        i,j = 0,0
        # 左边是已经排好的奇数,i指向排好奇数的下一个位置
        # j指向右边第一个奇数
        while i < len(array) and j < len(array):
            # 找到下一个偶数,即下一个奇数放的位置
            while i < len(array) and array[i] & 1:
                i += 1
            # 找到下一个奇数
            j = i
            while j < len(array) and not array[j] & 1:
                j += 1
            # 冒泡法
            if i < len(array) and j < len(array):
                temp = array[j]
                while j > i:   
                    array[j] = array[j-1]
                    j -= 1
                array[i] = temp

链表中倒数第k个节点

输入一个链表,输出该链表中倒数第 kk 个结点。

注意:

  • k >= 1;
  • 如果 kk 大于链表长度,则返回 NULL;

样例

输入:链表:1->2->3->4->5 ,k=2

输出:4

思路:

双指针,但是要注意第k个就是头节点的情况,这里其实应该思考边界case,具体的时间复杂度是2N,因为两个指针在走

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def findKthToTail(self, pListHead, k):
        """
        :type pListHead: ListNode
        :type k: int
        :rtype: ListNode
        """
        h,kth = pListHead,pListHead
        cnt = 0
        while cnt < k and kth:
            kth = kth.next
            cnt += 1
        # 注意kth是空时,也有可能刚好是要返回头节点
        if not kth:
            if cnt == k:
                return h
            else:
                return None
        while kth:
            h = h.next
            kth = kth.next
        return h

所以其实直接统计长度n,然后找第n-k个也是一样的,相当于复杂度也是2n

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def findKthToTail(self, pListHead, k):
        """
        :type pListHead: ListNode
        :type k: int
        :rtype: ListNode
        """
        # ;链表长度n
        p = pListHead
        length = 0
        while p:
            p = p.next
            length += 1
        if length < k:
            return None
        # 找第n-k个
        p = pListHead
        for i in range(length-k):
            p = p.next
        return p

反转链表

定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。

思考题:

  • 请同时实现迭代版本和递归版本

样例

输入:1->2->3->4->5->NULL

输出:5->4->3->2->1->NULL

头插法:

# Definition for singly-linked list.
# 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
        """
        dummy = ListNode(-1)
        p = head
        while p:
            temp = p.next
            q = dummy.next
            dummy.next = p
            p.next = q
            p = temp
        return dummy.next

递归:

这里需要注意的是,reverse head.next之后,其实head.next就是新链表的尾,所以可以用head.next.next来将head插入到反转链表的尾部

空间复杂度分析:总共递归 nn 层,系统栈的空间复杂度是 O(n),所以总共需要额外 O(n)的空间。
时间复杂度分析:链表中每个节点只被遍历一次,所以时间复杂度是 O(n)。

# Definition for singly-linked list.
# 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 or not head.next:
            return head
        
        tail = self.reverseList(head.next)
        head.next.next = head
        head.next = None
        
        return tail

合并两个排序的链表

输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。

样例

输入:1->3->5 , 2->4->5

输出:1->2->3->4->5->5

思路:

时间O(m+n)

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def merge(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        dummy = ListNode(-1)
        cur = dummy
        h1 = l1
        h2 = l2
        while h1 and h2:
            if h1.val <= h2.val:
                cur.next = h1
                h1 = h1.next
            else:
                cur.next = h2
                h2 = h2.next
            cur = cur.next
        if h2:
            h1 = h2
        cur.next = h1
        return dummy.next

树的子结构

输入两棵二叉树 A,BA,B,判断 BB 是不是 AA 的子结构。

我们规定空树不是任何树的子结构。

样例

树 AA:

     8
    / \
   8   7
  / \
 9   2
    / \
   4   7

树 BB:

   8
  / \
 9   2

返回 true,因为 BB 是 AA 的子结构。

思路:

递归

最坏情况下,我们对于树A中的每个节点都要递归判断一遍,每次判断在最坏情况下需要遍历完树B中的所有节点。
所以时间复杂度是 O(nm),其中 n 是树A中的节点数, m 是树B中的节点数。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def hasSubtree(self, pRoot1, pRoot2):
        """
        :type pRoot1: TreeNode
        :type pRoot2: TreeNode
        :rtype: bool
        """
        if not pRoot1 or not pRoot2:
            return False
        return self.hasSub(pRoot1,pRoot2)
        
    def hasSub(self, pRoot1, pRoot2):
        if not pRoot2:
            return True
            
        if not pRoot1:
            return False
            
        if pRoot1.val == pRoot2.val and self.hasSub(pRoot1.left,pRoot2.left) and self.hasSub(pRoot1.right,pRoot2.right):
            return True
        else:
            return self.hasSub(pRoot1.left,pRoot2) or self.hasSub(pRoot1.right,pRoot2)
        

思路2:

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def hasSubtree(self, pRoot1, pRoot2):
        """
        :type pRoot1: TreeNode
        :type pRoot2: TreeNode
        :rtype: bool
        """
        if not pRoot1 or not pRoot2:
            return False
        if self.isSame(pRoot1,pRoot2):
            return True
        return self.hasSubtree(pRoot1.left,pRoot2) or self.hasSubtree(pRoot1.right,pRoot2)
            
            
    def isSame(self, pRoot1, pRoot2):
        if not pRoot2:
            return True
        if not pRoot1 or pRoot1.val != pRoot2.val:
            return False
        return self.isSame(pRoot1.left,pRoot2.left) and self.isSame(pRoot1.right,pRoot2.right)

二叉树的镜像

输入一个二叉树,将它变换为它的镜像。

样例

输入树:
      8
     / \
    6  10
   / \ / \
  5  7 9 11

 [8,6,10,5,7,9,11,null,null,null,null,null,null,null,null] 
输出树:
      8
     / \
    10  6
   / \ / \
  11 9 7  5

 [8,10,6,11,9,7,5,null,null,null,null,null,null,null,null]

思路:递归

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def mirror(self, root):
        """
        :type root: TreeNode
        :rtype: void
        """
        if not root:
            return
        root.left,root.right = root.right,root.left  
        self.mirror(root.left)
        self.mirror(root.right)

对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。

如果一棵二叉树和它的镜像一样,那么它是对称的。

样例

如下图所示二叉树[1,2,2,3,4,4,3,null,null,null,null,null,null,null,null]为对称二叉树:
    1
   / \
  2   2
 / \ / \
3  4 4  3

如下图所示二叉树[1,2,2,null,4,4,3,null,null,null,null,null,null]不是对称二叉树:
    1
   / \
  2   2
   \ / \
   4 4  3

思路:

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        if not root:
            return True
        return self.isMirror(root.left,root.right)
        
    def isMirror(self,h1,h2):
        if not h1 and not h2:
            return True
        if not h1 or not h2 or h1.val != h2.val:
            return False
        return self.isMirror(h1.left,h2.right) and self.isMirror(h1.right,h2.left)
        

顺时针打印矩阵

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

样例

输入:
[
  [1, 2, 3, 4],
  [5, 6, 7, 8],
  [9,10,11,12]
]

输出:[1,2,3,4,8,12,11,10,9,5,6,7]

思路1:卡4个边界(这个太麻烦了,试了好久)

顺序是:

123

456

789

按照这样的顺序输出,每次将初始点设置,为了能够面对[[1]],这种情况,如果每个边都卡一个点,会导致这种情况进不了任何一个边

class Solution(object):
    def printMatrix(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        result = []

        if len(matrix) == 0 or len(matrix[0]) == 0:
            return result

        m, n = len(matrix), len(matrix[0])
        cnt = 0
        while cnt < (m+1)//2 and cnt < (n+1)//2:
            i, j = cnt, cnt
            while j <= n - cnt -1:
                result.append(matrix[i][j])
                j += 1

            j = n-cnt-1
            i = cnt + 1
            while i < m - cnt - 1:
                result.append(matrix[i][j])
                i += 1

            i = m-cnt-1
            j = n-cnt-1
            while j >= cnt:
                if i > cnt:
                    result.append(matrix[i][j])
                j -= 1

            i = m-cnt-2
            j = cnt
            while i > cnt:
                if j < n-cnt-1:
                    result.append(matrix[i][j])
                i -= 1

            cnt += 1
        return result

思路2:

使用额外O(n*m)的空间,加入一个visited数组,直接通过模拟走的过程来实现

class Solution(object):
    def printMatrix(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        result = []

        if len(matrix) == 0 or len(matrix[0]) == 0:
            return result

        m, n = len(matrix), len(matrix[0])
        visited = [[0 for j in range(n)] for i in range(m)]
        // 方向,右下左上
        dx = [0,1,0,-1]
        dy = [1,0,-1,0]

        i,j,d = 0,0,0
        for cnt in range(0,m*n):
            result.append(matrix[i][j])
            visited[i][j] = 1
            a,b = i+dx[d],j+dy[d]
            // 在能访问的范围内,先按照原始方向走
            if a < 0 or b < 0 or a >= m or b >= n or visited[a][b]:
                d = (d+1)%4
                a, b = i + dx[d], j + dy[d]
            i,j = a,b

        return result

包含min函数的栈(单调栈)

设计一个支持push,pop,top等操作并且可以在O(1)时间内检索出最小元素的堆栈。

  • push(x)–将元素x插入栈中
  • pop()–移除栈顶元素
  • top()–得到栈顶元素
  • getMin()–得到栈中最小元素

样例

MinStack minStack = new MinStack();
minStack.push(-1);
minStack.push(3);
minStack.push(-4);
minStack.getMin();   --> Returns -4.
minStack.pop();
minStack.top();      --> Returns 3.
minStack.getMin();   --> Returns -1.

思路:

(单调栈) O(1)
我们除了维护基本的栈结构之外,还需要维护一个单调栈,来实现返回最小值的操作。
下面介绍如何维护单调栈

当我们向栈中压入一个数时,如果该数 ≤ 单调栈的栈顶元素,则将该数同时压入单调栈中;否则,不压入,这是由于栈具有先进后出性质,所以在该数被弹出之前,栈中一直存在一个数比该数小,所以该数一定不会被当做最小数输出。
当我们从栈中弹出一个数时,如果该数等于单调栈的栈顶元素,则同时将单调栈的栈顶元素弹出
单调栈由于其具有单调性,所以它的栈顶元素,就是当前栈中的最小数。
时间复杂度
四种操作都只有常数次入栈出栈操作,所以时间复杂度都是 O(1).

class MinStack(object):

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.minStack = []
        self.singleStack = []
        # singleStack:单调栈,栈中为单调递减的值
        

    def push(self, x):
        """
        :type x: int
        :rtype: void
        """
        self.minStack.append(x)
        if len(self.singleStack) == 0 or self.singleStack[-1] >= x:
            self.singleStack.append(x)
            
    def pop(self):
        """
        :rtype: void
        """
        if self.minStack:
            if self.singleStack[-1] == self.minStack[-1]:
                self.singleStack.pop()
            self.minStack.pop()
           

    def top(self):
        """
        :rtype: int
        """
        if self.minStack:
            return self.minStack[-1]
        return None
        

    def getMin(self):
        """
        :rtype: int
        """
        if self.singleStack:
            return self.singleStack[-1]
        return None
        


# Your self.minStack object will be instantiated and called as such:
# obj = self.minStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()

栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。

假设压入栈的所有数字均不相等。

例如序列 1,2,3,4,51,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,14,5,3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,24,3,5,1,2 就不可能是该压栈序列的弹出序列。

注意:若两个序列长度不等则视为并不是一个栈的压入、弹出序列。若两个序列都为空,则视为是一个栈的压入、弹出序列。

样例

输入:[1,2,3,4,5]
      [4,5,3,2,1]

输出:true

思路:用一个额外的栈来模拟

时间:O(n)

空间:O(n)

class Solution(object):
    def isPopOrder(self, pushV, popV):
        """
        :type pushV: list[int]
        :type popV: list[int]
        :rtype: bool
        """
        if len(popV) != len(pushV):
            return False
        if not pushV:
            return True
        
        stack = []
        i,j = 0,0
        for i in range(len(pushV)):
            stack.append(pushV[i])
            while stack and stack[-1] == popV[j] and j < len(popV):
                stack.pop()
                j += 1
        return len(stack) == 0

不分行从上往下打印二叉树

从上往下打印出二叉树的每个结点,同一层的结点按照从左到右的顺序打印。

样例

输入如下图所示二叉树[8, 12, 2, null, null, 6, null, 4, null, null, null]
    8
   / \
  12  2
     /
    6
   /
  4

输出:[8, 12, 2, 6, 4]

思路:

用一个队列

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def printFromTopToBottom(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        result = []
        queue = [root]
        while queue:
            p = queue.pop(0)
            if p:
                result.append(p.val)
                queue.append(p.left)
                queue.append(p.right)
        return result

分行从上往下打印二叉树

从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印到一行。

样例

输入如下图所示二叉树[8, 12, 2, null, null, 6, null, 4, null, null, null]
    8
   / \
  12  2
     /
    6
   /
  4

输出:[[8], [12, 2], [6], [4]]

分行从上往下打印二叉树

从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印到一行。

样例

输入如下图所示二叉树[8, 12, 2, null, null, 6, null, 4, null, null, null]
    8
   / \
  12  2
     /
    6
   /
  4

输出:[[8], [12, 2], [6], [4]]

思路:

在上一道题 《不分行从上往下打印二叉树》 的基础上修改代码。

区别在于,每一层结束的时候,往queue里塞一个NULL做标记

在queue里读取一个数出来之后,先看看是不是level标识符NULL(因为是BFS,当前level读完,下一个level有哪些要读的也都放在queue里了,可以在queue结尾给加一个新的NULL), 是的话再看看是不是整个树读完了(即queue里没有点了)。

时间复杂度分析:每个点遍历一次

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def printFromTopToBottom(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if not root:
            return []
            
        result = [[]]
        queue = [root,None]
        while queue:
            p = queue.pop(0)
            if p:
                result[-1].append(p.val)
                if p.left:
                    queue.append(p.left)
                if p.right:
                    queue.append(p.right)
            # queue非空才加入标志,否则会死循环
            elif queue:
                result.append([])
                queue.append(None)
        return result
        
        

思路2:两个queue

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution(object):
    def printFromTopToBottom(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if not root:
            return []

        result,level,newQueue = [],[],[]
        queue = [root]
        while queue or newQueue:
            p = queue.pop(0)
            level.append(p.val)
            if p.left:
                newQueue.append(p.left)
            if p.right:
                newQueue.append(p.right)
            if not queue:
                result.append(level)
                queue = newQueue
                newQueue = []
                level = []
        return result

之字形打印二叉树(*)

请实现一个函数按照之字形顺序从上向下打印二叉树。

即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。

样例

输入如下图所示二叉树[8, 12, 2, null, null, 6, 4, null, null, null, null]
    8
   / \
  12  2
     / \
    6   4
输出:[[8], [2, 12], [6, 4]]

思路:

层次遍历变种,使用栈

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def printFromTopToBottom(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        result = []
        if not root:
            return result

        levelNum = 1
        newStack,level = [],[]
        stack = [root]
        while stack:
            # 奇数层
            if levelNum & 1:
                p = stack.pop()
                if p.left:
                    newStack.append(p.left)
                if p.right:
                    newStack.append(p.right)
            # 偶数层
            else:
                p = stack.pop()
                if p.right:
                    newStack.append(p.right)
                if p.left:
                    newStack.append(p.left)
            level.append(p.val)
            if not stack:
                result.append(level)
                levelNum += 1
                stack = newStack
                newStack,level = [],[]
        return result       
        


二叉搜索树的后序遍历序列(*)

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。

如果是则返回true,否则返回false。

假设输入的数组的任意两个数字都互不相同。

样例

输入:[4, 8, 6, 12, 16, 14, 10]

输出:true

思路:

class Solution:
    def verifySequenceOfBST(self, sequence):
        """
        :type sequence: List[int]
        :rtype: bool
        """
        if len(sequence) < 2:
            return True
            
        root = sequence[-1]
        left,right = [],[]
        i = 0
        while i < len(sequence)-1 and sequence[i] <= root:
            left.append(sequence[i])
            i += 1
        while i < len(sequence)-1:
            if sequence[i] < root:
                return False
            right.append(sequence[i])
            i += 1
        return self.verifySequenceOfBST(left) and self.verifySequenceOfBST(right)
                

二叉树中和为某一值的路径

输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。

从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

保证树中结点值均不小于 00。

数据范围

树中结点的数量 [0,1000][0,1000]。

样例

给出二叉树如下所示,并给出num=22。
      5
     / \
    4   6
   /   / \
  12  13  6
 /  \    / \
9    1  5   1

输出:[[5,4,12,1],[5,6,6,5]]

思路:

注意python写这题得copy

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
import copy

class Solution(object):
    def __init__(self):
        self.result = []

    def findPath(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: List[List[int]]
        """
        self.getPath(root,sum,[])

        return self.result

    def getPath(self, root, sum, path):
        if root:
            path.append(root.val)
            if sum == root.val and not root.left and not root.right:
                self.result.append(path)
            elif sum > root.val:
                self.getPath(root.left, sum - root.val, copy.deepcopy(path))
                self.getPath(root.right, sum - root.val, copy.deepcopy(path))

复杂链表的复刻

请实现一个函数可以复制一个复杂链表。

在复杂链表中,每个结点除了有一个指针指向下一个结点外,还有一个额外的指针指向链表中的任意结点或者null。

注意

  • 函数结束后原链表要与输入时保持一致。

数据范围

链表长度不超过 500500。

思路:

用一个字典记录每个点对应的节点

# Definition for singly-linked list with a random pointer.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#         self.random = None
class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head:
            return None
        mapDict = {}
        p = head
        while p:
            q = ListNode(p.val)
            mapDict[p] = q
            p = p.next
        p = head
        while p:
            if p.next:
                mapDict[p].next = mapDict[p.next]
            if p.random:
                mapDict[p].random = mapDict[p.random]
            p = p.next
        return mapDict[head]
        

遍历一遍的写法,边建立链表边建立字典:

# Definition for singly-linked list with a random pointer.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#         self.random = None
class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head:
            return None
        mapDict = {None:None}
        p = head
        while p:
            if p not in mapDict:
                mapDict[p] = ListNode(p.val)
            if p.next not in mapDict:
                mapDict[p.next] = ListNode(p.next.val)
            if p.random not in mapDict:
                mapDict[p.random] = ListNode(p.random.val)
            
            q = mapDict[p]
            q.next = mapDict[p.next]
            q.random = mapDict[p.random]
            
            p = p.next
        return mapDict[head]
        

二叉搜索树与双向链表(*)

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。

要求不能创建任何新的结点,只能调整树中结点指针的指向。

注意

  • 需要返回双向链表最左侧的节点。

例如,输入下图中左边的二叉搜索树,则输出右边的排序双向链表。

QQ截图20181202052830.png

数据范围

树中节点数量 [0,500][0,500]。

思路:

二叉树的中序遍历变种

class Solution(object):
    def convert(self, root):
        """
        :type root: TreeNode
        :rtype: TreeNode
        """
        
        stack = []
        p = root
        head,pre = None,None
        isFirst = True
        while stack or p:
            if p:
                stack.append(p)
                p = p.left
            else:
                p = stack.pop(-1)
                if isFirst:
                    head = p
                    isFirst = False
                if pre:
                    pre.right = p
                    p.left = pre
                pre = p
                p = p.right
        
        return head

补:

二叉树前序遍历非递归:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res,stack = [],[]
        p = root
        while p or stack:
            if p:
                res.append(p.val)
                stack.append(p)
                p = p.left
            else:
                p = stack.pop(-1)
                p = p.right
        return res

中序

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res,stack = [],[]
        p = root
        while p or stack:
            if p:
                stack.append(p)
                p = p.left
            else:
                p = stack.pop(-1)
                res.append(p.val)
                p = p.right
        return res

后序

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res,stack = [],[]
        p = root
        while p or stack:
            if p:
                res.append(p.val)
                stack.append(p)
                p = p.right
            else:
                p = stack.pop(-1)
                p = p.left
        res.reverse()
        
        return res

序列化二叉树(*)

请实现两个函数,分别用来序列化和反序列化二叉树。

您需要确保二叉树可以序列化为字符串,并且可以将此字符串反序列化为原始树结构。

数据范围

树中节点数量 [0,1000][0,1000]。

样例

你可以序列化如下的二叉树
    8
   / \
  12  2
     / \
    6   4

为:"[8, 12, 2, null, null, 6, 4, null, null, null, null]"

注意:

  • 以上的格式是AcWing序列化二叉树的方式,你不必一定按照此格式,所以可以设计出一些新的构造方式。

思路:

试了下发现层次遍历比较好写

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:

    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        if not root:
            return None
        
        res,queue = [root],[root]
        while queue:
            length = len(queue)
            for i in range(length):
                q = queue.pop(0)
                if q:
                    queue.append(q.left)
                    queue.append(q.right)
            res += queue
        for i in range(len(res)):
            if res[i]:
                res[i] = str(res[i].val)
            else:
                res[i] = "None"
        return '#'.join(res)
        
    def deserialize(self, data):
        """Decodes your encoded data to tree.
        
        :type data: str
        :rtype: TreeNode
        """
        if not data:
            return None
            
        res = data.split('#')
        root = TreeNode(res[0])
        queue = [root]
        i = 1
        while queue:
            length = len(queue)
            for j in range(length):
                q = queue.pop(0)
                if res[i] != "None":
                    q.left = TreeNode(res[i])
                    queue.append(q.left)
                i += 1
                if res[i] != "None":
                    q.right = TreeNode(res[i])
                    queue.append(q.right)
                i += 1
        return root
                
        

数字排列

输入一组数字(可能包含重复数字),输出其所有的排列方式。

数据范围

输入数组长度 [0,6]。

样例

输入:[1,2,3]

输出:
      [
        [1,2,3],
        [1,3,2],
        [2,1,3],
        [2,3,1],
        [3,1,2],
        [3,2,1]
      ]

思路:DFS

class Solution:
    def permutation(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        visited = [0 for num in nums]
        return self.dfs(nums,visited)
        
    def dfs(self,nums,visited):
        res = []
        i = 0
        while i < len(nums):
            if not visited[i]:
                cur = [nums[i]]
                visited[i] = 1
                pers = self.dfs(nums,visited)
                if pers:
                    for per in pers:
                        res.append(cur+per)
                else:
                    res.append(cur)
                visited[i] = 0
                cur = nums[i]
                while i < len(nums) and cur == nums[i]:
                    i += 1
            else:
                i += 1
        return res
                

数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

假设数组非空,并且一定存在满足条件的数字。

思考题

  • 假设要求只能使用 O(n)的时间和额外 O(1) 的空间,该怎么做呢?

数据范围

数组长度 [1,1000][1,1000]。

样例

输入:[1,2,1,1,3]

输出:1

思路:

class Solution(object):
    def moreThanHalfNum_Solution(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return None
            
        mostNum,cnt = nums[0],1
        for num in nums[1:]:
            if cnt == 0:
                mostNum = num
                cnt = 1
            elif num == mostNum:
                cnt += 1
            else:
                cnt -= 1
        
        return mostNum
                    
        

最小的k个数(*)

输入 n 个整数,找出其中最小的 kk 个数。

注意:

  • 输出数组内元素请按从小到大顺序排序;

数据范围

1≤k≤n≤10001≤k≤n≤1000

样例

输入:[1,2,3,4,5,6,7,8] , k=4

输出:[1,2,3,4]

思路1:大顶堆

时间复杂度:nlogk,适用于海量数据

思路:前k个构建大顶堆,对于k之后的数,与大顶堆堆顶比较,比堆顶小,就替换堆顶,往下调整

class Solution(object):
    def getLeastNumbers_Solution(self, input, k):
        """
        :type input: list[int]
        :type k: int
        :rtype: list[int]
        """
        if k < 1 or not input:
            return []
            
        heap = self.buildHeap([input[i] for i in range(k) if i < len(input)])
        
        for i in range(k,len(input)):
            if heap[0] > input[i]:
                heap[0] = input[i]
                self.adjust(heap,0)
        
        # 堆的pop
        res = []
        while heap:
            res.append(heap[0])
            heap[0] = heap[-1]
            heap.pop(-1)
            self.adjust(heap,0)
        res.reverse()
        return res
        
    # 因为调整堆的函数的写法,这里需要从下往上开始调整堆!
    def buildHeap(self,Input):
        for i in range(len(Input)/2-1,-1,-1):
            self.adjust(Input,i)
        return Input
    
    # 调整堆 
    def adjust(self,heap,i):
        n = len(heap)
        if i >= n:
            return
        maxIdx = i
        if 2*i+1 < n and heap[maxIdx] < heap[2*i+1]:
            maxIdx = 2*i+1
        if 2*i+2 < n and heap[maxIdx] < heap[2*i+2]:
            maxIdx = 2*i+2
        if maxIdx != i:
            heap[maxIdx],heap[i] = heap[i],heap[maxIdx]
            self.adjust(heap,maxIdx)
        

也可以直接调用python的堆函数

import heapq

class Solution(object):
    def getLeastNumbers_Solution(self, input, k):
        """
        :type input: list[int]
        :type k: int
        :rtype: list[int]
        """
        # python只有小顶堆,所以这里用了取负号
        h = [-input[i] for i in range(k) if i < len(input)]
        heapq.heapify(h)
        # 不断与堆顶比较
        for i in range(k,len(input)):
            if -input[i] > h[0]:
                heapq.heappop(h)
                heapq.heappush(h,-input[i])
                
        res = []
        for i in range(k):
            res.append(-heapq.heappop(h))
        res.reverse()
        return res

解法二:快速排序

注意快排最后与pivot交换的位置

class Solution(object):
    def getLeastNumbers_Solution(self, input, k):
        """
        :type input: list[int]
        :type k: int
        :rtype: list[int]
        """
        if k < 0 or k > len(input):
            return None
            
        if k < len(input):
            pivotIdx = -1
            l,r = 0,len(input)-1
            while pivotIdx != k-1:
                pivotIdx = self.findPivot(input,l,r)
                if pivotIdx > k-1:
                    r = pivotIdx - 1
                else:
                    l = pivotIdx + 1
                
        self.quickSort(input,0,k-1)
        
        return input[0:k]
                

    def findPivot(self,nums,l,r):
        pivot = nums[l]
        i,j = l,r
        while i < j:
            while i <= j and nums[i] <= pivot:
                i += 1
            
            while j >= i and nums[j] >= pivot:
                j -= 1
                
            if i < j:
                nums[i],nums[j] = nums[j],nums[i]
            
        nums[l],nums[j] = nums[j],nums[l]
        return j
        
    def quickSort(self,nums,l,r):
        if l >= r:
            return
        
        pivotIdx = self.findPivot(nums,l,r)
        self.quickSort(nums,l,pivotIdx-1)
        self.quickSort(nums,pivotIdx+1,r)

数据流中的中位数(*)

如何得到一个数据流中的中位数?

如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。

如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

数据范围

数据流中读入的数据总数 [1,1000]。

样例

输入:1, 2, 3, 4

输出:1,1.5,2,2.5

解释:每当数据流读入一个数据,就进行一次判断并输出当前的中位数。

思路:

这题我又忘了系列,参考之前写的

剑指offer刷题全纪录_向着星辰大海-CSDN博客

import heapq

class Solution:
    def __init__(self):
        self.largeHeap = []
        self.smallHeap = []

    def insert(self, num):
        """
        :type num: int
        :rtype: void
        """
        # 如果大顶堆为空,或者小于大顶堆堆顶
        if not self.largeHeap or num <= -self.largeHeap[0]:
            heapq.heappush(self.largeHeap,-num)
        else:
            heapq.heappush(self.smallHeap,num)
          
        # 对堆的大小进行调整
        if len(self.largeHeap)+2 == len(self.smallHeap):
            temp = heapq.heappop(self.smallHeap)
            heapq.heappush(self.largeHeap,-temp)
        elif len(self.largeHeap) == len(self.smallHeap)+2:
            temp = -heapq.heappop(self.largeHeap)
            heapq.heappush(self.smallHeap,temp)

    def getMedian(self):
        """
        :rtype: float
        """
        # 奇数
        if len(self.largeHeap) > len(self.smallHeap):
            return -self.largeHeap[0]
        elif len(self.smallHeap) > len(self.largeHeap):
            return self.smallHeap[0]
        # 偶数
        else:
            return (-self.largeHeap[0] + self.smallHeap[0] + 0.0) / 2

连续子数组的最大和

输入一个 非空 整型数组,数组里的数可能为正,也可能为负。

数组中一个或连续的多个整数组成一个子数组。

求所有子数组的和的最大值。

要求时间复杂度为 O(n)O(n)。

数据范围

数组长度 [1,1000][1,1000]。

样例

输入:[1, -2, 3, 10, -4, 7, 2, -5]

输出:18

思路:

一次遍历即可,当cur_sum小于0时,令当前值为cur_sum,否则累加当前值,并与max_sum比较是否需要更新

class Solution(object):
    def maxSubArray(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
            
        maxSum,curSum = nums[0],nums[0]
        for num in nums[1:]:
            if num+curSum >= num:
                curSum += num
            else:
                curSum = num
            maxSum = max(maxSum,curSum)
        return maxSum

从1到n整数中1出现的次数(*)

输入一个整数 nn,求从 11 到 nn 这 nn 个整数的十进制表示中 11 出现的次数。

例如输入 1212,从 11 到 1212 这些整数中包含 “1”“1” 的数字有 1,10,111,10,11 和 1212,其中 “1”“1” 一共出现了 55 次。

数据范围

1≤n≤10000

样例

输入: 12
输出: 5

思路:

每有1个10,个位就会有1个1

个位1的个数是

n/10+(n%10>1? 1:0)

每有1个100就会有10个1在十位上

十位1的个数是

n/100*10+(n%100>10? min(10,n%100-10+1):0)

class Solution(object):
    def numberOf1Between1AndN_Solution(self, n):
        """
        :type n: int
        :rtype: int
        """
        cnt = 0
        pivot = 1
        while n/(pivot/10) >= 1:
            cnt += (n//pivot)*(pivot/10)
            if n%pivot >= pivot/10:
                cnt += min(pivot/10,n%pivot-pivot/10+1)
            pivot *= 10
        return cnt

数字序列中某一位的数字(*)

数字以 0123456789101112131415…的格式序列化到一个字符序列中。

在这个序列中,第 5 位(从 00 开始计数)是 5,第 13位是 1,第 19 位是 4,等等。

请写一个函数求任意位对应的数字。

数据范围

0≤ 输入数字 ≤2147483647

样例

输入:13

输出:1

思路:我感觉我自己想还是有点混乱

具体可以参考剑指offer,或者直接看代码

class Solution(object):
    def digitAtIndex(self, n):
        """
        :type n: int
        :rtype: int
        """
        base = 9
        length = 1
        cnt = 0
        # 1位数9个,2位数90个,3位数900个...
        while cnt + base*length <= n:
            cnt += base*length
            base *= 10
            length += 1
        
        # 获得当前数字,通过余下的数是第几个来加 
        curNum = pow(10,length-1)+(n-cnt-1)/length
        # 获得n是当前数的第几位
        bit = (n-cnt-1)%length
        # 取出对应位
        return int(str(curNum)[bit])

把数组排成最小的数(*)

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

例如输入数组 [3,32,321],则打印出这 3 个数字能排成的最小数字 321323。

数据范围

数组长度 [0,500]。

样例

输入:[3, 32, 321]

输出:321323

注意:输出数字的格式为字符串。

思路:

这题我还是没啥想法...

(1)我们希望找到一个排序规则,数字根据这个排序规则排序之后能形成排成一个最小数

对于两个数3,321,我们应该比较3321和3213哪个比较小,小的那个排在前面

而不是仅仅比较两个数字哪个值大
(2)自定义sort比较函数

 注意这里是返回1和-1,而不是true和false

import functools

class Solution(object):
    def printMinNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: str
        """
        nums.sort(key=functools.cmp_to_key(self.cmp))
        return "".join([str(num) for num in nums])

    def cmp(self, num1, num2):
        s1 = str(num1) + str(num2)
        s2 = str(num2) + str(num1)
        if s1 > s2:
            return 1
        else:
            return -1

礼物的最大价值

在一个 m×nm×n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 00)。

你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格直到到达棋盘的右下角。

给定一个棋盘及其上面的礼物,请计算你最多能拿到多少价值的礼物?

注意:

  • m,n>0
  • m×n≤1350

样例:

输入:
[
  [2,3,1],
  [1,7,1],
  [4,6,1]
]

输出:19

解释:沿着路径 2→3→7→6→1 可以得到拿到最大价值礼物。

思路:

经典dp题目

这种移动求最优类的题目,一般第一反应就是dp

空间:O(m*n)解法

class Solution(object):
    def getMaxValue(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        if not grid or not grid[0]:
            return 0
        m,n = len(grid),len(grid[0])
        dp = [[0 for i in range(n+1)] for j in range(m+1)]
        for i in range(m):
            for j in range(n):
                dp[i+1][j+1] = max(dp[i][j+1],dp[i+1][j])+grid[i][j]
        return dp[m][n]

空间n解法:

class Solution(object):
    def getMaxValue(self, grid):
        """
        :type grid: List[List[int]]
        :rtype: int
        """
        if not grid or not grid[0]:
            return 0
        m,n = len(grid),len(grid[0])
        dp = [0 for i in range(n+1)]
        for i in range(m):
            for j in range(n):
                dp[j+1] = max(dp[j+1],dp[j])+grid[i][j]
        return dp[n]

最长不含重复字符的子字符串(*)

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

假设字符串中只包含从’a’到’z’的字符。

样例

输入:"abcabc"

输出:3

思路:

用一个字典记录出现过的字符所在下标,并且维护一个左边界,如果新遇到的字符重复,且在左边界右侧,则更新左边界

时间复杂度:O(n)

空间复杂度:O(n)

class Solution:
    def longestSubstringWithoutDuplication(self, s):
        """
        :type s: str
        :rtype: int
        """
        maxLen,left,n = 0,0,len(s)
        visited = {}
        for i in range(n):
            if s[i] in visited:
                left = max(visited[s[i]]+1,left)
            maxLen = max(maxLen,i-left+1)
            visited[s[i]] = i
        return maxLen
        

丑数

我们把只包含质因子 2、3和 5 的数称作丑数(Ugly Number)。

例如 6、8 都是丑数,但 14 不是,因为它包含质因子 7。

求第 n 个丑数的值。

数据范围

1≤n≤1000

样例

输入:5

输出:5

注意:习惯上我们把 1 当做第一个丑数。

思路:

丑数必然是在之前生成的数的基础上,乘以2/3/5生成的,所以存下之前的数,不断迭代

class Solution(object):
    def getUglyNumber(self, n):
        """
        :type n: int
        :rtype: int
        """
        i2,i3,i5 = 0,0,0
        nums = [1]
        for i in range(1,n):
            n2 = nums[i2]*2
            n3 = nums[i3]*3
            n5 = nums[i5]*5
            num = min(n2,min(n3,n5))
            nums.append(num)
            if n2 == num:
                i2 += 1
            if n3 == num:
                i3 += 1
            if n5 == num:
                i5 += 1
        return nums[-1]

字符串中第一个只出现一次的字符

在字符串中找出第一个只出现一次的字符。

如输入"abaccdeff",则输出b

如果字符串中不存在只出现一次的字符,返回 # 字符。

数据范围

输入字符串长度 [0,1000]。

样例:

输入:"abaccdeff"

输出:'b'

思路:这题简单

class Solution:
    def firstNotRepeatingChar(self, s):
        """
        :type s: str
        :rtype: str
        """
        letterDict = {}
        for i in range(len(s)):
            if s[i] not in letterDict:
                letterDict[s[i]] = i
            else:
                letterDict[s[i]] = -1
                
        minIdx,res = len(s),"#"
        for l in letterDict:
            if letterDict[l] >= 0 and letterDict[l] < minIdx:
                res = l
                minIdx = letterDict[l]
        return res

字符流中第一个只出现一次的字符

请实现一个函数用来找出字符流中第一个只出现一次的字符。

例如,当从字符流中只读出前两个字符 go 时,第一个只出现一次的字符是 g

当从该字符流中读出前六个字符 google 时,第一个只出现一次的字符是 l

如果当前字符流没有存在出现一次的字符,返回 # 字符。

数据范围

字符流读入字符数量 [0,1000]。

样例

输入:"google"

输出:"ggg#ll"

解释:每当字符流读入一个字符,就进行一次判断并输出当前的第一个只出现一次的字符。

思路:同前一题

class Solution:
    def __init__(self):
        self.letterDict = {}
        self.idx = 0
    
    def firstAppearingOnce(self):
        """
        :rtype: str
        """
        minIdx,res = self.idx,"#"
        for l in self.letterDict:
            if self.letterDict[l] >= 0 and self.letterDict[l] < minIdx:
                res = l
                minIdx = self.letterDict[l]
        return res
        
    def insert(self, char):
        """
        :type char: str
        :rtype: void
        """
        if char not in self.letterDict:
            self.letterDict[char] = self.idx
            self.idx += 1
        else:
            self.letterDict[char] = -1

数组中的逆序对

在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。

输入一个数组,求出这个数组中的逆序对的总数。

数据范围

给定数组的长度 [0,100][0,100]。

样例

输入:[1,2,3,4,5,6,0]

输出:6

思路:

归并排序,计算逆序对

class Solution(object):
    def __init__(self):
        self.pair = 0
        
    def inversePairs(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        self.MergeSort(nums,0,len(nums)-1)
        return self.pair
        
    def MergeSort(self,nums,begin,end):
        if begin >= end:
            return
        if begin+1 == end:
            if nums[begin] > nums[end]:
                nums[begin],nums[end] = nums[end],nums[begin]
                self.pair += 1
        self.MergeSort(nums,begin,(begin+end)//2)
        self.MergeSort(nums,(begin+end)//2+1,end)
        self.merge(nums,begin,end)
        
    def merge(self,nums,begin,end):
        mid = (begin+end)//2
        p,q = begin,mid+1
        res = []
        while p <= mid and q <= end:
            if nums[p] > nums[q]:
                self.pair += (mid-p+1)
                res.append(nums[q])
                q += 1
            else:
                res.append(nums[p])
                p += 1
        # 注意这里不需要计数了,上面已经计了
        while p <= mid:
            res.append(nums[p])
            p += 1
        while q <= end:
            res.append(nums[q])
            q += 1
        i = begin
        for num in res:
            nums[i] = num
            i += 1

两个链表的第一个公共结点

输入两个链表,找出它们的第一个公共结点。

当不存在公共节点时,返回空节点。

数据范围

链表长度 [1,2000][1,2000]。

样例

给出两个链表如下所示:
A:        a1 → a2
                   ↘
                     c1 → c2 → c3
                   ↗            
B:     b1 → b2 → b3

输出第一个公共节点c1

思路:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def findFirstCommonNode(self, headA, headB):
        """
        :type headA, headB: ListNode
        :rtype: ListNode
        """
        if not headA or not headB:
            return None
        lenA,lenB = 0,0
        p,q = headA, headB
        while p:
            p = p.next
            lenA += 1
        while q:
            q = q.next
            lenB += 1
        p,q = headA, headB
        while lenA > lenB:
            p = p.next
            lenA -= 1
        while lenB > lenA:
            q = q.next
            lenB -= 1
        while p and q:
            if p == q:
                return p
            else:
                p = p.next
                q = q.next
        return None
        

数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数。

例如输入排序数组 [1,2,3,3,3,3,4,5][1,2,3,3,3,3,4,5] 和数字 33,由于 33 在这个数组中出现了 44 次,因此输出 44。

数据范围

数组长度 [0,1000][0,1000]。

样例

输入:[1, 2, 3, 3, 3, 3, 4, 5] ,  3

输出:4

思路:

有序数组的查找:二分查找,时间复杂度log(N)

class Solution(object):
    def getNumberOfK(self, nums, k):
        """
        :type nums: list[int]
        :type k: int
        :rtype: int
        """
        if not nums:
            return 0
        n = len(nums)-1
        begin = self.findBegin(nums,k,0,n)
        if begin == -1:
            return 0
        end = self.findEnd(nums,k,0,n)
        if end == -1:
            return 1
        else:
            return end-begin+1
        
    def findBegin(self,nums,k,begin,end):
        if nums[begin] > k or nums[end] < k:
            return -1
        if begin == end:
            if nums[begin] == k and (begin == 0 or nums[begin-1] != k):
                return begin
            return -1
        
        mid = (begin+end)//2
        if nums[mid] == k and nums[mid-1] != k:
            return mid
        if nums[mid] >= k:
            return self.findBegin(nums,k,begin,mid-1)
        if nums[mid] < k:
            return self.findBegin(nums,k,mid+1,end)
            
    def findEnd(self,nums,k,begin,end):
        if nums[begin] > k or nums[end] < k:
            return -1
        if begin == end:
            if nums[begin] == k and (begin == len(nums)-1 or nums[begin+1] != k):
                return begin
            return -1
        
        mid = (begin+end)//2
        if nums[mid] == k and nums[mid+1] != k:
            return mid
        if nums[mid] > k:
            return self.findEnd(nums,k,begin,mid-1)
        if nums[mid] <= k:
            return self.findEnd(nums,k,mid+1,end)

0到n-1中缺失的数字(*)

一个长度为 n−1n−1 的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围 00 到 n−1n−1 之内。

在范围 00 到 n−1n−1 的 nn 个数字中有且只有一个数字不在该数组中,请找出这个数字。

数据范围

1≤n≤10001≤n≤1000

样例

输入:[0,1,2,4]

输出:3

思路:

如果非有序数组,可以这么做,时间复杂度O(n)

class Solution(object):
    def getMissingNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        i,n = 0,len(nums)
        while i < n:
            if nums[i] == i or nums[i] == n:
                i += 1
            else:
                nums[nums[i]],nums[i] = nums[i],nums[nums[i]]
        
        for i in range(n):
            if nums[i] != i:
                return i
        return n
            

递增数组:

logN二分查找

这道题目给定的是递增数组,假设数组中第一个缺失的数是 xx,那么数组中的数如下所示;

缺失数字.png


从中可以看出,数组左边蓝色部分都满足nums[i] == i,数组右边橙色部分都不满足nums[i] == i,因此我们可以二分出分界点 x 的值。

 另外要注意特殊情况:当所有数都满足nums[i] == i时,表示缺失的是 n。

class Solution(object):
    def getMissingNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
            
        n = len(nums)
        l,r = 0,n-1
        while l < r:
            mid = (l+r)//2
            if nums[mid] == mid:
                l = mid+1
            else:
                r = mid
        if nums[r] == r:
            r += 1
        return r

数组中数值和下标相等的元素

假设一个单调递增的数组里的每个元素都是整数并且是唯一的。

请编程实现一个函数找出数组中任意一个数值等于其下标的元素。

例如,在数组 [−3,−1,1,3,5]中,数字 3 和它的下标相等。

数据范围

数组长度 [1,100]。

样例

输入:[-3, -1, 1, 3, 5]

输出:3

注意:如果不存在,则返回-1。

思路同上

class Solution(object):
    def getNumberSameAsIndex(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        n = len(nums)
        l,r = 0,n-1
        while l < r:
            mid = (l+r)//2
            if mid == nums[mid]:
                return mid
            elif nums[mid] < mid:
                l = mid + 1
            elif nums[mid] > mid:
                r = mid - 1
        if nums[l] == l:
            return l
        return -1

二叉搜索树的第k个结点

给定一棵二叉搜索树,请找出其中的第 k 小的结点。

你可以假设树和 kk 都存在,并且 1≤k≤ 树的总结点数。

数据范围

树中节点数量 [1,500]。

样例

输入:root = [2, 1, 3, null, null, null, null] ,k = 3

    2
   / \
  1   3

输出:3

思路:中序遍历第k个节点

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

# 中序遍历第k个节点
class Solution(object):
    def kthNode(self, root, k):
        """
        :type root: TreeNode
        :type k: int
        :rtype: TreeNode
        """
        if not root:
            return None
            
        cnt = 0
        p = root
        s = []
        while p or s:
            if p:
                s.append(p)
                p = p.left
            else:
                p = s.pop(-1)
                cnt += 1
                if cnt == k:
                    return p
                p = p.right
        return None
        

二叉树的深度

输入一棵二叉树的根结点,求该树的深度。

从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

数据范围

树中节点数量 [0,500]。

样例

输入:二叉树[8, 12, 2, null, null, 6, 4, null, null, null, null]如下图所示:
    8
   / \
  12  2
     / \
    6   4

输出:3

思路:

树的题基本都可以考虑下递归

递归解法:

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def treeDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:
            return 0
        if root:
            return 1+max(self.treeDepth(root.left),self.treeDepth(root.right))

非递归:层次遍历

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def treeDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:
            return 0
        levelNodes,level = [root],0
        while levelNodes:
            level += 1
            n = len(levelNodes)
            for i in range(n):
                p = levelNodes.pop(0)
                if p.left:
                    levelNodes.append(p.left)
                if p.right:
                    levelNodes.append(p.right)
        return level
            

平衡二叉树

输入一棵二叉树的根结点,判断该树是不是平衡二叉树。

如果某二叉树中任意结点的左右子树的深度相差不超过 1,那么它就是一棵平衡二叉树。

注意:

  • 规定空树也是一棵平衡二叉树。

数据范围

树中节点数量 [0,500]。

样例

输入:二叉树[5,7,11,null,null,12,9,null,null,null,null]如下所示,
    5
   / \
  7  11
    /  \
   12   9

输出:true

思路:

递归

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def isBalanced(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        if not root:
            return True
        l = self.getDepth(root.left)
        r = self.getDepth(root.right)
        if abs(l-r) > 1:
            return False
        return self.isBalanced(root.left) and self.isBalanced(root.right)
    
    def getDepth(self,root):
        if not root:
            return 0
        return 1+max(self.getDepth(root.left),self.getDepth(root.right))

递归+剪枝:

解法1有很明显的问题,在判断上层结点的时候,会多次重复遍历下层结点,增加了不必要的开销。如果改为从下往上遍历如果子树是平衡二叉树,则返回子树的高度;如果发现子树不是平衡二叉树,则直接停止遍历,这样至多只对每个结点访问一次。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def isBalanced(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        if not root:
            return True
        if self.getDepth(root) == -1:
            return False
        return True
    
    def getDepth(self,root):
        if not root:
            return 0
        l = self.getDepth(root.left)
        r = self.getDepth(root.right)
        if l == -1 or r == -1 or abs(l-r) > 1:
            return -1
        return 1+max(l,r)

数组中只出现一次的两个数字(*)

一个整型数组里除了两个数字之外,其他的数字都出现了两次。

请写程序找出这两个只出现一次的数字。

你可以假设这两个数字一定存在。

数据范围

数组长度 [1,1000]。

样例

输入:[1,2,3,3,4,4]

输出:[1,2]

思路:

解题关键:两个相同数字,异或结果为0

(1)故整个数组异或后,得到两个只出现一次的数字的异或结果。

这题的难点是在最后得到ab异或的结果以后,怎样去将ab分开

(2)我们取出异或结果其中任意一位为‘1’的位,用来后面去区分a,b

就拿 {1,1,2,2,3,5} 来说,如果我们将其全部异或起来,我们知道相同的两个数异或的话为0,那么两个1,两个2,都抵消了,就剩3和5异或起来,那么就是二进制的11和101异或,得到110。

然后我们先初始化diff为1,如果最右端就是1,那么diff与a_xor_b去做与会得到1,否则是0,我们左移diff,用10再去做与,此时得到结果11,找到最右端第一个1了

(3)用diff来和数组中每个数字相与

根据结果的不同,一定可以把3和5区分开来,而其他的数字由于是成对出现,所以区分开来也是成对的,最终都会异或成0,不会3和5产生影响。分别将两个小组中的数字都异或起来,就可以得到最终结果了

class Solution(object):
    def findNumsAppearOnce(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        # 找到两个不同数字都异或
        flag = 0
        for num in nums:
            flag ^= num
        
        # 把两个数字通过异或之后有1的那位,分到两个数组
        diff = 1
        while not diff&flag:
            diff <<= 1
        s1,s2 = [],[]
        for num in nums:
            if num & diff:
                s1.append(num)
            else:
                s2.append(num)
        
        # 两个数组内进行异或
        num1,num2 = 0,0
        for num in s1:
            num1 ^= num
        for num in s2:
            num2 ^= num
        return [num1,num2]
        

数组中唯一只出现一次的数字(*)

在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次。

请找出那个只出现一次的数字。

你可以假设满足条件的数字一定存在。

思考题:

  • 如果要求只使用 O(n)的时间和额外 O(1) 的空间,该怎么做呢?

数据范围

数组长度 [1,1500]。

样例

输入:[1,1,1,2,2,2,3,4,4,4]

输出:3

思路:

这题我没思路

一个int一共32位,统计每一位上1出现的次数,如果能被3整除,则出现1次那个数在该位上为0,否则为1

class Solution(object):
    def findNumberAppearingOnce(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        res = 0
        for i in range(32):
            flag = 1 << i
            cnt = 0
            for num in nums:
                if num & flag:
                    cnt += 1
            if cnt%3:
                res ^= flag
        return res

翻转单词顺序(*)

输入一个英文句子,单词之间用一个空格隔开,且句首和句尾没有多余空格

翻转句子中单词的顺序,但单词内字符的顺序不变。

为简单起见,标点符号和普通字母一样处理。

例如输入字符串"I am a student.",则输出"student. a am I"

数据范围

输入字符串长度 [0,1000]。

样例

输入:"I am a student."

输出:"student. a am I"

思路:

先整句翻转,然后再翻转每个单词

class Solution(object):
    def reverseWords(self, s):
        """
        :type s: str
        :rtype: str
        """
        s = list(s)
        self.reverse(s,0,len(s)-1)
        l,r = 0,1
        while r <= len(s):
            if r == len(s) or s[r] == " ":
                self.reverse(s,l,r-1)
                l = r+1
            r += 1
        return ''.join(s)    
        
    def reverse(self,s,l,r):
        if l >= r:
            return
        if r >= len(s):
            r = len(s)-1
        while l < r:
            s[l],s[r] = s[r],s[l]
            l += 1
            r -= 1
        

左旋转字符串(*)

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。

请定义一个函数实现字符串左旋转操作的功能。

比如输入字符串"abcdefg"和数字 2,该函数将返回左旋转 2 位得到的结果"cdefgab"

注意:

  • 数据保证 n 小于等于输入字符串的长度。

数据范围

输入字符串长度 [0,1000]。

样例

输入:"abcdefg" , n=2

输出:"cdefgab"

思路:

先整体翻转一次,而后再分界点前后各自翻转一次

class Solution(object):
    def leftRotateString(self, s, n):
        """
        :type s: str
        :type n: int
        :rtype: str
        """
        s = list(s)
        self.reverse(s,0,n-1)
        self.reverse(s,n,len(s)-1)
        self.reverse(s,0,len(s)-1)
        return ''.join(s)
        
    def reverse(self,s,l,r):
        if l >= len(s):
            return 
        if r >= len(s):
            r = len(s)-1
        while l < r:
            s[l],s[r] = s[r],s[l]
            l += 1
            r -= 1

        

滑动窗口的最大值(*)

给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。

例如,如果输入数组 [2,3,4,2,6,2,5,1]及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,它们的最大值分别为 [4,4,6,6,6,5]。

注意:

  • 数据保证 k 大于 0,且 k 小于等于数组长度。

数据范围

数组长度 [1,1000]。

样例

输入:[2, 3, 4, 2, 6, 2, 5, 1] , k=3

输出: [4, 4, 6, 6, 6, 5]

思路:

双端队列维护一个区间的最大值,这里入队的是数组的下标

class Solution(object):
    def maxInWindows(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        if k >= len(nums):
            return [max(nums)]
        queue,res = [],[]
        for i in range(len(nums)):
            while queue and queue[0]+k <= i:
                queue.pop(0)
            # 从后往前删除比当前数小的数
            while queue and nums[queue[-1]] <= nums[i]:
                queue.pop(-1)
            queue.append(i)
            if k and i >= k-1:
                res.append(nums[queue[0]])
        return res
            

骰子的点数(*)

将一个骰子投掷 n 次,获得的总点数为 s,s 的可能范围为 n∼6n。

掷出某一点数,可能有多种掷法,例如投掷 2 次,掷出 3 点,共有 [1,2],[2,1] 两种掷法。

请求出投掷 n 次,掷出 n∼6n 点分别有多少种掷法。

数据范围

1≤n≤10

样例1

输入:n=1

输出:[1, 1, 1, 1, 1, 1]

解释:投掷1次,可能出现的点数为1-6,共计6种。每种点数都只有1种掷法。所以输出[1, 1, 1, 1, 1, 1]。

样例2

输入:n=2

输出:[1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]

解释:投掷2次,可能出现的点数为2-12,共计11种。每种点数可能掷法数目分别为1,2,3,4,5,6,5,4,3,2,1。

      所以输出[1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]。

思路

dp,i 等于1-6,f[n][sum] = f[n-1][sum-i],不断递推下去

class Solution(object):
    def numberOfDice(self, n):
        """
        :type n: int
        :rtype: List[int]
        """
        f = [[0 for j in range(6*n+1)] for i in range(2)]
        for i in range(1,7):
            f[1][i] = 1
        for i in range(2,n+1):
            cur,pre = i%2,(i-1)%2
            for j in range(i,6*i+1):
                f[cur][j] = 0
                for k in range(1,7):
                    # 注意这里有一个边界判断
                    if j-k >= i-1:
                        f[cur][j] += f[pre][j-k]
        res = [f[n%2][i] for i in range(n,6*n+1)]
        return res

扑克牌的顺子

从扑克牌中随机抽 55 张牌,判断是不是一个顺子,即这 55 张牌是不是连续的。

2∼10 为数字本身,A 为 1,J 为 11,Q 为 12,K 为 13,大小王可以看做任意数字。

为了方便,大小王均以 0 来表示,并且假设这副牌中大小王均有两张。

样例1

输入:[8,9,10,11,12]

输出:true

样例2

输入:[0,8,9,11,12]

输出:true

思路:

判断0的个数

class Solution(object):
    def isContinuous(self, numbers):
        """
        :type numbers: List[int]
        :rtype: bool
        """
        if not numbers or len(numbers) < 5:
             return False
        numbers.sort()
        zeroCnt = 0
        for i in range(0,len(numbers)):
            if numbers[i] == 0:
                zeroCnt += 1
            elif i > 0 and numbers[i-1] != 0:
                flag = numbers[i]-numbers[i-1] -1
                if flag < 0:
                    return False
                zeroCnt -= flag
                if zeroCnt < 0:
                    return False
        return True

圆圈中最后剩下的数字(*)

0,1,…,n−1 这 n 个数字 (n>0) 排成一个圆圈,从数字 0 开始每次从这个圆圈里删除第 m 个数字。

求出这个圆圈里剩下的最后一个数字。

数据范围

1≤n,m≤1000

样例

输入:n=5 , m=3

输出:3

思路1:

环形列表模拟这个过程

思路2:

约瑟夫环,公式递归

class Solution(object):
    def lastRemaining(self, n, m):
        """
        :type n: int
        :type m: int
        :rtype: int
        """
        if n == 0:
            return 0
        else:
            return (self.lastRemaining(n-1,m)+m)%n

股票的最大利润(*)

假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖 一次 该股票可能获得的利润是多少?

例如一只股票在某些时间节点的价格为 [9,11,8,5,7,12,16,14]。

如果我们能在价格为 5 的时候买入并在价格为 16 时卖出,则能收获最大的利润 11。

数据范围

输入数组长度 [0,500]。

样例

输入:[9, 11, 8, 5, 7, 12, 16, 14]

输出:11

思路:

O(N)遍历即可

class Solution(object):
    def maxDiff(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if len(nums) < 2:
            return 0
        profit,minNum,maxNum = 0,nums[0],nums[0]
        for num in nums:
            if minNum > num:
                profit = maxNum-minNum
                minNum,maxNum = num,0
            elif maxNum < num:
                maxNum = num
        return max(max(profit,maxNum-minNum),0)

求1+2+…+n

求 1+2+…+n1+2+…+n,要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句 (A?B:C)。

数据范围

1≤n≤1000。

样例

输入:10

输出:55

思路:

class Solution(object):
    def getSum(self, n):
        """
        :type n: int
        :rtype: int
        """
        if n == 1:
            return 1
        return n + self.getSum(n-1)

不用加减乘除做加法

写一个函数,求两个整数之和,要求在函数体内不得使用 +、-、×、÷+、-、×、÷ 四则运算符号。

数据范围

−1000≤num1,num2≤1000

样例

输入:num1 = 1 , num2 = 2

输出:3

思路:

位运算加法

AcWing 85. 不用加减乘除做加法 关于~(x^0xFFFFFFFF)详细说明python - AcWing

AcWing 85. 不用加减乘除做加法python - AcWing

class Solution(object):
    def add(self, num1, num2):
        """
        :type num1: int
        :type num2: int
        :rtype: int
        """
        res = 0
        while num2:
            res = num1 ^ num2
            carry = (num1 & num2)<<1
            num1 = res
            num2 = carry
        return res

构建乘积数组

给定一个数组A[0, 1, …, n-1],请构建一个数组B[0, 1, …, n-1],其中B中的元素B[i]=A[0]×A[1]×… ×A[i-1]×A[i+1]×…×A[n-1]

不能使用除法。

数据范围

输入数组长度 [0,20]。

样例

输入:[1, 2, 3, 4, 5]

输出:[120, 60, 40, 30, 24]

思考题

  • 能不能只使用常数空间?(除了输出的数组之外)

思路:

class Solution(object):
    def multiply(self, A):
        """
        :type A: List[int]
        :rtype: List[int]
        """
        if not A:
            return []
        res,ret = [1],nums[0]
        for num in A[1:]:
            res.append(ret)
            ret *= num
        ret = 1
        for i in range(len(A)-1,-1,-1):
            res[i] *= ret
            ret *= A[i]
        return res

把字符串转换成整数(*)

请你写一个函数 StrToInt,实现把字符串转换成整数这个功能。

当然,不能使用 atoi 或者其他类似的库函数。

数据范围

输入字符串长度 [0,20][0,20]。

样例

输入:"123"

输出:123

注意:

你的函数应满足下列条件:

  1. 忽略所有行首空格,找到第一个非空格字符,可以是 ‘+/−’ 表示是正数或者负数,紧随其后找到最长的一串连续数字,将其解析成一个整数;
  2. 整数后可能有任意非数字字符,请将其忽略;
  3. 如果整数长度为 00,则返回 00;
  4. 如果整数大于 INT_MAX(231−1231−1),请返回 INT_MAX;如果整数小于INT_MIN(−231−231) ,请返回 INT_MIN;

思路:

class Solution(object):
    def strToInt(self, string):
        num,base = 0,1
        string = string.strip()
        if not string:
            return 0
        neg,start = False,0
        if string[0] == '-' or string[0] == '+':
            start = 1
            if string[0] == '-':
                neg = True
        end = start+1
        while end < len(string) and string[end].isdigit():
            end += 1
        end = min(end,len(string))
        for i in range(end-1,start-1,-1):
            if not string[i].isdigit():
                return 0
            num += int(string[i])*base
            base *= 10
        if neg:
            num *= -1
        if num > pow(2,31)-1:
            return pow(2,31)-1
        if num <= -pow(2,31):
            return -pow(2,31)
        return num
        

树中两个结点的最低公共祖先

给出一个二叉树,输入两个树节点,求它们的最低公共祖先。

一个树节点的祖先节点包括它本身。

注意:

  • 输入的二叉树不为空;
  • 输入的两个节点一定不为空,且是二叉树中的节点;

数据范围

树中节点数量 [0,500]。

样例

二叉树[8, 12, 2, null, null, 6, 4, null, null, null, null]如下图所示:
    8
   / \
  12  2
     / \
    6   4

1. 如果输入的树节点为2和12,则输出的最低公共祖先为树节点8。

2. 如果输入的树节点为2和6,则输出的最低公共祖先为树节点2。

思路:

递归

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if not root or root == p or root == q:
            return root
        left = self.lowestCommonAncestor(root.left,p,q)
        right = self.lowestCommonAncestor(root.right,p,q)
        if left and right:
            return root
        if left:
            return left
        else:
            return right
        

思路2:

找根节点到两个节点的路径,它们的交点就是最低公共节点

import copy

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if not root or not p or not q:
            return None
        pPath = self.findPath(root,p,[])
        qPath = self.findPath(root,q,[])
        i = 0
        while i < len(pPath) and i < len(qPath) and pPath[i] == qPath[i]:
            i += 1
        return pPath[i-1]
            
        
    def findPath(self,root,p,res):
        if not root:
            return None
        res.append(root)
        if root == p:
            return  res
        l = self.findPath(root.left,p,copy.copy(res))
        r = self.findPath(root.right,p,copy.copy(res))
        if l:
            return l
        return r

findPath不递归

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

import copy

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if not root or not p or not q:
            return None
        pPath = self.findPath(root,p,[])
        qPath = self.findPath(root,q,[])
        i = 0
        while i < len(pPath) and i < len(qPath) and pPath[i] == qPath[i]:
            i += 1
        return pPath[i-1]
            
        
    def findPath(self,root,p,res):
        stack = []
        q,pre = root,None
        while q or stack:
            while q:
                stack.append(q)
                if q == p:
                    return stack
                q = q.left
            q = stack[-1]
            while not q.right or (pre and q.right == pre):
                pre = q
                stack.pop(-1)
                q = stack[-1]
            q = q.right
        return []

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>