矩阵中的路径
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。
路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。
如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。
注意:
- 输入的路径不为空;
- 所有出现的字符均为大写英文字母;
样例
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。
注意:
0<=m<=500<=n<=500<=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"都不是。
注意:
- 小数可以没有整数部分,例如.123等于0.123;
- 小数点后面可以没有数字,例如233.等于233.0;
- 小数点前面和后面可以有数字,例如233.666;
- 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
- 当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]
二叉搜索树与双向链表(*)
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。
要求不能创建任何新的结点,只能调整树中结点指针的指向。
注意:
- 需要返回双向链表最左侧的节点。
例如,输入下图中左边的二叉搜索树,则输出右边的排序双向链表。

数据范围
树中节点数量 [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
解释:每当数据流读入一个数据,就进行一次判断并输出当前的中位数。
思路:
这题我又忘了系列,参考之前写的


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,那么数组中的数如下所示;

从中可以看出,数组左边蓝色部分都满足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
注意:
你的函数应满足下列条件:
- 忽略所有行首空格,找到第一个非空格字符,可以是 ‘+/−’ 表示是正数或者负数,紧随其后找到最长的一串连续数字,将其解析成一个整数;
- 整数后可能有任意非数字字符,请将其忽略;
- 如果整数长度为 00,则返回 00;
- 如果整数大于 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 []
1201

被折叠的 条评论
为什么被折叠?



