本博客同时发布于个人主页:www.doctorsrn.cn
最近使用Python把《剑指offer》刷了一遍,自己能第一时间有想法的题目就直接写,没有思路的题目就看懂书上的思路和参考其他开源的实现后再自己写一遍。主要以牛客网《剑指offer》作为在线评测网站,有些题目牛客网没有的再找其他网站进行在线评测,主要使用的其他网站有:
刷题过程主要参考的开源实现有:
本博客对应的代码仓库在此。
今年企业缩招,找工作的行情不容乐观,秋招路漫漫啊。
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
## 方法一:排序,然后查找
## 时间复杂度:O(nlog n) 空间复杂度:*
# -*- coding:utf-8 -*-
class Solution:
# 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
# 函数返回True/False
def duplicate(self, numbers, duplication):
# write code here
if not numbers or len(numbers) <= 1:
return False
for i in numbers:
if i<0 or i > len(numbers)-1:
return False
numbers_sorted = self.quickSort(numbers)
for i,j in zip(numbers_sorted[:-1],numbers_sorted[1:]):
if i == j:
duplication[0] = i
return True
return False
def quickSort(self, arr):
if len(arr) <= 1:
return arr
length = len(arr)
pivot = arr[length // 2]
left = [x for x in arr if x > pivot]
right = [x for x in arr if x < pivot]
mid = [x for x in arr if x == pivot]
# 升序排列可以通过,降序排列无法通过全部测试样例
return self.quickSort(right) + mid + self.quickSort(left)
#return self.quickSort(left) + mid + self.quickSort(right)
## 方法二:引入hash表(字典)
## 时间复杂度:O(n) 空间复杂度:O(n)
# -*- coding:utf-8 -*-
class Solution:
# 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
# 函数返回True/False
def duplicate(self, numbers, duplication):
# write code here
if not numbers or len(numbers) <= 1:
return False
for i in numbers:
if i < 0 or i > len(numbers) - 1:
return False
dic = {}
for i in numbers:
if dic.has_key(i):
duplication[0] = i
return True
else:
dic[i]=1
return False
## 方法三:利用数字自身下标和值的key-value特性
## 时间复杂度:O(n) 空间复杂度:O(1)
# -*- coding:utf-8 -*-
class Solution:
# 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
# 函数返回True/False
def duplicate(self, numbers, duplication):
# write code here
if not numbers or len(numbers) <= 1:
return False
for i in numbers:
if i < 0 or i > len(numbers) - 1:
return False
i = 0
while i < len(numbers):
if i == numbers[i]:
i += 1
elif numbers[i] == numbers[numbers[i]]:
duplication[0] = numbers[i]
return True
else:
tmp = numbers[i]
numbers[i] = numbers[tmp]
numbers[tmp] = tmp
return False
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数
# 方法一:暴力遍历法
# 时间复杂度:O(n^2)
# -*- coding:utf-8 -*-
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
if len(array) < 1:
return False
for i in array:
for j in i:
if target == j:
return True
return False
# 方法二:反向剔除不满足元素法:从右上角进行查找
# -*- coding:utf-8 -*-
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
if len(array) < 1 or len(array[0]) < 1:
return False
r_l = len(array)
c_l = len(array[0])
# 从右上角开始搜索
i, j = 0, c_l-1
while target != array[i][j]:
if target > array[i][j]:
if i < r_l - 1:
i += 1
else:
return False
else:
if j > 0:
j -= 1
else:
return False
return True
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
# 方法一:遍历判断然后替换法,时间复杂度O(n)
# -*- coding:utf-8 -*-
class Solution:
# s 源字符串
def replaceSpace(self, s):
# write code here
if not isinstance(s, str) or len(s) < 1 or s == None:
return s
tmp = []
for i in s:
if i != ' ':
tmp.append(i)
else:
tmp.append("%20")
res = ''.join(tmp)
return res
# 方法二:从后向前遍历替换法,时间复杂度O(n)
# -*- coding:utf-8 -*-
class Solution:
# s 源字符串
def replaceSpace(self, s):
# write code here
if not isinstance(s, str) or len(s) < 1 or s == None:
return ''
spaceN = 0
for i in s:
if i == ' ':
spaceN += 1
total = len(s) + 2 * spaceN
newStr = [None] * total
indexO, indexN = len(s)-1, total-1
while indexO >= 0:
if s[indexO] == ' ':
newStr[indexN]='0'
newStr[indexN-1]='2'
newStr[indexN-2]='%'
indexN -= 3
indexO -= 1
else:
newStr[indexN] = s[indexO]
indexN -= 1
indexO -= 1
return ''.join(newStr)
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
# 方法一: 使用栈
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回从尾部到头部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
# write code here
if listNode == None:
return []
if listNode.next == None:
return listNode.val
stack = []
nextNode = listNode
while nextNode.next != None:
stack.append(nextNode.val)
nextNode = nextNode.next
stack.append(nextNode.val)
return stack[::-1]
# 方法二:递归的方法
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回从尾部到头部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
# write code here
if not listNode:
return []
if listNode.next == None:
res = []
res.append(listNode.val)
return res
re = self.printListFromTailToHead(listNode.next)
re.append(listNode.val)
return re
L=[]
l=ListNode(1)
t = l
for i in range(4):
t.next = ListNode(i)
t = t.next
# while l.next != None:
# print(l.val)
# l = l.next
# print(l.val)
s = Solution()
s.printListFromTailToHead(l)
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
# 递归法
# 按照前序遍历和中序遍历的特点进行递归重建
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回构造的TreeNode根节点
def reConstructBinaryTree(self, pre, tin):
# write code here
# 有效性判断
if set(pre) != set(tin):
return None
# 递归终止条件
if len(pre) < 1 or len(tin) < 1:
return None
if len(pre) == 1 or len(tin) == 1:
return TreeNode(pre[0])
root = pre[0]
root_index = tin.index(root)
L_tin, R_tin = tin[:root_index], tin[root_index+1:]
L_pre, R_pre = pre[1:1+len(L_tin)], pre[1+len(L_tin):]
# 构建左树
L_child = self.reConstructBinaryTree(L_pre, L_tin)
# 构建右树
R_child = self.reConstructBinaryTree(R_pre, R_tin)
node = TreeNode(root)
node.left, node.right = L_child, R_child
return node
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
# -*- coding:utf-8 -*-
class TreeLinkNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
self.next = None
# -*- coding:utf-8 -*-
# class TreeLinkNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
# self.next = None
class Solution:
def GetNext(self, pNode):
# write code here
if not pNode:
return pNode
# 如果存在右子节点,则下一个节点是右子节点中的最左节点
if pNode.right:
pNode = pNode.right
while pNode.left:
pNode = pNode.left
return pNode
# 如果不存在右子节点,则反向根据父节点和子节点的关系确定
else:
pC = pNode # 记录当前节点
# 当父节点不为根节点时,如果当前节点是父左子节点,返回当前节点
while pNode.next:
pN = pNode.next
if pN.left and pN.left == pNode:
return pN
else:
pC = pNode
pNode = pN
# 当父节点是根节点时,如果当前节点是根节点的左子节点,则返回根节点
if pNode.left and pNode.left == pC:
return pNode
else:
return None
a = TreeLinkNode('a')
b = TreeLinkNode('b')
c = TreeLinkNode('c')
d = TreeLinkNode('d')
e = TreeLinkNode('e')
f = TreeLinkNode('f')
g = TreeLinkNode('g')
h = TreeLinkNode('h')
i = TreeLinkNode('i')
a.left, a.right = b, c
b.left, b.right, b.next = d, e, a
c.left, c.right, c.next = f, g, a
d.next = b
e.left, e.right, e.next = h, i, b
h.next = e
i.next = e
f.next, g.next = c, c
s = Solution()
print(s.GetNext(i).val)
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.stack1 = [] # 用于push
self.stack2 = [] # 用于pop
def push(self, node):
# write code here
self.stack1.append(node)
def pop(self):
if len(self.stack1) == 0 and len(self.stack2) == 0:
return None
# stack2不为空时,弹出栈顶元素即为队列头部元素
elif len(self.stack2) > 0:
res = self.stack2.pop()
return res
else:
# stack2为空时,将stack1元素弹出后压入stack2,则stack2栈顶元素即为队列头部元素
while len(self.stack1) > 0:
i = self.stack1.pop()
self.stack2.append(i)
res = self.stack2.pop()
return res
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39
f ( n ) = { 0 n = 0 1 n = 1 f ( n − 1 ) + f ( n − 2 ) n > 1 f(n) = \begin{cases} 0 & n = 0 \\ 1 & n = 1 \\ f(n-1)+f(n-2) & n > 1 \end{cases} f(n)=⎩⎪⎨⎪⎧01f(n−1)+f(n−2)n=0n=1n>1
# 方法一:循环法实现
# 时间复杂度为:O(n)
# 自下而上的求解
# -*- coding:utf-8 -*-
class Solution:
def Fibonacci(self, n):
# write code here
if not isinstance(n,int) or n < 0 or n > 39:
return None
if n == 0:
return 0
if n == 1:
return 1
fn1, fn2 = 1, 0
for i in range(2,n+1):
fn = fn1 + fn2
fn2 = fn1
fn1 = fn
return fn
# 方法二:递归法实现----运行超时
# 时间复杂度为n的指数级
# 自上而下的求解
# -*- coding:utf-8 -*-
class Solution:
def Fibonacci(self, n):
# write code here
if not isinstance(n, int) or n < 0 or n > 39:
return 0
if n == 0:
return 0
if n == 1:
return 1
return self.Fibonacci(n-1) + self.Fibonacci(n-2)
可证明存在如下公式:
[ f ( n ) f ( n − 1 ) f ( n − 1 ) f ( n − 2 ) ] = [ 1 1 1 0 ] n − 1 \begin{bmatrix} f(n)& f(n-1)\\ f(n-1)& f(n-2) \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^{n-1} [f(n)f(n−1)f(n−1)f(n−2)]=[1110]n−1
利用上述公式可以根据 [ 1 1 1 0 ] n − 1 \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^{n-1} [1110]n−1求得 f ( n ) f(n) f(n)
# 方法一:更简洁的循环实现
# -*- coding:utf-8 -*-
class Solution:
def Fibonacci(self, n):
# write code here
tempArray = [0,1]
if n >= 2:
for i in range(2, n+1):
tempArray[i%2] = tempArray[0] + tempArray[1]
return tempArray[n%2]
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
**分析:**设跳法是台阶数n的函数f(n), n>2时第一次跳有两种跳法,跳一个台阶则共f(n-1)种,跳两个台阶则共f(n-2)种,所以总共f(n)=f(n-1)+f(n-2)
# -*- coding:utf-8 -*-
class Solution:
def jumpFloor(self, number):
# write code here
if number < 1:
return 0
if number == 1:
return 1
if number == 2:
return 2
fn1 = 2
fn2 = 1
for i in range(3, number+1):
fn = fn1 + fn2
fn2 = fn1
fn1 = fn
return fn
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
**解析:**类比普通跳台阶问题,设跳法是台阶数n的函数f(n), 则有:
f ( n ) = f ( n − 1 ) + f ( n − 2 ) + . . . + f ( 1 ) + 1 f(n)=f(n-1)+f(n-2)+...+f(1)+1 f(n)=f(n−1)+f(n−2)+...+f(1)+1
递推可得: f ( n − 1 ) = f ( n − 2 ) + f ( n − 3 ) + . . . + f ( 1 ) + 1 f(n-1)=f(n-2)+f(n-3)+...+f(1)+1 f(n−1)=f(n−2)+f(n−3)+...+f(1)+1
两式相减可得: f ( n ) − f ( n − 1 ) = f ( n − 1 ) f(n)-f(n-1)=f(n-1) f(n)−f(n−1)=f(n−1)
即 f ( n ) = 2 f ( n − 1 ) f(n)=2f(n-1) f(n)=2f(n−1),即 f ( n ) f(n) f(n)是公比为2的等比级数: f ( n ) = 2 n − 1 f(n)=2^{n-1} f(n)=2n−1
# -*- coding:utf-8 -*-
class Solution:
def jumpFloorII(self, number):
# write code here
if number < 1:
return 0
fn = 1
if number == 1:
return fn
for _ in range(2,number+1):
fn *= 2
return fn
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
# -*- coding:utf-8 -*-
class Solution:
def rectCover(self, number):
# write code here
if number < 1 or not isinstance(number, int):
return 0
if number == 1:
return 1
if number == 2:
return 2
fn1 = 2
fn2 = 1
for i in range(3, number+1):
fn = fn1 + fn2
fn2 = fn1
fn1 = fn
return fn
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
# 方法一:暴力遍历法--不推荐
# 时间复杂度为:O(n)
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if len(rotateArray) < 1:
return 0
small = rotateArray[0]
for i in rotateArray:
if small > i:
small = i
return small
return small
# 方法二:二分法
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if len(rotateArray) < 1:
return 0
if len(rotateArray) == 1:
return rotateArray[0]
index = len(rotateArray) // 2 # index >= 1
base = len(rotateArray) // 2
while 0<= base < len(rotateArray):
if rotateArray[base-1] > rotateArray[base]: # 如果前一个大于当前数,则当前数为最小
return rotateArray[base]
elif base + 1 < len(rotateArray) and rotateArray[base] > rotateArray[base+1]: # 如果后一个数存在且小于当前数,则后一个数为最小
return rotateArray[base+1]
else:
# 否则重新搜索,以数组第一个数为基准移动索引
if rotateArray[base] > rotateArray[0]:
base += index // 2
else:
base -= index // 2
index //= 2
return rotateArray[base]
## 这个方法存在问题,当针对[1,1,1,1,1,0,1]时无法正常判断,所以应该修改为当base与左右值相等时采用顺序查找的方法搜索最小值
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if len(rotateArray) < 1:
return 0
if len(rotateArray) == 1:
return rotateArray[0]
index = len(rotateArray) // 2 # index >= 1
base = len(rotateArray) // 2
while 0<= base < len(rotateArray):
if rotateArray[base-1] > rotateArray[base]: # 如果前一个大于当前数,则当前数为最小
return rotateArray[base]
elif base + 1 < len(rotateArray) and rotateArray[base] > rotateArray[base+1]: # 如果后一个数存在且小于当前数,则后一个数为最小
return rotateArray[base+1]
else:
# 否则重新搜索,以数组第一个数为基准移动索引
if rotateArray[base] > rotateArray[0]:
base += index // 2
elif rotateArray[base] < rotateArray[0]:
base -= index // 2
else: # 顺序查找
minNum = self.minSearch(rotateArray)
return minNum
index //= 2
return rotateArray[base]
def minSearch(self, rotateArray):
small = rotateArray[0]
for i in rotateArray:
if small > i:
small = i
return small
return small
# 方法三:二分法示例代码
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if len(rotateArray) < 1:
return 0
if len(rotateArray) == 1:
return rotateArray[0]
# 双指针
first = 0
end = len(rotateArray)-1
while end - first != 1:
mid = (first + end) // 2
if rotateArray[first] == rotateArray[mid] == rotateArray[end]: # 特殊情况,使用顺序查找
minN = rotateArray[0]
for i in rotateArray[1:]:
if i < minN:
minN = i
return minN
elif rotateArray[first] < rotateArray[end]: # 特殊情况,整个数组是升序,没有进行旋转
return rotateArray[first]
elif rotateArray[mid] >= rotateArray[first]:
first = mid
else:
end = mid
return rotateArray[end]
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 [ a b c e s f c s a d e e ] \begin{bmatrix} a & b & c & e \\ s & f & c & s \\ a & d & e & e \end{bmatrix} ⎣⎡asabfdcceese⎦⎤ 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
# 回溯法--使用递归实现
# -*- coding:utf-8 -*-
class Solution:
def hasPath(self, matrix, rows, cols, path):
# write code here
if len(matrix) < 1 or rows < 1 or cols < 1 or len(path) < 1:
return False
visited = [0] * (rows * cols)
pathL = 0
for row in range(rows):
for col in range(cols):
if self.if_has_path(matrix, rows, cols, path, row, col, visited, pathL):
return True
return False
def if_has_path(self, matrix, rows, cols, path, row, col, visited, pathL):
# 递归终止条件
if pathL == len(path):
return True
hasPath = False
if 0 <= row < rows and 0 <= col < cols and \
matrix[row * cols + col] == path[pathL] and \
not visited[row * cols + col]:
pathL += 1
visited[row * cols + col] = 1
hasPath = self.if_has_path(matrix, rows, cols, path, row-1, col, visited, pathL) or \
self.if_has_path(matrix, rows, cols, path, row+1, col, visited, pathL) or \
self.if_has_path(matrix, rows, cols, path, row, col-1, visited, pathL) or \
self.if_has_path(matrix, rows, cols, path, row, col+1, visited, pathL)
if not hasPath:
pathL -= 1
visited[row * cols + col] = 0
return hasPath
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
# 回溯法
# -*- coding:utf-8 -*-
class Solution:
def movingCount(self, threshold, rows, cols):
# write code here
if threshold < 1 or rows < 1 or cols < 1:
return 0
visited = [0] * (rows * cols) # 记录当前位置是否访问过
available = [0] * (rows * cols) # 记录当前位置是否可达
self.ifAvailable(threshold, rows, cols, 0, 0, visited, available)
return sum(available)
def ifTrue(self, row, col, threshold):
res = 0
while row != 0:
res += row % 10
row //= 10
while col != 0:
res += col % 10
col //= 10
if res <= threshold:
return True
else:
return False
def ifAvailable(self, threshold, rows, cols, row, col, visited, available):
if 0 <= row < rows and 0 <= col < cols and self.ifTrue(row, col, threshold) and \
not visited[row * cols + col]:
available[row * cols + col] = 1
visited[row * cols + col] = 1
self.ifAvailable(threshold, rows, cols, row-1, col, visited, available)
self.ifAvailable(threshold, rows, cols, row+1, col, visited, available)
self.ifAvailable(threshold, rows, cols, row, col-1, visited, available)
self.ifAvailable(threshold, rows, cols, row, col+1, visited, available)
else:
# visited[row * cols + col] = 0
return
# 回溯法--示例代码
# -*- coding:utf-8 -*-
class Solution:
def movingCount(self, threshold, rows, cols):
visited = [False] * (rows * cols)
count = self.movingCountCore(threshold, rows, cols, 0, 0, visited)
return count
def movingCountCore(self, threshold, rows, cols, row, col, visited):
count = 0
if self.check(threshold, rows, cols, row, col, visited):
visited[row * cols + col] = True
count = 1 + self.movingCountCore(threshold, rows, cols, row-1, col, visited) + \
self.movingCountCore(threshold, rows, cols, row+1, col, visited) + \
self.movingCountCore(threshold, rows, cols, row, col-1, visited) + \
self.movingCountCore(threshold, rows, cols, row, col+1, visited)
return count
def check(self, threshold, rows, cols, row, col, visited):
if row >= 0 and row < rows and col >= 0 and col < cols and self.getDigitSum(row) + self.getDigitSum(col) <= threshold and not visited[row * cols + col]:
return True
return False
def getDigitSum(self, number):
sum = 0
while number > 0:
sum += (number % 10)
number = number // 10
return sum
与leetcode 343题一样,中文版:
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
注意:存储最优解的数组的前三个值是剪开后或者不剪情况下绳子的最大值,而不仅仅是剪开后乘积的最大值
参考题解:https://blog.csdn.net/weixin_41796401/article/details/84899457
# 方法一:动态规划法
# 时间复杂度为O(n^2),空间复杂度为O(n)
class Solution(object):
def integerBreak(self, n):
"""
:type n: int
:rtype: int
"""
if n < 2:
return 0
if n == 2:
return 1
if n == 3:
return 2
# mem[0] not used
# !!! mem中存储的是剪开后或者不剪情况下绳子的最大值,而不仅仅是剪开后乘积的最大值
mem = [0] * (n+1)
mem[1], mem[2], mem[3] = 1, 2, 3
for i in range(4, n+1):
max_product = 0
for j in range(1, i//2+1):
max_product = max(max_product, mem[j] * mem[i-j])
mem[i] = max_product
return mem[n]
# 方法二:贪婪法
# 证明:n > 4时,3(n-3) >= 2(n-2) > n
# 尽可能多的将数分解成3,当余下是4时,分解成2*2
class Solution(object):
def integerBreak(self, n):
"""
:type n: int
:rtype: int
"""
if n < 2:
return 0
if n == 2:
return 1
if n == 3:
return 2
p = (n // 3)
r = (n % 3)
if r == 0:
return pow(3, p)
elif r == 1:
return pow(3, p-1)*4
elif r == 2:
return pow(3, p) *2
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
解析:
# 方法一:常规不会陷入死循环的方法
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1(self, n):
# write code here
if n == 0:
return 0
# 设置循环终止条件
MAX_INT = (1 << (32-1))
flag = 1
count = 0
while n and flag <= MAX_INT :
if n & flag:
count += 1
n -= flag
flag = flag << 1
return count
# 方法二:技巧性方法:正数减去1,再和原来的数做与运算,会把正数最右边的1变成0
# 参考:https://www.cnblogs.com/klchang/p/8017627.html
# 或者根据负数补码的特点,使用模将负数转化:n = n & 0xffffffff,然后再进行减1做与运算
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1(self, n):
# write code here
if n == 0:
return 0
count = 0
# 假设系统的整数是四字节,由于python自身整数是8字节,所以手动设置整数的界限
# 例如-1在8字节情况下的补码是:1111.......1111(64个),在4字节下1111.......1111(32个)
MAX_INT = 1 << (32-1)
while n != 0:
if -MAX_INT > n or n > MAX_INT: # 主要是针对负数,当n为负数时,随着n = n & (n-1),n将越界
break # 如果不添加该终止循环的条件,程序将陷入死循环
count += 1
n = n & (n-1)
return count
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
# 方法一:普通方法
# 注意边界条件的控制、功能的测试、
# -*- coding:utf-8 -*-
class Solution:
def Power(self, base, exponent):
# write code here
if base == 0:
return 0
res = 1
if exponent == 0:
return res
elif exponent > 0:
for _ in range(exponent):
res *= base
return res
else:
for _ in range(abs(exponent)):
res *= base
if res != 0:
return 1/res
else:
return
利用如下的递推公式:
a n = { a n / 2 ⋅ a n / 2 n 为 偶 数 a ( n − 1 ) / 2 ⋅ a ( n − 1 ) / 2 ⋅ a n 为 奇 数 a^n = \begin{cases} a^{n/2}\cdot a^{n/2} & n 为偶数 \\ a^{(n-1)/2}\cdot a^{(n-1)/2}\cdot a & n 为奇数 \end{cases} an={an/2⋅an/2a(n−1)/2⋅a(n−1)/2⋅an为偶数n为奇数
# -*- coding:utf-8 -*-
class Solution:
def Power(self, base, exponent):
# write code here
# 要考虑base=0而exponent为负数这种异常情况的处理
if base == 0 and exponent < 0:
raise Exception('Error: base is zero ')
if base == 0:
return 0
if exponent == 0:
return 1
if exponent > 0:
return self.power_re(base, exponent)
else:
return 1/self.power_re(base, abs(exponent))
# 递归求解
def power_re(self, base, exponent):
if exponent == 1:
return base
if (exponent & 0x1) == 0:
return self.power_re(base, (exponent >> 1))*self.Power(base, (exponent >> 1))
else:
return self.power_re(base, ((exponent-1) >> 1)) * self.Power(base, ((exponent-1) >> 1))*base
解析:主要需要解决大数问题
# 方法一:使用字符串数组解决大数问题,实现进位功能
class Solution:
def Print1ToMaxofNDigits(self, n):
if n < 1:
raise Exception('invalid input n')
str_num = ['0'] * n
while True:
# 确定进位的位置
i = len(str_num) - 1
while str_num[i] == '9' and i > 0:
i -= 1
# 打印结束终止条件
if str_num[0] == '9' and i == 0:
break
# 不需要进位
if i == len(str_num)-1:
str_num[i] = str(int(str_num[i])+1)
self.Print(str_num)
# 需要进位
else:
str_num[i] = str(int(str_num[i])+1)
for j in range(i+1,len(str_num)):
str_num[j] = '0'
self.Print(str_num)
def Print(self, str_num):
index = 0
for i in range(len(str_num)):
if str_num[i] != '0':
index = i
break
print(''.join(str_num[index:]))
s = Solution()
s.Print1ToMaxofNDigits(3)
# 方法二:使用数字排列的方法实现
# 使用递归实现数字排列:从上到下分析,从下到上实现。。
# 从上到下分析:从最高位开始排列,高位排列完后排列低一位,直到排列完最低位
class Solution:
def Print1ToMaxofNDigits(self, n):
if n < 1:
raise Exception('invalid input n')
str_num = ['0'] * n
for i in range(10):
str_num[0] = str(i)
self.recursivePrint1ToMaxofNDigits(str_num, len(str_num), index=0)
def recursivePrint1ToMaxofNDigits(self, str_num, length, index):
# 递归终止条件
if index == length - 1:
self.Print(str_num)
return
for i in range(10):
str_num[index + 1] = str(i)
self.recursivePrint1ToMaxofNDigits(str_num, length, index + 1)
def Print(self, str_num):
index = -1
for i in range(len(str_num)):
if str_num[i] != '0':
index = i
break
if index != -1:
print(''.join(str_num[index:]))
s = Solution()
s.Print1ToMaxofNDigits(2)
给定单向链表的头指针和一个节点指针,定义一个函数在 O ( 1 ) O(1) O(1)时间内删除该节点。
lintcode: https://www.lintcode.com/problem/delete-node-in-a-linked-list/description
# 解题思路:将要删除节点的下一个节点复制到当前节点即可
"""
Definition of ListNode
class ListNode(object):
def __init__(self, val, next=None):
self.val = val
self.next = next
"""
class Solution:
"""
@param: node: the node in the list should be deleted
@return: nothing
"""
def deleteNode(self, node):
# write your code here
if not node or not node.next:
node = None
return
node.val = node.next.val
node.next = node.next.next
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
# 标准解答:https://github.com/apachecn/awesome-algorithm/blob/master/docs/%E5%89%91%E6%8C%87offer/Python/56-%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E4%B8%AD%E9%87%8D%E5%A4%8D%E7%9A%84%E7%BB%93%E7%82%B9.py
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteDuplication(self, pHead):
# write code here
if pHead is None or pHead.next is None:
return pHead
first = ListNode(-1)
first.next = pHead
last = first # 记录上一个有效节点
# 遍历不重复节点,更新last自身和last指向的节点
while pHead and pHead.next:
if pHead.val == pHead.next.val:
# 遍历得到与当前值不相等的节点,并将last指向该节点
val = pHead.val
while pHead and val == pHead.val:
pHead = pHead.next
last.next = pHead
else:
# 如果当前节点有效,则更新last节点
last = pHead
pHead = pHead.next
return first.next
请实现一个函数用来匹配包括’.‘和’*‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
题解参考:递归求解,根据s和pattern长度不同进行分情况讨论:
另外可以使用动态规划来优化递归,参考题解。
# -*- coding:utf-8 -*-
class Solution:
# s, pattern都是字符串
def match(self, s, pattern):
# write code here
#if not s and not pattern:
# return False
return self.match_recursion(s, pattern)
def match_recursion(self, s, pattern):
# 递归终止条件
if len(s) == 0 and len(pattern) == 0:
return True
elif len(s) != 0 and len(pattern) == 0:
return False
elif len(s) == 0 and len(pattern) != 0:
if len(pattern) > 1 and pattern[1] is "*":
return self.match_recursion(s, pattern[2:])
else:
return False
else:
# s和pattern都不为空
if len(pattern) > 1 and pattern[1] is "*":
# s和pattern第一个元素不相同,则s不变,pattern向后移动2位,相当于pattern前两位当成空
if s[0] != pattern[0] and pattern[0] != ".":
return self.match_recursion(s, pattern[2:])
else:
# 如果s[0]和pattern[0]相同,此时有三种情况
# 1.pattern后移2位,s不变;相当于把pattern前两位当成空,匹配后面的
# 2.pattern后移2位,s后移1位;相当于pattern前两位与s[0]匹配
# 3.pattern不变,s后移1位;相当于pattern前两位与s中的多位进行匹配
return self.match_recursion(s,pattern[2:]) or self.match_recursion(s[1:], pattern[2:]) or self.match_recursion(s[1:], pattern)
else:
if s[0] == pattern[0] or pattern[0] is ".":
return self.match_recursion(s[1:], pattern[1:])
else:
return False
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
# -*- coding:utf-8 -*-
class Solution:
# s字符串
def isNumeric(self, s):
# write code here
if len(s) < 1:
return False
dot_index = -1
e_index = -1
for i in range(len(s)):
if s[i] is '.':
dot_index = i
if s[i] is 'e' or s[i] is 'E':
e_index = i
break
res = True
if dot_index > 0:
# 小数点不在首位
res = res and self.isInt(s[:dot_index], 1)
if e_index > 0:
# 存在e或者E
res = res and self.scanInt(s[dot_index+1:e_index])
res = res and self.isInt(s[e_index+1:], 0)
else:
res = res and self.scanInt(s[dot_index+1:])
elif dot_index == 0:
# 小数点在首位
if e_index > 0:
# 存在e或者E
res = res and self.scanInt(s[dot_index+1:e_index])
res = res and self.isInt(s[e_index+1:], 0)
else:
res = res and self.scanInt(s[dot_index+1:])
else:
# 不存在小数点
if e_index > 0:
# 存在e或者E -+E
if s[dot_index+1] == '-' or s[dot_index+1] == '+':
res = res and self.scanInt(s[dot_index+2:e_index])
else:
res = res and self.scanInt(s[dot_index+1:e_index])
res = res and self.isInt(s[e_index+1:], 0)
else:
# 不存在小数点和E
res = res and self.isInt(s[dot_index+1:], 0)
return res
def isInt(self, s, case):
# 是否是整形数字,即“-+”在首位的0--9数字
# case = 1:表示要判断的部分在数字的开头,此时可以仅有“-+”,而没有数字
if len(s) < 1:
return False
if s[0] == '+' or s[0] == '-':
if len(s) == 1 and case:
return True
else:
return self.scanInt(s[1:])
else:
return self.scanInt(s[0:])
def scanInt(self, s):
# 必须在0--9之间
if len(s) < 1:
return False
a = [str(x) for x in range(10)]
for i in s:
if not i in a:
return False
return True
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
# -*- coding:utf-8 -*-
class Solution:
def reOrderArray(self, array):
if len(array) < 1:
return array
# write code here
odd_number = []
even_number = []
for i in array:
if i%2 == 1:
odd_number.append(i)
else:
even_number.append(i)
return odd_number+even_number
输入一个链表,输出该链表中倒数第k个结点
# 方法1:先计数,在输出
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindKthToTail(self, head, k):
# write code here
if head is None or k == 0:
return None
count = 1
temp = head
while temp.next != None:
count += 1
temp = temp.next
if count < k:
return None
else:
k = count - k
count = 0
while count != k:
count += 1
head = head.next
return head
# 方法二:双指针法,使用两个间隔为k-1的指针
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindKthToTail(self, head, k):
# write code here
if head is None or k == 0:
return None
fP = head
sP = head
count = 0
while count < k:
# 注意对fP是否为空的判断
if fP is None:
return None
fP = fP.next
count += 1
while fP != None:
fP = fP.next
sP = sP.next
return sP
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
if pHead is None:
return None
node_num = self.hasCircle(pHead)
if node_num == 0:
return None
else:
# 存在环,环中节点数为node_num,查找环的入口节点
fP = pHead # first point
sP = pHead # second point
count = 0
while count < node_num:
fP = fP.next
count += 1
while fP != sP:
fP = fP.next
sP = sP.next
return sP
def hasCircle(self, pHead):
# 判断是否存在circle,返回0表示不存在,返回正整数表示环中节点个数
fast = pHead
slow = pHead
while fast != None:
# fast 移动两步,slow移动一步
fast = fast.next
if fast == None:
break
fast = fast.next
slow = slow.next
if fast == slow:
# 存在环,统计环中节点的个数
num = 1
fast = fast.next
while fast != slow:
num += 1
fast = fast.next
return num
return 0
输入一个链表,反转链表后,输出新链表的表头。
# 借助三个指针,一边遍历链表一边修改当前节点的指向,直到遍历完整个链表
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# write code here
if pHead is None:
return None
previous_p = pHead
current_p = pHead.next
if current_p is None:
# 只有一个节点
return pHead
next_p = current_p.next
if next_p is None:
# 只有两个节点
previous_p.next = None
current_p.next = previous_p
return current_p
#节点数大于等于3,借助三个指针一边遍历一边修改指向
previous_p.next = None
while next_p != None:
current_p.next = previous_p
# 更新各个指针指向。向前移动
previous_p = current_p
current_p = next_p
next_p = next_p.next
current_p.next = previous_p
return current_p
# 方法二:递归法实现,从尾部开始修改指向
# -*- coding:utf-8 -*-
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# write code here
if pHead is None:
return None
previous_p = pHead
current_p = pHead.next
if current_p is None:
# 只有一个节点
return pHead
#节点数大于等于2,递归实现反转
previous_p.next = None
return self.Rev_recursion(previous_p, current_p)
def Rev_recursion(self, pre, cur):
# 递归终止条件:遍历至尾节点
if cur.next is None:
cur.next = pre
return cur
next_node = cur.next # 保存当前节点下一节点的指向
cur.next = pre # 修改当前节点指向上一节点
return self.Rev_recursion(cur, next_node)
def PrintList(self, pHead):
while pHead.next != None:
print(pHead.val)
pHead =pHead.next
print(pHead.val)
# test
a = ListNode(1)
b = ListNode(2)
c = ListNode(3)
d = ListNode(4)
a.next = b
b.next = c
c.next = d
s=Solution()
h = s.ReverseList(a)
s.PrintList(h)
4
3
2
1
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
Tips:python对象如果是结构体或类,则把对象的引用值修改,那么原始对象的值也会被改变。
# 方法1:确定头节点后,将另一个链表插入头节点所在的链表
# -*- coding:utf-8 -*-
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
if pHead1 is None:
return pHead2
elif pHead2 is None:
return pHead1
# 确定头结点
if pHead1.val > pHead2.val:
head = self.insertMerge(pHead2, pHead1)
else:
head = self.insertMerge(pHead1, pHead2)
return head
def insertMerge(self, head, insertL):
# head指头节点所在的链表,insertL链表中的元素将被插入head指向的链表
pHead = head
insert_h = insertL
while pHead.next != None:
pre1 = pHead
cur1 = pHead.next
insert_p = insert_h
if insert_p.val <= cur1.val:
# 介于两个节点之间,满足插入条件,插入后移动head所在链表和insertL链表
# !!!!必须先更新,在重新连接节点,否则变量的值已经被改变
# 如果按照注释中的顺序执行,则变量pHead和insert_h的值就会收到前面赋值的影响
# 此处insert_h值的更新必须要在insert_p.next的赋值之前,因为结构体内部值的改变会间接改变原始变量的值
# pre1.next = insert_p
# insert_p.next = cur1
# pHead = pHead.next
# insert_h = insert_h.next
pHead = pHead.next
insert_h = insert_h.next
pre1.next = insert_p
insert_p.next = cur1
else:
# 不满足插入条件,移动head所在链表,insertL链表不动
pHead = pHead.next
if insert_h != None:
pHead.next = insert_h
return head
def PrintList(self, pHead):
while pHead.next != None:
print(pHead.val)
pHead =pHead.next
print(pHead.val)
a = ListNode(1)
b = ListNode(3)
c = ListNode(5)
d = ListNode(7)
a.next = b
b.next = c
c.next = d
e = ListNode(2)
f = ListNode(4)
g = ListNode(6)
h = ListNode(8)
e.next = f
f.next = g
g.next = h
s = Solution()
h = s.Merge(a, e)
# s.PrintList(h)
# 方法二:递归法
# 只对两个链表的头节点进行重排,直到其中一个链表排至尾部时终止
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
# 递归终止条件
if pHead1 is None:
return pHead2
elif pHead2 is None:
return pHead1
if pHead1.val < pHead2.val:
pHead1.next = self.Merge(pHead1.next, pHead2)
return pHead1
else:
pHead2.next = self.Merge(pHead1, pHead2.next)
return pHead2
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def HasSubtree(self, pRoot1, pRoot2):
# write code here
# 特殊边界情况判断
if pRoot1 is None or pRoot2 is None:
return False
res = False
if pRoot1.val == pRoot2.val:
res = self.isEqual(pRoot1, pRoot2)
if not res:
# 当前节点不相等则继续递归遍历树
if pRoot1.left != None:
res = self.HasSubtree(pRoot1.left, pRoot2)
if not res and pRoot1.right != None:
res = self.HasSubtree(pRoot1.right, pRoot2)
return res
def isEqual(self, pRoot1, pRoot2):
# 递归终止条件
if pRoot2 is None :
return True
if pRoot1 is None:
return False
if pRoot2.val == pRoot1.val:
res = self.isEqual(pRoot1.left, pRoot2.left)
else:
return False
# 递归
res = res and self.isEqual(pRoot1.left, pRoot2.left) and self.isEqual(pRoot1.right, pRoot2.right)
return res
操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述:
二叉树的镜像定义:
源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5
非递归实现:
# 递归法
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回镜像树的根节点
def Mirror(self, root):
# write code here
# 异常情况判断
if root is None:
return
# 递归终止条件:遍历至非叶子节点
if root.left is None and root.right is None:
return
if root.left != None and root.right != None:
root.left, root.right = root.right, root.left
self.Mirror(root.left)
self.Mirror(root.right)
elif root.left != None and root.right is None:
root.left, root.right = None, root.left
self.Mirror(root.right)
elif root.left is None and root.right != None:
root.left, root.right = root.right, None
self.Mirror(root.left)
return
# 更简洁的递归实现
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回镜像树的根节点
def Mirror(self, root):
# write code here
# 异常情况判断
if root is None:
return
# 递归终止条件:遍历至非叶子节点
if root.left is None and root.right is None:
return
root.left, root.right = root.right, root.left
self.Mirror(root.left)
self.Mirror(root.right)
return
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSymmetrical(self, pRoot):
# write code here
# 空树对称
if pRoot is None:
return True
return self.issymm_recursion(pRoot.left, pRoot.right)
def issymm_recursion(self, left, right):
# 如果左右节点均为None,则遍历结束,且满足对称
if left is None and right is None:
return True
# 反之仅有一个节点为空则不满足对称
if left is None or right is None:
return False
# 当两个节点均不为空时,对其值进行判断
res = False
if left.val == right.val: # 两个节点的值相等时,对其子节点进行递归判断
# 对称前序遍历递归实现
res = self.issymm_recursion(left.left, right.right)
res = res and self.issymm_recursion(left.right, right.left)
return res
# 更简洁的参考实现:
# https://github.com/apachecn/awesome-algorithm/blob/master/docs/%E5%89%91%E6%8C%87offer/Python/58-%E5%AF%B9%E7%A7%B0%E7%9A%84%E4%BA%8C%E5%8F%89%E6%A0%91.py
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSymmetrical(self, pRoot):
# write code here
return self.selfIsSym(pRoot,pRoot)
def selfIsSym(self,root1,root2):
if root1 == root2 and root2 == None:
return True
if root1 == None or root2 == None:
return False
if root1.val != root2.val:
return False
return self.selfIsSym(root1.left, root2.right) and self.selfIsSym(root1.right,root2.left)
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
# 方法一:借助四个数指定打印范围
# -*- coding:utf-8 -*-
class Solution:
# matrix类型为二维列表,需要返回列表
def printMatrix(self, matrix):
# write code here
if len(matrix) < 1:
return None
if len(matrix[0]) <= 1:
m = [x[0] for x in matrix]
return m
row_len = len(matrix) # 行
col_len = len(matrix[0]) # 列
# 四个数确定打印的边界
a, b, c, d = 0, row_len-1, 0, col_len-1
m = []
while a <= b and c <= d:
# 在当前边界条件下打印一次
m = m + self.Print(matrix, a, b, c, d)
# 更新边界
a += 1
b -= 1
c += 1
d -= 1
return m
def Print(self, matrix, a, b, c, d):
m = []
# 打印过程为:先横向右移,再纵向下移,再横向左移,最后纵向上移
m = matrix[a][c:d+1] # 横向右移打印
# 如果a=b,则只需要横向打印一次即完成打印
if a == b:
return m
m = m + [x[d] for x in matrix[a+1:b+1]] # 纵向下移打印
if c == 0: #横向左移打印
m = m + (matrix[b][d-1::-1])
else:
m = m + (matrix[b][d-1:c-1:-1])
m = m + ([x[c] for x in matrix[b-1:a:-1]]) # 纵向上移打印
return m
s = Solution()
a = [[1,2],[3,4]]
s.printMatrix(a)
[1, 2, 4, 3]
# 方法二:根据规律左上角的坐标中行标和列标总是相等的
# 参考代码
# https://github.com/apachecn/awesome-algorithm/blob/master/docs/%E5%89%91%E6%8C%87offer/Python/19-%E9%A1%BA%E6%97%B6%E9%92%88%E6%89%93%E5%8D%B0%E7%9F%A9%E9%98%B5.py
class Solution:
# matrix类型为二维列表,需要返回列表
def printMatrix(self, matrix):
if not matrix:
return []
rows = len(matrix)
columns = len(matrix[0])
start = 0
result = []
while rows > start * 2 and columns > start * 2:
self.PrintMatrixInCircle(matrix, columns, rows, start,result)
start += 1
return result
def PrintMatrixInCircle(self, matrix, columns, rows,start,result):
endX = columns - 1 - start
endY = rows - 1 - start
# 从左到右打印一行
for i in range(start, endX+1):
#number = matrix[start][i]
result.append(matrix[start][i])
# 从上到下打印一行
if start < endY:
for i in range(start+1, endY+1):
#number = matrix[i][endX]
result.append(matrix[i][endX])
# 从右到左打印一行
if start < endX and start < endY:
for i in range(endX-1, start-1, -1):
#number = matrix[endY][i]
result.append(matrix[endY][i])
# 从下到上打印一行
if start < endX and start < endY-1:
for i in range(endY-1, start, -1):
#number = matrix[i][start]
result.append(matrix[i][start])
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
# 借助辅助栈实现
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.stack = []
self.assist_stack = []
def push(self, node):
# write code here
self.stack.append(node)
if len(self.assist_stack) < 1:
self.assist_stack.append(node)
elif node < self.assist_stack[-1]:
self.assist_stack.append(node)
else:
self.assist_stack.append(self.assist_stack[-1])
def pop(self):
# write code here
if len(self.stack) < 1:
return None
else:
self.assist_stack.pop()
return self.stack.pop()
def top(self):
# write code here
return self.stack[-1]
def min(self):
# write code here
return self.assist_stack[-1]
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
# 方法:借助辅助栈实现
# -*- coding:utf-8 -*-
class Solution:
def IsPopOrder(self, pushV, popV):
# write code here
if len(pushV) != len(popV):
return False
elif len(pushV) < 1:
return True
stack = []
for i in popV:
# 辅助栈为空时,根据情况向辅助栈压入一个元素
if len(stack) < 1:
if len(pushV) < 1:
return False
else:
stack.append(pushV.pop(0))
while i != stack[-1]:
# 判断辅助栈栈顶元素是否和当前弹出元素相等,不相等则将压入序列中元素不断压入辅助栈,直到发现相等元素
if len(pushV) < 1:
# 如果压入序列为空,则没有元素可以匹配,所以不满足弹出顺序
return False
else:
stack.append(pushV.pop(0))
# 如果弹出序列和辅助栈栈顶元素相等则,将辅助栈栈顶元素弹出
stack.pop()
return True
# 参考实现
# -*- coding:utf-8 -*-
class Solution:
def IsPopOrder(self, pushV, popV):
# write code here
if pushV == [] or popV == []:
return False
stack = []
for i in pushV:
stack.append(i)
while len(stack) and stack[-1] == popV[0]:
stack.pop()
popV.pop(0)
if len(stack):
return False
else:
return True
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
# 方法:借助辅助队列实现
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回从上到下每个节点值列表,例:[1,2,3]
def PrintFromTopToBottom(self, root):
# write code here
if root is None:
return []
que = []
res = []
que.append(root)
while len(que) >= 1:
node = que.pop(0)
res.append(node.val)
if node.left != None:
que.append(node.left)
if node.right != None:
que.append(node.right)
return res
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回二维列表[[1,2],[4,5]]
def Print(self, pRoot):
# write code here
if pRoot is None:
return []
que1 = [] # 存储每行元素构成的队列,que1是二维数组
que2 = [] # 将一行元素作为队列存储
que2.append(pRoot)
que1.append(que2)
res = [] # 存储最终结果,是二维数组
res1 = [] # 存储每一行打印的结果
while len(que1) >= 1:
# 打印所有行
nodes = que1.pop(0)
que2 = []
for node in nodes:
# 打印一行
res1.append(node.val)
if node.left != None:
que2.append(node.left)
if node.right != None:
que2.append(node.right)
if len(que2) >= 1:
que1.append(que2)
# 存储当前行打印的结果
res.append(res1)
res1 = []
return res
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def Print(self, pRoot):
# write code here
if pRoot is None:
return []
count = 1 # 打印层数记录
res, res1 = [], []
nodes = [pRoot]
while nodes:
que = []
if count % 2 == 1:
for node in nodes:
res1.append(node.val)
# 节点存储顺序由左至右,然后翻转
if node.left != None:
que.append(node.left)
if node.right != None:
que.append(node.right)
else:
for node in nodes:
res1.append(node.val)
# 节点存储顺序由右至左,然后翻转
if node.right != None:
que.append(node.right)
if node.left != None:
que.append(node.left)
nodes = que[::-1]
res.append(res1)
res1 = []
count += 1
return res
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
# -*- coding:utf-8 -*-
class Solution:
def VerifySquenceOfBST(self, sequence):
# write code here
if len(sequence) == 0:
return False
return self.recursion_verify(sequence)
def recursion_verify(self, sequence):
if len(sequence) <= 1:
return True
root = sequence[-1]
for i in range(len(sequence)-1):
if sequence[i] > root:
break
# 如果只有左子树,仅对左子树递归判断
if i == len(sequence)-2:
return self.recursion_verify(sequence[0:i])
for j in sequence[i:-1]:
if j < root:
return False
# 左右子树都有节点,则对左右子树递归判断
return self.recursion_verify(sequence[0:i]) and self.recursion_verify(sequence[i:-1])
s=Solution()
# a = [4,8,6,12,16,14,10]
a = [5,4,3,2,1]
print(s.VerifySquenceOfBST(a))
True
输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
# 方法:递归法
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回二维列表,内部每个列表表示找到的路径
def FindPath(self, root, expectNumber):
# write code here
if root is None:
return []
stack = [] # 辅助栈,存储路径节点
path = [] # 存储路径
sums = root.val # 和值
stack.append(root)
res = self.recursion_find(root, expectNumber, stack, sums, path)
return res
def recursion_find(self, root, expectNumber, stack, sums, path):
# 递归终止条件:递归至叶节点
# 或只有根节点的情况
if root.left is None and root.right is None:
if sums == expectNumber:
res = [x.val for x in stack] # 将路径节点转换为值列表
path.append(res)
return path
else:
return path
else: #VLR前序递归
if root.left != None: # 递归左节点
stack.append(root.left)
sums += root.left.val
self.recursion_find(root.left, expectNumber, stack, sums, path)
stack.pop() # 一次递归结束后弹出当前节点,并恢复原来的和值
sums -= root.left.val
if root.right != None: # 递归右节点
stack.append(root.right)
sums += root.right.val
self.recursion_find(root.right, expectNumber, stack, sums, path)
stack.pop() # 一次递归结束后弹出当前节点,并恢复原来的和值
sums -= root.right.val
return sorted(path, key=lambda x: -len(x)) # 降序排列
# 参考解法
# https://github.com/apachecn/awesome-algorithm/blob/master/docs/%E5%89%91%E6%8C%87offer/Python/24-%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%AD%E5%92%8C%E4%B8%BA%E6%9F%90%E4%B8%80%E5%80%BC%E7%9A%84%E8%B7%AF%E5%BE%84.py
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回二维列表,内部每个列表表示找到的路径
def FindPath(self, root, expectNumber):
# write code here
if not root or root.val > expectNumber: # 没有找到路径,递归终止
return []
if not root.left and not root.right and root.val == expectNumber: # 找到路径
return [[root.val]]
else:
expectNumber -= root.val
left = self.FindPath(root.left,expectNumber)
right = self.FindPath(root.right,expectNumber)
result = [[root.val]+i for i in left]
for i in right:
result.append([root.val]+i)
return sorted(result, key=lambda x:-len(x))
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
# 解法一: 分两步:第一步:顺序复制链表,第二步:复制特殊指针,每次从原始链表头部开始遍历查找特殊指针的位置
# 时间复杂度:O(n^2)
# -*- coding:utf-8 -*-
# class RandomListNode:
# def __init__(self, x):
# self.label = x
# self.next = None
# self.random = None
class Solution:
# 返回 RandomListNode
def Clone(self, pHead):
# write code here
if pHead is None:
return None
pH_new_c = RandomListNode(pHead.label) # 复制链表的头结点
pH_new = pH_new_c # 定义临时变量,用于遍历
next_node = pHead.next # 定义临时变量,用于遍历
# 先顺序复制
while next_node != None:
copy_next_node = RandomListNode(next_node.label)
pH_new.next = copy_next_node
next_node = next_node.next
pH_new = copy_next_node
# 复制特殊指针
pH_new = pH_new_c # 临时变量,用于遍历
next_node = pHead
rand_node = pHead.random
while next_node != None:
rand_node = next_node.random
if rand_node == None:
pH_new.random = None
else:
temp = pHead # 原始头结点
copy_random = pH_new_c # 复制链表的头结点
# 同时遍历原始链表和复制链表,寻找复制链表中特殊指针指向的节点
while temp != None:
if temp.label == rand_node.label: # 找到特殊指针指向的节点
break
else:
temp = temp.next
copy_random = copy_random.next
pH_new.random = copy_random # 给复制链表当前节点的特殊指针赋值
# 进行下个节点特殊指针的赋值
next_node = next_node.next
pH_new = pH_new.next
return pH_new_c
# 解法二:时间换空间法,借助哈希表存储当前节点和特殊指针之间的对应关系
# 时间和空间复杂度均为:O(n)
# -*- coding:utf-8 -*-
# class RandomListNode:
# def __init__(self, x):
# self.label = x
# self.next = None
# self.random = None
class Solution:
# 返回 RandomListNode
def Clone(self, pHead):
# write code here
if pHead is None:
return None
pH_new_c = RandomListNode(pHead.label) # 复制链表的头结点
pH_new = pH_new_c # 定义临时变量,用于遍历
next_node = pHead.next # 定义临时变量,用于遍历
N_Nc = {} # 定义字典,存储赋值前--赋值后节点对
N_Nc[pHead.label] = pH_new
# 先顺序复制
while next_node != None:
copy_next_node = RandomListNode(next_node.label)
pH_new.next = copy_next_node
N_Nc[next_node.label] = copy_next_node # 存储赋值前--赋值后节点对
next_node = next_node.next
pH_new = copy_next_node
# 复制特殊指针
pH_new = pH_new_c # 临时变量,用于遍历
next_node = pHead
rand_node = pHead.random
while next_node != None:
rand_node = next_node.random
if rand_node == None:
pH_new.random = None
else:
# 借助哈希表存储的赋值前--赋值后节点对,给复制链表当前节点的特殊指针赋值
pH_new.random = N_Nc[rand_node.label]
# 进行下个节点特殊指针的赋值
next_node = next_node.next
pH_new = pH_new.next
return pH_new_c
# 解法三:不使用辅助空间,在原始链表上添加复制链表成为一个长链表,然后拆分出复制链表
# -*- coding:utf-8 -*-
# class RandomListNode:
# def __init__(self, x):
# self.label = x
# self.next = None
# self.random = None
class Solution:
# 返回 RandomListNode
def Clone(self, pHead):
# write code here
if pHead is None:
return None
node = pHead # 定义临时变量,用于遍历
# 先顺序复制,将复制的节点接在原始节点后面,得到一个长链表
while node != None:
next_node = node.next
copy_next_node = RandomListNode(node.label)
copy_next_node.next = next_node
node.next = copy_next_node
node = next_node
node = pHead
while node != None:
next_node = node.next
if node.random != None:
next_node.random = node.random.next
node = next_node.next
node = pHead
pH_new = pH_new_c = node.next # 复制链表的头节点
node.next = pH_new.next # 需要借助node.next进行节点的赋值
node = node.next
while node != None:
pH_new_c.next = node.next # 复制节点
pH_new_c = pH_new_c.next # 复制节点
node.next = pH_new_c.next # 原始节点,需要借助node.next进行节点的赋值
node = node.next #原始节点
return pH_new
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
# 方法:将二叉搜索树的左右节点分别重置为双向链表的向前、向后指针
# 使用LVR递归遍历实现
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def Convert(self, pRootOfTree):
# write code here
if pRootOfTree is None:
return None
return self.recursion_modify(pRootOfTree)
def recursion_modify(self, pRoot):
if pRoot is None:
return None
if pRoot.left is None and pRoot.right is None:
return pRoot
self.recursion_modify(pRoot.left)
left = pRoot.left
if left != None:
while left.right != None:
left = left.right
pRoot.left = left
left.right = pRoot
self.recursion_modify(pRoot.right)
right = pRoot.right
if right != None:
while right.left != None:
right = right.left
pRoot.right = right
right.left = pRoot
while pRoot.left != None:
pRoot = pRoot.left
return pRoot
请实现两个函数,分别用来序列化和反序列化二叉树
# 使用前序遍历实现序列化和反序列化
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def Serialize(self, root):
# write code here
if root is None:
return '$,'
return str(root.val) + ',' + self.Serialize(root.left) + self.Serialize(root.right)
def Deserialize(self, s):
# write code here
# 特殊情况处理
if len(s) < 1:
return None
# 原始二叉树为空时,序列化得到的是"$,",此时直接返回None
elif len(s) == 2 and s[0] == '$':
return None
s_list = s.split(',')
root = TreeNode(int(s_list[0])) # 设置头节点
s_list.pop(0) # 将用过的节点弹出
# 递归重构二叉树
root.left = self.recursion_deserialize(s_list)
root.right = self.recursion_deserialize(s_list)
return root
def recursion_deserialize(self, s):
if len(s) < 1:
return None
if s[0] == '$':
s.pop(0)
return None
# 递归重构二叉树
val = s.pop(0)
node = TreeNode(int(val))
node.left = self.recursion_deserialize(s)
node.right = self.recursion_deserialize(s)
return node
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
# 递归法:将字符串分为头部+头部之外两部分
# -*- coding:utf-8 -*-
class Solution:
def Permutation(self, ss):
# write code here
if ss is None:
return []
if len(ss) == 1:
return list(ss)
s = list(ss)
s.sort() # 得到升序排列的字符数组
res = []
for i in range(len(s)):
if i > 0 and s[i] == s[i-1]: # 重复字符排列过就不用再重排
continue
temp = self.Permutation(''.join(s[:i])+''.join(s[i+1:]))
for j in temp:
res.append(s[i] + j)
return res
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
# 方法一:借助字典实现,统计每个数字出现的频次
# 时间和空间复杂度均为:O(n)
# -*- coding:utf-8 -*-
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
# write code here
if numbers is None:
return 0
count = {}
length = len(numbers) // 2
for i in numbers:
if i in count:
count[i] += 1
else:
count[i] = 1
for key in count:
if count[key] > length:
return key
return 0
# 方法二:根据数组特点寻找,次数超过一半
# 使用两个变量记录当前数字和数字出现的次数,出现与当前数字相同的数时次数加1,否则减1,若减为0,则将之前的数改为当前的数字
# 最后存储的数字在进行是否频次大于长度一半的验证即可
# -*- coding:utf-8 -*-
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
# write code here
if numbers is None:
return 0
count = 1
num = numbers[0]
for i in numbers[1:]:
if i == num:
count += 1
else:
if count > 0:
count -= 1
else:
count += 1
num = i
# 验证num是否次数大于长度一半
count = 0
for i in numbers:
if i == num:
count += 1
if count > len(numbers) // 2:
return num
else:
return 0
# 方法三:利用快速排序得到排序后的数组,判断排序后的数组中位数是否出现频次大于数组长度一半
# 时间复杂度:O(nlogn)
# -*- coding:utf-8 -*-
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
# write code here
if numbers is None:
return 0
nums = self.quickSort(numbers) # 排序
num = nums[len(nums)//2] # 取中位数
# 验证num是否次数大于长度一半
count = 0
for i in numbers:
if i == num:
count += 1
if count > len(numbers) // 2:
return num
else:
return 0
# 快排
def quickSort(self, numbers):
if len(numbers) <= 1:
return numbers
pivot = numbers[0]
left = [x for x in numbers if x < pivot]
mid = [x for x in numbers if x == pivot]
right = [x for x in numbers if x > pivot]
return self.quickSort(left) + mid + self.quickSort(right)
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
# 方法一:排序后取前K个
# 时间复杂度由排序算法决定:快排则为O(nlogn)
# -*- coding:utf-8 -*-
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# write code here
if tinput is None or len(tinput) < k:
return []
tinput = self.quickSort(tinput)
return tinput[0:k]
def quickSort(self, arr):
if len(arr) <= 1:
return arr
pivot = arr[0]
left = [x for x in arr if x < pivot]
mid = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return self.quickSort(left) + mid + self.quickSort(right)
# 方法二:类似计数排序法,计数后取出前K个
# 时间复杂度O(n+k)
# -*- coding:utf-8 -*-
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# write code here
if tinput is None or len(tinput) < k or k == 0:
return []
tinput = self.countingSort(tinput, k)
return tinput
def countingSort(self, arr, k):
min_num = min(arr)
max_num = max(arr)
counts = [0] * (max_num-min_num+1)
for i in arr:
counts[i-min_num] += 1
res = []
count = 0
for i in range(len(counts)):
for j in range(counts[i]):
res.append(i+min_num)
if len(res) == k:
return res
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
# 方法一:使用有序数组
# 插入数据和查找中位数的时间复杂度分别为:O(n),O(1)
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.num = []
def Insert(self, num):
# write code here
if num != None:
length = len(self.num)
for i in range(length):
if num <= self.num[i]:
self.num.insert(i, num)
break
# 如果没有插入,则说明该数最大,所以放置于数组末尾
if length == len(self.num):
self.num.append(num)
def GetMedian(self, x):
# write code here
if len(self.num) < 1:
return None
length = len(self.num)
if length % 2 == 1:
return self.num[length//2]
else:
return (self.num[length//2]+self.num[length//2-1])/2.0
# 方法二:使用大顶堆和小顶堆存储数据,实现中位数的查找
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.min_heap = [] # 小顶堆
self.max_heap = [] # 大顶堆
def Insert(self, num):
# write code here
# 根据数据总个数插入,当数据总数为奇数时插入大顶堆,否则插入小顶堆
length = len(self.min_heap) + len(self.max_heap)
if length % 2 == 1: # 总数为奇数,将元素插入大顶堆
self.min_heap.append(num)
min_num = self.adjustMin()
self.min_heap.pop(0) # 将小顶堆最小元素弹出后放入大顶堆
self.adjustMin() # 弹出后再次调整堆结构
self.max_heap.append(min_num)
self.adjustMax()
else: # 总数为偶数,将元素插入小顶堆
self.max_heap.append(num)
max_num = self.adjustMax()
self.max_heap.pop(0) # 将小顶堆最小元素弹出后放入大顶堆
self.adjustMax() # 弹出后再次调整堆结构
self.min_heap.append(max_num)
self.adjustMin()
def GetMedian(self, x):
# x 为无效参数
# write code here
length = len(self.min_heap) + len(self.max_heap)
if length == 0:
return []
if length % 2 == 1:
return self.min_heap[0]
else:
return (self.max_heap[0]+self.min_heap[0])/2.0
def adjustMax(self):
# 大顶堆调整,返回堆顶元素
length = len(self.max_heap)
if length < 1:
return
if length == 1:
return self.max_heap[0]
i = length // 2 - 1
while i >= 0:
left = 2*i + 1
right = 2*i + 2
if right < length and self.max_heap[left] < self.max_heap[right]:
left = right
if self.max_heap[left] > self.max_heap[i]:
self.max_heap[i], self.max_heap[left] = self.max_heap[left], self.max_heap[i]
if left <= length // 2 - 1:
i = left
continue
i -= 1
return self.max_heap[0]
def adjustMin(self):
# 小顶堆调整,返回堆顶元素
length = len(self.min_heap)
if length < 1:
return
if length == 1:
return self.min_heap[0]
i = length // 2 - 1
while i >= 0:
left = 2*i + 1
right = 2*i + 2
if right < length and self.min_heap[left] > self.min_heap[right]:
left = right
if self.min_heap[left] < self.min_heap[i]:
self.min_heap[i], self.min_heap[left] = self.min_heap[left], self.min_heap[i]
if left <= length // 2 - 1:
i = left
continue
i -= 1
return self.min_heap[0]
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)要求时间复杂度为 O ( n ) O(n) O(n)
# 方法一:根据数组和的规律进行查找
# -*- coding:utf-8 -*-
class Solution:
def FindGreatestSumOfSubArray(self, array):
# write code here
if array is None or len(array) == 0:
return
if len(array) == 1:
return array[0]
max_sum = 0
final_max = array[0] # 存储最大和
for i in range(len(array)):
max_sum += array[i]
print(max_sum)
print(array[i])
if max_sum < array[i]:
max_sum = array[i]
if final_max < max_sum:
final_max = max_sum
return final_max
# 方法二:动态规划法
# 设f(i)表示第i个数结尾的的子数组的最大和,则要求的最大和为max(f(i))
# -*- coding:utf-8 -*-
class Solution:
def FindGreatestSumOfSubArray(self, array):
# write code here
if array is None or len(array) == 0:
return
if len(array) == 1:
return array[0]
max_sum = 0 # f(i)
final_max = array[0] # 存储最大和 max(f(i))
for i in array:
# 循环实现动态规划
if max_sum <= 0:
max_sum = i
else:
max_sum += i
if final_max < max_sum:
final_max = max_sum
return final_max
求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
# 方法一:暴力遍历法,时间复杂度为O(nlogn)
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
# write code here
if n is None:
return
count = 0
for i in range(1,n+1):
count += self.countNum(i)
return count
def countNum(self, n):
count = 0
while n > 0:
if n % 10 == 1:
count += 1
n = n // 10
return count
s = Solution()
s.NumberOf1Between1AndN_Solution(10000)
4001
# 方法二:递归法,根据数字的规律进行递归求解
# 时间复杂度:O(logn)
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
# write code here
if n is None or n < 1:
return 0
return self.recursion_count(n)
def recursion_count(self, n):
if n == 0:
return 0
if n < 10:
return 1
count = 0
high_num, time = self.getHigh(n)
# 1在最高位时
if high_num > 1:
count = pow(10, time-1)
else:
count = n - pow(10, time-1) + 1
# 1在除最高位之外的位置时
count += high_num * (time - 1) * pow(10, time - 2) # 排列组合
# 递归
count += self.recursion_count(n - high_num*pow(10, time-1))
return count
def getHigh(self, n):
# 获取高位数字和总的位数
if n == 0:
return 0
time = 0
while n > 0:
time += 1
if n < 10:
return n, time
n = n // 10
数字以01234567891011121314…的格式排列。在这个序列中,第5位(从0开始计)是5,第13位是1,第19位是4。求任意第n为对应的数字。
**PS:**此题牛客网没有对应的在线评测,可以在acwing第57题进行在线评测。
# 方法:根据数字不同位数所占个数的规律进行求解
class Solution(object):
def digitAtIndex(self, n):
"""
:type n: int
:rtype: int
"""
i = 1 # 数字位数
while n - self.count_of_integers(i) >= 0:
n = n - self.count_of_integers(i)
i += 1
rest = n // i # 相对当前位数数字所处的位置
inte = n % i # 相对当前数字的位置
return self.get_number(i, rest, inte)
def count_of_integers(self, n):
# 计算不同位数数字的个数
if n == 1:
return 10
else:
return (pow(10, n) - pow(10, n-1)) * n
def get_number(self, i, rest, inte):
# 根据数字位数i,相对i位数起始第rest个数字,得到第rest个数字的第inte位的数字
if i == 1:
num = 0 + rest
else:
num = pow(10, i-1) + rest
inte = i - inte - 1
while inte != 0:
num = num // 10
inte -= 1
return num % 10
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
# 方法:重新定义比较大小函数,根据字符串m+n与n+m的比较结果进行排序
# python3中利用functools模块,实现自定义排序
# -*- coding:utf-8 -*-
import functools
class Solution:
def PrintMinNumber(self, numbers):
# write code here
if numbers is None or len(numbers) == 0:
return ''
numbers = [str(x) for x in numbers]
numbers.sort(key=functools.cmp_to_key(self.comp))
return "".join(numbers)
def comp(self, x, y):
if (str(x)+str(y)) < (str(y)+str(x)):
return -1
else:
return 1
s = Solution()
num = [3,5,1,4,2]
#num = [3, 32, 321]
s.PrintMinNumber(num)
'12345'
给定一个数字,我们按照如下规则把它翻译为字符串:
0翻译成”a”,1翻译成”b”,……,11翻译成”l”,……,25翻译成”z”。
一个数字可能有多个翻译。例如12258有5种不同的翻译,它们分别是”bccfi”、”bwfi”、”bczi”、”mcfi”和”mzi”。
请编程实现一个函数用来计算一个数字有多少种不同的翻译方法。
PS: 此题牛客网没有对应的在线评测,可以在acwing第55题进行评测。
自上而下的实现(即从字符串左至右递归),存在很多重复计算的问题。
递归公式: f ( i ) = f ( i + 1 ) + g ( i , i + 1 ) ∗ f ( i + 2 ) f(i)=f(i+1)+g(i,i+1)*f(i+2) f(i)=f(i+1)+g(i,i+1)∗f(i+2)
其中 f ( i ) f(i) f(i)表示从第i位数字开始的翻译数目, g ( i , i + 1 ) g(i,i+1) g(i,i+1)表示第i位和第i+1为组合的数字如果在10–25范围则为1,否则为0
class Solution:
def getTranslationCount(self, s):
"""
:type s: str
:rtype: int
"""
if int(s) < 0 or s is None:
return 0
# 将数字拆分
s = [x for x in s]
return self.recursion_count(s)
def recursion_count(self, s):
# 递归终止条件
if len(s) < 2:
return 1
count = 0
# 将s拆分为1个元素和其余元素
count += self.recursion_count(s[1:])
# 将s拆分为前两个元素和其余元素
if 10 <= int(s[0]+s[1]) <= 25:
if len(s) >= 3:
count += self.recursion_count(s[2:])
else:
count += 1
return count
自下而上实现(即从字符串右至左循环)效率更高。递归公式相同: f ( i ) = f ( i + 1 ) + g ( i , i + 1 ) ∗ f ( i + 2 ) f(i)=f(i+1)+g(i,i+1)*f(i+2) f(i)=f(i+1)+g(i,i+1)∗f(i+2)
从i=len(s)开始循环计算。
class Solution:
def getTranslationCount(self, s):
"""
:type s: str
:rtype: int
"""
if int(s) < 0 or s is None:
return 0
# 将数字拆分
s = [x for x in s]
return self.curlce_count(s)
def curlce_count(self, s):
# 递归终止条件
if len(s) < 2:
return 1
count = 0
length = len(s)
# 递推公式fi = fi_1 + g(i,i+1)*fi_2
# 初始赋值,i从len(s)-1开始,此时赋值如下
fi_2,fi_1,fi = 0, 0, 1
for i in range(length-2, -1, -1):
if i+2 > length:
fi_2 = 0
# 更新
fi_2, fi_1 = fi_1, fi
if 10 <= int(s[i]+s[i+1]) <= 25:
fi = fi_1 + fi_2
else:
fi + fi_1
return fi
在一个m×n的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于0)。
你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格直到到达棋盘的右下角。
给定一个棋盘及其上面的礼物,请计算你最多能拿到多少价值的礼物?
**PS:**此题牛客网没有对应的在线评测,可以在acwing第60题进行评测。
# 方法一:动态规划
# 递推公式:f(i,j) = max(f(i, j-1), f(i-1, j)) + gift[i, j]
class Solution(object):
def getMaxValue(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
if grid is None or len(grid) < 1 or len(grid[0]) < 1:
return 0
m, n = len(grid), len(grid[0])
assist_list = [[0 for _ in range(n)] for _ in range(m)] # 辅助二维数组
# 递推公式为:f(i,j) = max(f(i, j-1), f(i-1, j)) + gift[i, j]
for i in range(m):
for j in range(n):
if i == 0 and j == 0:
assist_list[0][0] = grid[0][0]
elif i == 0:
assist_list[0][j] = assist_list[0][j-1] + grid[0][j]
elif j == 0:
assist_list[i][0] = assist_list[i-1][0] + grid[i][0]
else:
assist_list[i][j] = max(assist_list[i-1][j], assist_list[i][j-1]) + grid[i][j]
return assist_list[-1][-1]
# 方法二: 在动态规划的基础上,可以进一步优化空间使用,只使用一维数组进行辅助
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
假设字符串中只包含从’a’到’z’的字符。
这个题没有包含在牛客网《剑指offer》题库里,可以在以下网址进行在线评测:
长度为n的字符串,其子字符串的个数为 n ∗ ( n + 1 ) / 2 + 1 n*(n+1)/2+1 n∗(n+1)/2+1,所以从长度为n的字符串中生成子串的时间复杂度为 O ( n 2 ) O(n^2) O(n2).
则暴力法的时间复杂度为: O ( n 3 ) O(n^3) O(n3)
# 方法一:双指针法
class Solution:
def longestSubstringWithoutDuplication(self, s):
"""
:type s: str
:rtype: int
"""
if s is None:
return 0
s = [x for x in s]
length = len(s)
assist_list = [-1]*26 # 辅助数组,记录字符出现的位置
left, right = 0, 0 # 左右双指针扫描
max_l, count = 0, 0
for left in range(length):
right = left
while right < length and assist_list[ord(s[right])-ord('a')] == -1: # 当右指针未到末尾且当前字符未出现过时继续扫描
count += 1
assist_list[ord(s[right])-ord('a')] = right
right += 1
if max_l < count:
max_l = count
# 从left下一位置重新扫描计数
count = 0
assist_list = [-1]*26
return max_l
递推公式: f ( i ) = f ( i − 1 ) + 1 f(i) = f(i-1) + 1 f(i)=f(i−1)+1, f(i)表示以第i个字符结尾的不重复子串的最长长度
分情况讨论:
class Solution:
def longestSubstringWithoutDuplication(self, s):
"""
:type s: str
:rtype: int
"""
if s is None:
return 0
s = [x for x in s]
length = len(s)
assist_list = [-1]*26
fi_1, fi, d = 0, 0, 0
max_l = 0
for i in range(length):
if assist_list[ord(s[i])-ord('a')] == -1:
fi = fi + 1
else:
d = i - assist_list[ord(s[i])-ord('a')]
if d <= fi:
fi = d
else:
fi = fi + 1
# 更新字符位置
assist_list[ord(s[i])-ord('a')] = i
# 记录最大值
if max_l < fi:
max_l = fi
return max_l
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
# 方法一:暴力遍历法,时间复杂度过大,不可取
# -*- coding:utf-8 -*-
class Solution:
def GetUglyNumber_Solution(self, index):
# write code here
if index < 1:
return 0
i, n = 1, 1
while i < index:
n += 1
if self.ifUgly(n):
i += 1
return n
def ifUgly(self, number):
while number % 2 == 0:
number = number // 2
while number % 3 == 0:
number = number // 3
while number % 5 == 0:
number = number // 5
return number == 1
# 方法二:根据丑数生成的规律,使用辅助数组进行丑数生成和查找
# -*- coding:utf-8 -*-
class Solution:
def GetUglyNumber_Solution(self, index):
# write code here
if index < 1:
return 0
i, n = 1, 1
ugly_number_list = [1] # 辅助数组,存储所有的丑数
Max = ugly_number_list[0] # 当前最大的丑数
T2, T3, T5 = 0, 0, 0 # 记录上一次大于最大丑数的位置
M2, M3, M5 = 0, 0, 0 # 记录当前大于最大丑数的第一个数
while i < index:
for j in range(T2, len(ugly_number_list)):
if ugly_number_list[j] * 2 > Max:
M2 = ugly_number_list[j] * 2
T2 = j
break
for j in range(T3, len(ugly_number_list)):
if ugly_number_list[j] * 3 > Max:
M3 = ugly_number_list[j] * 3
T3 = j
break
for j in range(T5, len(ugly_number_list)):
if ugly_number_list[j] * 5 > Max:
M5 = ugly_number_list[j] * 5
T5 = j
break
ugly_number_list.append(min(M2,M3,M5)) # 按顺序添加丑数
Max = ugly_number_list[-1] # 更新当前的最大丑数
i += 1
return ugly_number_list[-1]
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)
# 方法:借助字典统计字符出现次数,确定第一个出现一次的字符,时间复杂度O(n)
# -*- coding:utf-8 -*-
class Solution:
def FirstNotRepeatingChar(self, s):
# write code here
if s is None or len(s) < 1:
return -1
count_dict = {}
# 使用字典统计字符出现次数
for i in s:
if count_dict.has_key(i):
count_dict[i] += 1
else:
count_dict[i] = 1
# 再次遍历,确定第一个只出现一次的字符
for p, i in enumerate(s):
if count_dict[i] == 1:
return p
return -1
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
# 方法:借助字典统计字符出现的位置,当之前出现过时,将位置置为特殊标记,比如-1
# 插入和查找的时间复杂度分别为O(1)、O(n)
# -*- coding:utf-8 -*-
class Solution:
# 返回对应char
def __init__(self):
self.count_dict = {} # 辅助数组
self.counter = 0 # 记录字符个数,即字符出现位置
def FirstAppearingOnce(self):
# write code here
firstChar, count = None, 1000000 # count表示第一个出现一次字符的位置,初始时赋一个大值
for char, count_ in self.count_dict.items():
if count_ != -1 and count_ < count:
firstChar = char
count = count_
if firstChar is not None:
return firstChar
else:
return '#'
def Insert(self, char):
# write code here
if char is None:
return
self.counter += 1
if self.count_dict.has_key(char):
self.count_dict[char] = -1
else:
self.count_dict[char] = self.counter
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
**PS:**此题牛客网Python实现都超时无法通过,在AcWing65题评测使用Python可以通过。
# 方法一:暴力法,时间复杂度O(n^2),时间复杂度过大,不可取
class Solution:
def InversePairs(self, data):
# write code here
if data is None or len(data) < 2:
return 0
P = 0
for i in range(len(data)-1):
for j in range(i+1, len(data)):
if data[i] > data[j]:
P += 1
return P%1000000007
s = Solution()
data = [1,2,3,4,5,6,7,0]
s.InversePairs(data)
7
# 方法二:基于分治法--归并排序思路进行统计
# 时间复杂度:O(nlogn),但是在牛客网评测只通过50%,然后超时。在AcWing网站评测可以通过
# 牛客网使用Python 无法AC此题
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.count = 0
def InversePairs(self, data):
# write code here
if data is None or len(data) < 2:
return 0
self.mergeSort(data)
return self.count%1000000007
def mergeSort(self, data):
if len(data) <= 1:
return data
mid = len(data) // 2
# 分
left = self.mergeSort(data[:mid])
right = self.mergeSort(data[mid:])
# 治
return self.merge(left, right)
def merge(self, left, right):
i, j = 0, 0
res = []
while i < len(left) and j < len(right):
if left[i] > right[j]:
res.append(left[i])
i += 1
self.count += len(right[j:])
else:
res.append(right[j])
j += 1
if i < len(left):
res += left[i:]
if j < len(right):
res += right[j:]
return res
输入两个链表,找出它们的第一个公共结点。
方法:
存在公共节点的链表呈Y字形分布,倒叙遍历将分叉。
# 方法三:统计链表节点个数,让长链表先遍历若干步,然后同时开始遍历,第一个相等节点即为第一个公共节点
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
# write code here
if pHead1 is None or pHead2 is None:
return None
count1, count2 = 1, 1
# 统计链表节点个数
p = pHead1
while p.next != None:
count1 += 1
p = p.next
p = pHead2
while p.next != None:
count2 += 1
p = p.next
# 让较长的链表先遍历若干步
dis = count1 - count2
p1, p2 = pHead1, pHead2
if dis > 0:
rest_length = count2
for _ in range(dis):
p1 = p1.next
else:
rest_length = count1
dis = abs(dis)
for _ in range(dis):
p2 = p2.next
# 寻找第一个公共节点
for _ in range(rest_length):
if p1.val == p2.val:
return p1
else:
p1, p2 = p1.next, p2.next
return None
统计一个数字在排序数组中出现的次数。
# 方法一:遍历统计:O(n)
# -*- coding:utf-8 -*-
class Solution:
def GetNumberOfK(self, data, k):
# write code here
if data is None:
return 0
count = 0
for i in data:
if i == k:
count += 1
return count
# 方法二:二分查找思路,O(logn)
# -*- coding:utf-8 -*-
class Solution:
def GetNumberOfK(self, data, k):
# write code here
if data is None or len(data) < 1:
return 0
# 判断排序数组是升序还是降序
up_flag = False
if data[0] < data[-1]:
up_flag = True # 升序
# 二分法查找与K相等的值的index
left, right = 0, len(data)
pivot = (left + right) // 2
previous_pivot = pivot
while data[pivot] != k:
if data[pivot] > k:
if up_flag:
right = pivot
pivot = (left + right) // 2
else:
left = pivot
pivot = (left + right) // 2
else:
if up_flag:
left = pivot
pivot = (left + right) // 2
else:
right = pivot
pivot = (left + right) // 2
if previous_pivot == pivot:
# 如果两次的pivot相等,则说明数组里不存在k值,返回0
return 0
previous_pivot = pivot
# 找到k所在位置后,在k左右进行统计
count = 1
index = pivot - 1
while index > -1 and data[index] == k:
count += 1
index -= 1
index = pivot + 1
while index < len(data) and data[index] == k:
count += 1
index += 1
return count
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0到n-1之内。
在范围0到n-1的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
PS: 牛客网没有此题在线评测,可在AcWing第68题进行评测。
# 方法:二分法查找与下标不相等的第一个数
class Solution(object):
def getMissingNumber(self, data):
"""
:type nums: List[int]
:rtype: int
"""
if data is None or len(data) < 1:
return 0
# 特例情况
if len(data) >1 and data[0] != 0:
return 0
# 二分法查找与K相等的值的index
left, right = 0, len(data)
pivot = (left + right) // 2
previous_pivot = pivot
while True:
if data[pivot] > pivot:
right = pivot
pivot = (left + right) // 2
else:
left = pivot
pivot = (left + right) // 2
if previous_pivot == pivot:
# 如果两次的pivot相等,则下一个数即为不存在的数
return pivot+1
previous_pivot = pivot
假设一个单调递增的数组里的每个元素都是整数并且是唯一的。
请编程实现一个函数找出数组中任意一个数值等于其下标的元素。
例如,在数组[-3, -1, 1, 3, 5]中,数字3和它的下标相等。
PS: 牛客网没有此题在线评测,可在AcWing第69题进行评测。
class Solution(object):
def getNumberSameAsIndex(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if nums is None or len(nums) < 1:
return -1
left, right = 0, len(nums)
mid = (left + right) // 2
previous_mid = mid
while nums[mid] != mid:
if nums[mid] < mid:
left = mid
else:
right = mid
mid = (left + right) // 2
#print(mid)
if previous_mid == mid:
return -1
previous_mid = mid
return mid
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
# 方法:中序遍历得到节点数组,然后取节点数组第k个元素
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回对应节点TreeNode
def KthNode(self, pRoot, k):
# write code here
if pRoot is None or k < 1:
return None
node_list = self.recursion_LVR(pRoot)
if len(node_list) < k:
return None
else:
return node_list[k-1]
def recursion_LVR(self, pRoot):
# 中序遍历,返回节点数组
if pRoot is None:
return []
return self.recursion_LVR(pRoot.left) + [pRoot] + self.recursion_LVR(pRoot.right)
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
# 递归实现计算,选择较长的子节点所在路径长度作为深度
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def TreeDepth(self, pRoot):
# write code here
if pRoot is None:
return 0
left = self.TreeDepth(pRoot.left)
right = self.TreeDepth(pRoot.right)
return (left+1) if (left > right) else (right+1)
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
# 方法一:递归得到每个子节点的深度,判断是否满足平衡二叉树条件
# 该方法存在重复遍历过程,效率较低,不可取
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def IsBalanced_Solution(self, pRoot):
# write code here
if pRoot is None:
return True
left = self.tree_depth(pRoot.left)
right = self.tree_depth(pRoot.right)
if abs(left-right) > 1:
return False
return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
def tree_depth(self, pRoot):
# 递归遍历求子节点的深度
if pRoot is None:
return 0
left = self.tree_depth(pRoot.left)
right = self.tree_depth(pRoot.right)
return (left+1) if (left > right) else (right + 1)
# 方法二:递归遍历子节点的过程,判断当前子树是否满足平衡条件,不满足则将标志位置为False
# 与方法一相比,少了重复递归的过程
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
self.flag = True
def IsBalanced_Solution(self, pRoot):
# write code here
if pRoot is None:
self.depth = 0
return True
self.tree_depth(pRoot)
return self.flag
def tree_depth(self, pRoot):
if pRoot is None:
return 0
left = self.tree_depth(pRoot.left)
right = self.tree_depth(pRoot.right)
if abs(left - right) > 1:
self.flag = False
return (left+1) if (left > right) else (right + 1)
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
**方法:**根据异或的性质来解题。当一个数组只存在一个出现一次的数,将数组所有数进行异或,则结果为这个只出现一次的数;当数组存在两个只出现一次的数,将数组所有数进行异或,然后按照异或结果将数组可分为两部分(分类依据是:找出异或结果中第一个非零位,根据该位是否为零可以将数组分为两部分),将这两部分分别进行异或即可得到两个各出现一次的数字。
# -*- coding:utf-8 -*-
class Solution:
# 返回[a,b] 其中ab是出现一次的两个数字
def FindNumsAppearOnce(self, array):
# write code here
if array is None or len(array) < 2:
return []
bin_xor = array[0]
for i in array[1:]:
bin_xor = bin_xor ^ i
if bin_xor != 0:
first1 = self.getFirst1(bin_xor)
else:
return []
a, b = 0, 0
for i in array:
if self.getFirst1(i) == first1:
a ^= i
else:
b ^= i
return [a, b]
def getFirst1(self, num):
# 自右向左获取第一个非零位置
n = 0
while num & (1 << n) == 0:
n += 1
return n
在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次。
请找出那个只出现一次的数字。
你可以假设满足条件的数字一定存在。
思考题:
如果要求只使用 O(n) 的时间和额外 O(1) 的空间,该怎么做呢?
此题牛客网没有在线评测,可以在AcWing第74题进行评测。
方法:
# 基于方法一
class Solution(object):
def findNumberAppearingOnce(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if nums is None or len(nums) < 1:
raise('Invalid input')
digits_count = [0] * 16 # 定义数组,存储数字二进数数每一位1的总个数
# 累加数组中数字二进数每一位1的总个数
for i in nums:
n = 0
while i>>n != 0:
digits_count[n] += (i>>n) & 1
n += 1
# 根据每一位1的个数是否能被3整除得到只出现一次的数字对应的二进制数
flag_index = 0
for i in range(len(digits_count)):
if digits_count[i] == 0:
flag_index = i
break
else:
digits_count[i] %= 3
# 将只出现一次的数字从二进制数转化为整数
res = 0
for i, n in enumerate(digits_count[:flag_index]):
res += pow(2, i) * n
return res
s = Solution()
nums = [1,1,1,2,2,2,3,4,4,4]
s.findNumberAppearingOnce(nums)
3
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
方法:
# 双指针法
# -*- coding:utf-8 -*-
class Solution:
def FindNumbersWithSum(self, array, tsum):
# write code here
if array is None or len(array) < 2:
return []
head, end = 0, len(array) - 1
while head < end:
s = array[head] + array[end]
if s == tsum:
return [array[head], array[end]]
elif s > tsum:
end -= 1
else:
head += 1
return []
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
# 双指针法,同时从头开始向后递增,循环条件为head < (tsum+1) // 2
# -*- coding:utf-8 -*-
class Solution:
def FindContinuousSequence(self, tsum):
# write code here
if tsum < 1:
return []
res = []
head, end = 1, 2
while head < (tsum+1) // 2:
s = sum(range(head,end))
if s == tsum:
res.append(range(head, end))
end += 1
elif s < tsum:
end += 1
else:
head += 1
return res
输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。
为简单起见,标点符号和普通字母一样处理。
例如输入字符串"I am a student.",则输出"student. a am I"。
# 方法一:借助额外的空间记录句子中的单词,然后对单词进行翻转,空间复杂度O(n)
# -*- coding:utf-8 -*-
class Solution:
def ReverseSentence(self, s):
# write code here
if s is None or len(s) < 1:
return ""
start, end = 0, 0
assist_list = []
for i,c in enumerate(s):
if c is " ":
assist_list.append(s[start:i])
start = i+1
if i == len(s)-1:
assist_list.append(s[start:])
assist_list.reverse()
return " ".join(assist_list)
# 方法二:两次翻转法,不需要额外的空间消耗
# -*- coding:utf-8 -*-
class Solution:
def ReverseSentence(self, s):
# write code here
if s is None or len(s) < 1:
return ""
s = list(s)
# 翻转句子
s = self.reverse(s)
res = []
# 翻转单词
start, end = 0, 0
for i, c in enumerate(s):
if c == " ":
temp = self.reverse(s[start:i])
res.append(''.join(temp))
start = i + 1
if i == len(s) - 1:
temp = self.reverse(s[start:])
res.append(''.join(temp))
return " ".join(res)
def reverse(self, s):
# 对整个句子进行翻转
head, end = 0, len(s)-1
while head < end:
s[head], s[end] = s[end], s[head]
head += 1
end -= 1
return s
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
# 方法一:利用python字符串特性直接操作
# -*- coding:utf-8 -*-
class Solution:
def LeftRotateString(self, s, n):
# write code here
if s is None or len(s) < 1 or n < 0:
return ""
length = len(s)
n = n % length
return s[n:] + s[:n]
# 方法二:从移位点将字符串分割为左右两部分,分别进行翻转,总共进行三次翻转实现左旋
# -*- coding:utf-8 -*-
class Solution:
def LeftRotateString(self, s, n):
# write code here
if s is None or len(s) < 1 or n < 0:
return ""
s = list(s)
length = len(s)
n = n % length
s = self.reverse(s)
left = self.reverse(s[:length-n])
right = self.reverse(s[length-n:])
return "".join(left+right)
def reverse(self, s):
# 对整个句子进行翻转
head, end = 0, len(s)-1
while head < end:
s[head], s[end] = s[end], s[head]
head += 1
end -= 1
return s
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
# 方法:利用双端队列存储窗口的最大值在数组中的位置(下标)
# -*- coding:utf-8 -*-
class Solution:
def maxInWindows(self, num, size):
# write code here
if num is None or len(num) < 1 or size == 0:
return []
max_deque = []
max_val = []
for i, n in enumerate(num):
# 弹出超出窗口的队首
if len(max_deque) > 0 and i >= (max_deque[0]+size):
max_deque.pop(0)
if len(max_deque) > 0 and i < (max_deque[0]+size) and n >= num[max_deque[0]]:
# 更新队首为最大值
max_deque = []
max_deque.append(i)
elif len(max_deque) > 0 and num[max_deque[-1]] <= n:
# 更新队尾为较大值
while len(max_deque) > 0 and num[max_deque[-1]] <= n:
max_deque.pop(-1)
max_deque.append(i)
else:
max_deque.append(i)
# 取出各个窗口的最大值
if i - size >= -1:
max_val.append(num[max_deque[0]])
return max_val
定义一个队列并实现函数 max 得到队列里的最大值,要求函数 max、push_back 和 pop_front 的时间复杂度都是O(1)。
**PS:**此题没有在网上找到对应的在线评测平台。
方法: 利用双端队列存储队列最大值,实现 O ( 1 ) O(1) O(1)获取队列最大值
class QueueWithMax:
def __init__(self):
self.max_q = []
self.queue_with_max = []
def push_back(self, n):
self.queue_with_max.append(n)
index = len(self.queue_with_max) - 1 # 当前插入值对应的index
if len(self.max_q) > 0 and n >= self.queue_with_max[self.max_q[0]]:
self.max_q = []
self.max_q.append(index)
elif len(self.max_q) > 0 and self.queue_with_max[self.max_q[-1]] <= n:
while len(self.max_q) > 0 and self.queue_with_max[self.max_q[-1]] <= n:
self.max_q.pop(-1)
self.max_q.append(index)
else:
self.max_q.append(index)
def pop_front(self):
if len(self.queue_with_max) > 0:
res = self.queue_with_max.pop(0)
# 弹出最大值队列队首元素
if self.max_q[0]-1 < 0:
self.max_q.pop(0)
# 将最大值队列所存储的索引前移一位
for i in range(len(self.max_q)):
self.max_q[i] -= 1
return res
else:
return None
def max(self):
if len(self.max_q) < 1:
return None
else:
return self.queue_with_max[self.max_q[0]]
# Test
s = QueueWithMax()
s.push_back(1)
print(s.max())
s.push_back(5)
print(s.max())
s.push_back(3)
print(s.max())
s.push_back(7)
print(s.max())
s.push_back(2)
print(s.max())
print(s.pop_front())
print(s.max())
print(s.pop_front())
print(s.max())
print(s.pop_front())
print(s.max())
print(s.pop_front())
print(s.max())
s.push_back(5)
print(s.max())
1
5
5
7
7
1
7
5
7
3
7
7
2
5
将一个骰子投掷n次,获得的总点数为s,s的可能范围为n~6n。
掷出某一点数,可能有多种掷法,例如投掷2次,掷出3点,共有[1,2],[2,1]两种掷法。
请求出投掷n次,掷出n~6n点分别有多少种掷法。
PS: 此题牛客网没有对应的在线评测,可以在AcWing第80题进行评测。
# 方法一:递归法,递归公式f(n) += f(n-1),使用辅助数组存储和值出现的次数,数组的索引即为和值
# 这种方法时间效率较低,由于递归存在很多重复计算
class Solution(object):
def numberOfDice(self, n):
"""
:type n: int
:rtype: List[int]
"""
if n is None or n < 1:
return []
return self.recursion_count(n)
def recursion_count(self, n):
# 递归计算不同和值出线的次数
if n == 1:
res = [0] * (6*n - n + 1)
for i in range(1,7): res[i-1] = 1
return res
res = [0] * (6*n - n + 1) # 统计n时,和值出现的次数
#print(6*n - n + 1)
res_pre = self.recursion_count(n-1)
#print(res_pre)
for i in range(1,7):
for j in range(n-1, 6*(n-1)+1): # n-1时和值的范围为[n-1, 6*(n-1)]
#print(i+j - n)
res[i+j - n] += res_pre[j-(n-1)] # n-1时和值出现的次数为res_pre[j-(n-1)]
return res
# 如果求每个和值的概率,则返回如下
# sum_count = sum(res)
# return [x/sum_count for x in res]
# 方法二:将方法一中的递归使用循环实现,减少重复计算,时间效率O(n^2)
class Solution(object):
def numberOfDice(self, n):
"""
:type n: int
:rtype: List[int]
"""
if n is None or n < 1:
return []
if n == 1:
res = [0] * (6*n - n + 1)
for i in range(1,7): res[i-1] = 1
return res
res_pre = [1] * (6*1 - 1 + 1) # 只有一个骰子的情况记录
for num in range(2, n+1):
res = [0] * (6*num - num + 1)
for i in range(1,7):
for j in range(num-1, 6*(num-1)+1): # num-1时和值的范围为[num-1, 6*(num-1)]
res[i+j - num] += res_pre[j-(num-1)] # num-1时和值出现的次数为res_pre[j-(num-1)]
res_pre = res
return res
# 如果求每个和值的概率,则返回如下
# sum_count = sum(res)
# return [x/sum_count for x in res]
# 方法三:动态规划法
# 状态转移公式,f(n,k) = f(n-1, k-1) + f(n-1, k-2) + ... + f(n-1, k-6), n<= k <= 6n
# 初始状态: f(1, 1)= f(1, 2) = ... = f(1, 5) = f(1, 6) = 1
# 其中f(n , k)表示n个骰子点数和为k时出现的次数
# 使用1维数组存储有效的和值次数,比二维数组空间使用率稍高
# 时间复杂度: O(n^2)
class Solution(object):
def numberOfDice(self, n):
"""
:type n: int
:rtype: List[int]
"""
if n is None or n < 1:
return []
f1 = [1] * 6
if n == 1:
return f1
# 动态规划
fn_1 = f1 # n-1时有效和值的次数统计
for i in range(2, n+1):
fn = [0] * (6*i - i + 1) # 和值有效范围[n, 6*n]-->线性映射至(长度为(6*n - n + 1)的一维数组
for k in range(len(fn)):
#fn[k] =
k_sum = k + i # 从数组索引反映射回对应的有效和值
# 此处求f(n,k), f(n,k) = f(n-1, k-1) + f(n-1, k-2) + ... + f(n-1, k-6),
for j in range(1, 7):
if 6*(i-1) >= k_sum-j >= i-1:
fn[k] += fn_1[k_sum- j - (i-1)]
fn_1 = fn
return fn
从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。
2~10为数字本身,A为1,J为11,Q为12,K为13,大小王可以看做任意数字。
为了方便,大小王均以0来表示,并且假设这副牌中大小王均有两张。
# 根据数字间的差值和王的数量关系判断是否成为顺子,同时如果存在对子则无法成为顺子
# -*- coding:utf-8 -*-
class Solution:
def IsContinuous(self, numbers):
# write code here
if numbers is None or len(numbers) != 5:
return False
numbers.sort()
zero_num = 0 # 记录零的个数
dif = 0 # 统计数字差值
for i,j in zip(numbers[:-1], numbers[1:]):
if i == 0:
zero_num += 1
elif i == j: # 如果存在对子,则无法成为顺子
return False
else:
dif += j - i - 1 # 统计数字之间比1大的差值
if dif <= zero_num: # 王的数量是否大于等于数字之间的差值
return True
else:
return False
0, 1, …, n-1这n个数字(n>0)排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。
求出这个圆圈里剩下的最后一个数字。
约瑟夫环问题。
时间复杂度 O ( n m ) , 空 间 复 杂 度 O(nm),空间复杂度 O(nm),空间复杂度O(n),这种方法在牛客网超时无法通过,在Acwing第82题可以通过。
# -*- coding:utf-8 -*-
class Solution:
def LastRemaining_Solution(self, n, m):
# write code here
if n is None or n <= 1 or m < 1:
return 0
num = [x for x in range(n)]
index = 0
for _ in range(n-1):
count = 0
while count < m:
if num[index] != -1:
count += 1
index = (index + 1) % n
else:
index = (index + 1) % n
num[index-1] = -1
for i in num:
if i != -1:
return i
s = Solution()
s.LastRemaining_Solution(5, 3)
3
定义关于 n , m n,m n,m的函数 f ( n , m ) f(n,m) f(n,m),表示每次在n个数字 0 , 1 , 2 , ⋯   , n − 1 0,1,2,\cdots, n-1 0,1,2,⋯,n−1中删除第m个数字后剩下的数字。第一个被删除的是 k = ( m − 1 ) k = (m-1)%n k=(m−1),剩余数字为 0 , 1 , ⋯   , k − 1 , k + 1 , ⋯   , n − 1 0,1,\cdots, k-1, k+1,\cdots, n-1 0,1,⋯,k−1,k+1,⋯,n−1,则新的序列为$k+1,\cdots, n-1, 0,1,\cdots, k-1 , 可 记 为 ,可记为 ,可记为f(n-1, m)$,将新的序列可映射至 0 n − 2 0~n-2 0 n−2范围,映射函数为 p ( x ) = ( x − k − 1 ) % n p(x)=(x-k-1)\%n p(x)=(x−k−1)%n,逆映射为 p − 1 ( x ) = ( x + k + 1 ) % n p^{-1}(x)=(x+k+1)\%n p−1(x)=(x+k+1)%n。最终可得递推关系:
f ( n , m ) = { 0 n = 1 [ f ( n − 1 , m ) + m ] % n n > 1 f(n,m) = \left\{ \begin{matrix} 0 & n=1 \\ [f(n-1,m)+m]\%n & n > 1 \end{matrix}\right. f(n,m)={0[f(n−1,m)+m]%nn=1n>1
# 根据方法二的递推公式使用循环法从1开始递推
# -*- coding:utf-8 -*-
class Solution:
def LastRemaining_Solution(self, n, m):
# write code here
if n is None or n < 1 or m < 1:
return -1
last = 0
for i in range(2, n+1):
last = (last + m)%i
return last
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖 一次 该股票可能获得的利润是多少?
例如一只股票在某些时间节点的价格为[9, 11, 8, 5, 7, 12, 16, 14]。
如果我们能在价格为5的时候买入并在价格为16时卖出,则能收获最大的利润11。
**PS:**此题牛客网没有在线评测,可在AcWing第83题进行评测。
class Solution(object):
def maxDiff(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if nums is None or len(nums) <= 1:
return 0
min_in = nums[0]
profit = 0
for i in nums[1:]:
if i < min_in:
min_in = i
elif (i - min_in) > profit:
profit = i - min_in
return profit
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
# 使用递归实现累加
# -*- coding:utf-8 -*-
class Solution:
def Sum_Solution(self, n):
# write code here
res = n
flag = (n > 0) and self.Sum_Solution(n - 1)
res += flag
return res
# 另一种递归
# -*- coding:utf-8 -*-
class Solution:
def Sum_Solution(self, n):
# write code here
return n and self.Sum_Solution(n - 1) + n
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
# 方法:使用位运算实现
# -*- coding:utf-8 -*-
class Solution:
def Add(self, num1, num2):
# write code here
if num1 is None or num2 is None:
return -1
while num2 != 0:
sums = num1 ^ num2
num2 = (num1&num2)<<1 # 进位
num1 = sums & 0xFFFFFFFF # 考虑负数
return num1 if num1 >> 31 == 0 else num1 - 4294967296
给定一个数组 A [ 0 , 1 , . . . , n − 1 ] A[0,1,...,n-1] A[0,1,...,n−1],请构建一个数组 B [ 0 , 1 , . . . , n − 1 ] B[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 ] B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1] B[i]=A[0]∗A[1]∗...∗A[i−1]∗A[i+1]∗...∗A[n−1]。不能使用除法。
# 方法一暴力法:O(n^2)
# 方法二使用A构造矩阵来构造B
# -*- coding:utf-8 -*-
class Solution:
def multiply(self, A):
# write code here
if A is None or len(A) <= 1:
return []
length = len(A)
BA = [[1 if i == j else A[j] for j in range(length)] for i in range(length)] # 构建矩阵,对角线元素为1
B = [1] * length
for i, li in enumerate(BA):
mul = 1
for j in li:
mul *= j
B[i] = mul
return B
将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
# -*- coding:utf-8 -*-
class Solution:
def StrToInt(self, s):
# write code here
if s is None or len(s) < 1:
return 0
if len(s) == 1 and (s[0] is "+" or s[0] is "-"):
return 0
flag = 1
head = 0
valid_list = "1234567890"
if s[0] is "-":
flag = -1
elif s[0] != "-" and s[0] != "+":
if s[0] in valid_list:
head = int(s[0])
else:
return 0
length,count = len(s), len(s)
res = 0
while count > 1:
if s[count-1] in valid_list:
res += int(s[count-1])*pow(10, length-count)
count -= 1
else:
return 0
if head == 0:
return flag*res
else:
return head*pow(10, length-1)+res
在AcWing第87题对字符串转整数提出更高的要求。
你的函数应满足下列条件:
class Solution(object):
def strToInt(self, str):
"""
:type str: str
:rtype: int
"""
s = str
if s is None or len(s) < 1:
return 0
if len(s) == 1 and (s[0] is "+" or s[0] is "-"):
return 0
dig_flag = 1
head = 0
valid_list = "1234567890"
# 剔除字符串首的空格,尾的不合法元素
count = 0
while s[0] == " ": #!= "+" and s[0] != "-" and s[0] not in valid_list and :
count += 1
s = s[1:]
if len(s) < 1:
return 0
count = len(s)
while s[-1] not in valid_list:
count -= 1
s = s[:-1]
if len(s) < 1:
return 0
# 处理字符串第一个元素
if s[0] is "-":
dig_flag = -1
elif s[0] != "-" and s[0] != "+":
if s[0] in valid_list:
head = int(s[0])
else:
return 0
# 处理其余元素
length,count = len(s), len(s)
res = 0
while count > 1:
if s[count-1] in valid_list:
res += int(s[count-1])*pow(10, length-count)
count -= 1
else:
return 0
INT_MAX = pow(2,31)-1
INT_MIN = -pow(2,31)
# 合并得到最终值
if head == 0:
res = dig_flag*res
else:
res = head*pow(10, length-1)+res
# 判断值的范围
if INT_MIN <= res <= INT_MAX:
return res
elif res > INT_MAX:
return INT_MAX
else:
return INT_MIN
给出一个二叉树,输入两个树节点,求它们的最低公共祖先。
一个树节点的祖先节点包括它本身。
注意:
**PS:**此题牛客网没有在线评测,可在AcWing第88题进行评测。
方法:
# 递归遍历
# 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 root is None:
return None
if root == p or root == q:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left != None and right != None:
return root
if left is None:
return right
elif right is None:
return left