按照tag来写:
目录
26 remove-duplicates-from-sorted-array
一、tag == '二分查找'
29. 两数相除
题目:给定两个整数,被除数 dividend
和除数 divisor
。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend
除以除数 divisor
得到的商。
说明:
- 被除数和除数均为 32 位有符号整数。
- 除数不为 0。
- 假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。
思路:先尽可能以2的指数次扩大divisor到temp,尽量够到dividend,记录扩大的倍数;将dividend减去temp,再一次进行尽可能多地扩大divisor到temp;直到dividend减去temp小于divisor为止。将每次的扩大倍数相加。
容易写错的点:
- 开始时判断符号是否一致,计算时直接用绝对值。否则就超时
- 最后return的要在要求范围之内。 max(min(pos * ans, 0x7fffffff), -2147483648)
class Solution(object):
def divide(self, dividend, divisor):
"""
:type dividend: int
:type divisor: int
:rtype: int
"""
if divisor == 0:
return False
pos = 1
if dividend*divisor <0:
pos = -1
ans = 0
dividend = abs(dividend)
divisor = abs(divisor)
while (dividend >= divisor):
temp, i = divisor, 1
while (dividend > temp<<1):
temp <<= 1
i <<= 1
ans += i
dividend -= temp
c = pos * ans
return max(min(pos * ans, 0x7fffffff), -2147483648)
33. 搜索旋转排序数组
题目:
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7]
可能变为 [4,5,6,7,0,1,2]
)。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1
。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
思路:
相当于分段的有序数列,在普通的二分查找的基础上,先判断下target在左边还在右边;再每次判断一下,mid点是在左边还在右边。
容易写错:
- 这种操作一次就要检查条件的,就在外面加一个while,里面都用if;如果是while套while的话,外面一层while可能就不符合了
- 这个题目,有一种特殊情况就是,单调递增List,相当于没有旋转
- while 外面的一个return -1之前忘了写
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
if not nums:
return -1
if len(nums) == 1:
if nums[0] == target: return 0
else: return -1
left = target >= nums[0]
right = target <= nums[-1]
if not left and not right:
return -1
low = 0
high = len(nums) - 1
while (low <= high):
mid = (low + high)//2
mid_left = nums[mid] >= nums[0]
mid_right = nums[mid] <= nums[-1]
if nums[mid] == target:
return mid
elif nums[mid] > target:
if left and right:
high = mid - 1
elif left and not right:
high = mid - 1
elif right and not left:
if mid_left:
low = mid + 1
elif mid_right:
high = mid - 1
elif nums[mid] < target:
if left and right:
low = mid + 1
elif right and not left:
low = mid + 1
elif left and not right:
if mid_left:
low = mid + 1
elif mid_right:
high = mid - 1
return -1
二、tag == '树'
树的常用代码
这里以199题为例,放上一个python版本二叉树实现的完整代码:
from collections import deque
import json
# 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 rightSideView(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
def dfs(root, h): # 指的是树中的层数
# c = root.val
if root:
if h == len(ans):
ans.append(root.val)
dfs(root.right, h + 1)
dfs(root.left, h + 1)
ans = []
dfs(root, 0)
return ans
def stringToTreeNode(input):
# input = input.strip()
# input = input[1:-1]
if not input:
return None
inputValues = [s.strip() for s in input.split(',')]
root = TreeNode(int(inputValues[0]))
nodeQueue = [root]
front = 0
index = 1
while index < len(inputValues):
node = nodeQueue[front]
front = front + 1
item = inputValues[index]
index = index + 1
if item != "null":
leftNumber = int(item)
node.left = TreeNode(leftNumber)
nodeQueue.append(node.left)
if index >= len(inputValues):
break
item = inputValues[index]
index = index + 1
if item != "null":
rightNumber = int(item)
node.right = TreeNode(rightNumber)
nodeQueue.append(node.right)
return root
def integerListToString(nums, len_of_list=None):
if not len_of_list:
len_of_list = len(nums)
return json.dumps(nums[:len_of_list])
def main():
import sys
# def readlines():
# for line in sys.stdin:
# yield line.strip('\n')
#
# lines = readlines()
while True:
try:
# line = ['1','2','3','null','5','4','null']
line = '1,2,3,null,5,null,4'
root = stringToTreeNode(line)
ret = Solution().rightSideView(root)
print(ret)
# out = integerListToString(ret)
# print(out)
except StopIteration:
break
if __name__ == '__main__':
main()
94. 二叉树中序遍历
容易写错:
-
以为是完全二叉树,左子树节点是跟的2倍,右子树节点是跟的2*i+1。普通二叉树没有这样的性质
- 这个是已经自建好了一个二叉树类型了
- 如果输入节点是Null,插入的时候比较特殊,直接从左子节点跳转到右子节点,不过下面代码没有写这部分
参考别人的,完成的Python实现二叉树的代码:
class Node:
def __init__(self,item):
self.item = item
self.child1 = None
self.child2 = None
class Tree:
def __init__(self):
self.root = None
def add(self, item):
node = Node(item)
if self.root is None:
self.root = node
else:
q = [self.root]
while True:
print(len(q))
pop_node = q.pop(0)0
# print(q)
if pop_node.child1 is None:
pop_node.child1 = node
return
elif pop_node.child2 is None:
pop_node.child2 = node
return
else:
q.append(pop_node.child1)
q.append(pop_node.child2)
def traverse(self): # 层次遍历
if self.root is None:
return None
q = [self.root]
res = [self.root.item]
while q != []:
pop_node = q.pop(0)
if pop_node.child1 is not None:
q.append(pop_node.child1)
res.append(pop_node.child1.item)
if pop_node.child2 is not None:
q.append(pop_node.child2)
res.append(pop_node.child2.item)
return res
def preorder(self,root): # 先序遍历
if root is None:
return []
result = [root.item]
left_item = self.preorder(root.child1)
right_item = self.preorder(root.child2)
return result + left_item + right_item
def inorder(self,root): # 中序序遍历
if root is None:
return []
result = [root.item]
left_item = self.inorder(root.child1)
right_item = self.inorder(root.child2)
return left_item + result + right_item
def postorder(self,root): # 后序遍历
if root is None:
return []
result = [root.item]
left_item = self.postorder(root.child1)
right_item = self.postorder(root.child2)
return left_item + right_item + result
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
res, stack = [], [(1, root)]
while stack:
p = stack.pop()
if not p[1]: continue
stack.extend([(1, p[1].right), (0, p[1]), (1, p[1].left)]) if p[0] != 0 else res.append(p[1].val)
return res
t = Tree()
for i in [1,2,3,4,5,6,7]:
t.add(i)
print('层序遍历:',t.traverse())
print('先序遍历:',t.preorder(t.root))
# print('中序遍历:',t.inorderTraversal(t.root))
print('后序遍历:',t.postorder(t.root))
三、tag == ‘深度优先搜索’
529. 扫雷游戏
说明:给定一个代表游戏板的二维字符矩阵。 'M' 代表一个未挖出的地雷,'E' 代表一个未挖出的空方块,'B' 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字('1' 到 '8')表示有多少地雷与这块已挖出的方块相邻,'X' 则表示一个已挖出的地雷。
现在给出在所有未挖出的方块中('M'或者'E')的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:
- 如果一个地雷('M')被挖出,游戏就结束了- 把它改为 'X'。
- 如果一个没有相邻地雷的空方块('E')被挖出,修改它为('B'),并且所有和其相邻的方块都应该被递归地揭露。
- 如果一个至少与一个地雷相邻的空方块('E')被挖出,修改它为数字('1'到'8'),表示相邻地雷的数量。
- 如果在此次点击中,若无更多方块可被揭露,则返回面板。
思路:广度优先搜索+深度优先搜索,先广度优先搜索统计周围是否有雷,再决定是否要进行深度优先搜索,进行递归
容易写错:
- height和width搞混
class Solution(object):
def updateBoard(self, board, click):
"""
:type board: List[List[str]]
:type click: List[int]
:rtype: List[List[str]]
"""
width = len(board[0]) - 1
hight = len(board) - 1
[row, col], direction = click, ((-1,0), (1,0), (0,-1), (0,1), (-1,-1), (-1,1), (1,-1), (1,1))
if board[row][col] == 'M':
board[row][col] = 'X'
elif board[row][col] == 'E':
num = sum([ board[row+r][col+c] == 'M' for r, c in direction if 0 <= row+r <= hight and 0 <= col+c <= width ])
board[row][col] = str(num if num else 'B')
if not num:
for r, c in direction:
if 0 <= row+r <= hight and 0 <= col+c <= width:
self.updateBoard(self, board, [row+r, col+c])
return board
841. 钥匙和房间
题目:
有 N
个房间,开始时你位于 0
号房间。每个房间有不同的号码:0,1,2,...,N-1
,并且房间里可能有一些钥匙能使你进入下一个房间。
在形式上,对于每个房间 i
都有一个钥匙列表 rooms[i]
,每个钥匙 rooms[i][j]
由 [0,1,...,N-1]
中的一个整数表示,其中 N = rooms.length
。 钥匙 rooms[i][j] = v
可以打开编号为 v
的房间。
最初,除 0
号房间外的其余所有房间都被锁住。
你可以自由地在房间之间来回走动。
如果能进入每个房间返回 true
,否则返回 false
思路:获取钥匙后用dfs,比较简单
class Solution(object):
def canVisitAllRooms(self, rooms):
"""
:type rooms: List[List[int]]
:rtype: bool
"""
visited = {}
def dfs(key):
visited[key] = True
for nkey in rooms[key]:
if nkey not in visited.keys():
visited[nkey] = True
dfs(nkey)
dfs(0)
for i in range(len(rooms)):
if i not in visited.keys():
return False
return True
547.朋友圈
思路:用dfs,每次遇到还没visited的人,朋友圈数就加一
class Solution(object):
def findCircleNum(self, M):
"""
:type M: List[List[int]]
:rtype: int
"""
numc = 0
visited = {}
nump = len(M)
def dfs(i):
visited[i] = True
# frds = [frd in range(nump) if M[i][frd]==1]
for frd in range(nump):
if M[i][frd] == 1:
if frd not in visited.keys():
visited[frd] = True
dfs(frd)
for j in range(nump):
if j not in visited.keys():
numc += 1
dfs(j)
return numc
四、tag == '广度优先搜索'
199. 二叉树的右视图
思路:主要是深度优先吧。设置一个深度标记h,还有右视图看到的记录ans。只有当显示的深度比ans目前的个数多的时候,才给ans append
class Solution(object):
def rightSideView(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
def dfs(root, h):
if root:
if h == len(ans):
ans.append(root.val)
dfs(root.right, h + 1)
dfs(root.left, h + 1)
ans = []
dfs(root, 0)
return ans
五、一些以前写的
38 count-and-say
用到递归,在比较最后一个字符的时候有点麻烦
class Solution(object):
def countAndSay(self, n):
"""
:type n: int
:rtype: str
"""
if n <=0:
return False
if n == 1:
return '1'
if n == 2:
return '11'
if n >= 3:
finalAnswr = ''
lastAnswr = self.countAndSay(self, n-1) # 这里括号中的这个self有异议,在我自己电脑上IDE需要加,提交的时候就不需要
base_str = lastAnswr[0]
base_count = 1
for i in range(len(lastAnswr)-1):
cmp_str = lastAnswr[i+1]
if cmp_str == base_str:
base_count += 1
if i == len(lastAnswr)-2:
finalAnswr = finalAnswr + str(base_count) + base_str
else:
finalAnswr = finalAnswr + str(base_count) + base_str
base_str = cmp_str
base_count = 1
if i == len(lastAnswr)-2:
finalAnswr = finalAnswr + cmp_str + '1'
return finalAnswr
26 remove-duplicates-from-sorted-array
关键是不能占用更多内存。
判断和前面最开始的是不是一样,如果不一样就在base_compare的下一个位置写这个新数据
class Solution(object):
def removeDuplicates(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) <= 1:
return len(nums)
slow = 0
for i in range(1, len(nums)):
if nums[i] != nums[slow]:
slow += 1
nums[slow] = nums[i]
return nums
11 盛最多水的容器
正常写循环很简单,但是超时间了。
设置左右挡板,谁小谁往前走。用while
def maxArea(self, height):
"""
:type height: List[int]
:rtype: int
"""
leftp = 0
rightp = len(height)-1
max_area = 0
while leftp < rightp:
area = (rightp - leftp) * min(height[rightp], height[leftp])
if area > max_area:
max_area = area
if height[leftp] < height[rightp]:
leftp += 1
else:
rightp -= 1
return max_area
六、tag == '动态规划'
1. 网易笔试
题目的大概意思:一种双核CPU的两个核能够同时的处理任务,现在有n个已知数据量的任务需要交给CPU处理,假设已知CPU的每个核1秒可以处理1kb,每个核同时只能处理一项任务。n个任务可以按照任意顺序放入CPU进行处理,现在需要设计一个方案让CPU处理完这批任务所需的时间最少,求这个最小的时间。
输入包括两行:
第一行为整数n(1 ≤ n ≤ 50)
第二行为n个整数length[i](1024 ≤ length[i] ≤ 4194304),表示每个任务的长度为length[i]kb,每个数均为1024的倍数。
输出一个整数,表示最少需要处理的时间。
import sys
import math
res0 = []
s = sys.stdin.readline().strip("\n")
while s != "":
s = s.split()
s = [int(each) for each in s]
res0.append(s)
s = sys.stdin.readline().strip("\n")
n = res0[0][0]
lth = [c//1024 for c in res0[1]]
sum_volume = sum(lth)//2
res = [[0 for j in range(sum_volume+1)] for i in range(n+1)]
for i in range(1, n+1):
for j in range(1, sum_volume+1):
res[i][j] = res[i-1][j]
if j >= lth[i-1] and res[i-1][j-lth[i-1]]+lth[i-1] > res[i-1][j]:
res[i][j] = res[i-1][j-lth[i-1]]+lth[i-1]
choose = [False for i in range(n)]
c=sum_volume
for i in range(n, 0, -1):
if res[i][c] > res[i-1][c]:
choose[i-1] = True
c = sum_volume - lth[i-1]
ans1 = [res0[1][i] for i in range(n) if choose[i] == True]
ans2 = [res0[1][i] for i in range(n) if choose[i] == False]
print(max(sum(ans1), sum(ans2)))
64. 最小路径和
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
思路:只能向右或者向下走,最短路径为到左边格子的最短路径或者到上边格子的最短路径再加上本格子的数值。所以对grid求出从左上角到每个格子的最短路径。
class Solution(object):
def minPathSum(self, grid):
r = len(grid)
c = len(grid[0])
pathsum = grid
for j in range(1, r):
pathsum[j][0] += pathsum[j-1][0]
for i in range(1, c):
pathsum[0][i] += pathsum[0][i-1]
for i in range(1, r):
for j in range(1, c):
pathsum[i][j] += min(pathsum[i-1][j], pathsum[i][j-1])
return pathsum[-1][-1]
62. 不同路径
比较简单,排列组合,求C
class Solution(object):
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
if m == 1 or n == 1:
return 1
r = n - 1
c = m - 1
all = [ii for ii in range(1, r + c + 1)]
ans1 = all[0]
ans2 = all[-1]
for i in range(1, c):
ans1 *= all[i]
ans2 *= all[-i-1]
return ans2//ans1
877. 石子游戏
思路:这个题刚开始想错了,以为每次拿就只要拿两端石头堆里面个数多的就好了,不是这么简单,和下一步的走法也有关系。因为和下一步(也就是石头堆减少)的走法有关,所以想到用动态规划。dp记录的是第一个走的人比第二个走的人,多获得的石子数。很多别的解法里用的是二维数组,这里用的是一维数组,直接在一个数组上进行修改。首先考虑只有两个石头堆的情况(d==1),此时赢得的石头数是 max(piles[i] - dp[i + 1], piles[i + d] - dp[i]) ;当d增大时,更新dp[i],dp[i]的意思是当只有i到i+d这些石头堆时,第一个人能得到的更多的石头个数;那当d最大,也就是n-1时,此时的dp[i]就是考虑全部石头堆时,第一个人获得的更多的石头数。大于0就返回true.
动态规划的代码有时候真的非常简短
class Solution:
def stoneGame(self, piles):
"""
:type piles: List[int]
:rtype: bool
"""
n = len(piles)
dp = piles[:]
for d in range(1, n):
for i in range(n - d):
dp[i] = max(piles[i] - dp[i + 1], piles[i + d] - dp[i])
return dp[0] > 0
413. 等差数列划分
思路:刚开始我想得比较麻烦,打算序列逐个增加,最后一个增加的数和前面的数组成等差数列的个数。应该也是一个思路,不过自己还没写出来,后来参考了网上的:等差数列就先考察相邻两个数的差。从头遍历这些差;如果有连续的n个差相等,那么这(n+1)个数可以组成n*(n-1)/2个长度至少为3的等差序列;如果差变了,就重新统计。
容易写错:
- 想不到先求差
- 在遇到新的差时,要更新pre,更新ndiff,后者之前忘记更新了
class Solution(object):
def numberOfArithmeticSlices(self, A):
"""
:type nums: List[int]
:rtype: int
"""
if len(A) < 3:
return 0
diff = [A[i] - A[i-1] for i in range(1, len(A))]
ans = 0
pre = diff[0]
ndiff = 1
for i in range(1, len(diff)):
if diff[i] == pre:
ndiff += 1
if i == len(diff) - 1:
ans += ndiff * (ndiff - 1) / 2
else:
pre = diff[i]
ans += ndiff * (ndiff - 1) / 2
ndiff = 1
return ans
221. 最大正方形
题目:找到二维矩阵中标记为1的正方形的面积。
思路:正方形主要考虑右下角的那个点。dp矩阵中,除了第一行第一列以外,其他点如果为1,那么它的dp值为上、左、斜左上三个位置的最小值加1,更新maxn(边长)。
class Solution(object):
def maximalSquare(self, matrix):
if len(matrix) == 0:
return 0
dp = [[0 for i in range(len(matrix[0]))] for j in range(len(matrix))]
# dp[0] = matrix[0]
maxn = 0
for i in range(len(matrix)):
for j in range(len(matrix[0])):
if matrix[i][j] == '1':
if i == 0:
dp[i][j] = 1
elif j == 0:
dp[i][j] = 1
else:
dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1], dp[i][j - 1]) + 1
maxn = [maxn, dp[i][j]][dp[i][j] > maxn]
return maxn * maxn
120. 三角形最小路径和
思路: 只使用 O(n) 的额外空间(n 为三角形的总行数),新建一个dp行,来从下往上相加。
class Solution(object):
def minimumTotal(self, triangle):
"""
:type triangle: List[List[int]]
:rtype: int
"""
dp = triangle[-1]
for i in range(len(triangle) - 2, -1, -1):
for j in range(len(triangle[i])):
dp[j] = min(dp[j], dp[j+1]) + triangle[i][j]
return dp[0]
718. 最长重复子数组
最长重复数组是dp中的常见问题,如果A[i-1]和B[j-1]相等,那么最长重复子数组的长度就加一;否则就是0
class Solution(object):
def findLength(self, A, B):
"""
:type A: List[int]
:type B: List[int]
:rtype: int
"""
dp = [[0 for j in range(len(B)+1)] for i in range(len(A)+1)]
for i in range(1, len(A)+1):
for j in range(1, len(B)+1):
# print(i, j)
if A[i-1] == B[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
return max(max(row) for row in dp)