推荐Ctrl + F搜索题目
二维数组的查找
行列排除法
- 从右上角or左下角开始查找,以左下角开始查找为例,若当前值<目标值,则目标值一定在当前值所在列的右侧,因此列+1;反之将行-1。相当于循环一次就可以排除一列or一行。
- 对于m行n列矩阵,暴力查找时间复杂度O(mn),以下方法O(m+n)。
class Solution:
# array 二维列表
def Find(self, target, array):
# write code here
row = len(array)-1
col = 0
while row>=0 and col<len(array[0]):
if target == array[row][col]:
return True
elif target > array[row][col]:
col+=1
else:
row-=1
return False
替换空格
- 先遍历确定’ '个数,然后从后向前插入(相对于从前向后插入,此方法下每个字符只用被修改一次),对于第count个空格和第count+1空格之间的字符,需要向后移位2*count。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|
h | e | l | l | o | w | ||||||
h | % | 2 | 0 | e | l | l | % | 2 | 0 | o | w |
# -*- coding:utf-8 -*-
class Solution:
# s 源字符串
def replaceSpace(self, s):
# write code here
l = list(s)
count = 0
for k in l:
if k == ' ':
count+=1
l.extend(['1','1'])
for i in range(len(s)-1,-1,-1):
if l[i]!=' ':
l[i+2*count]=l[i]
else:
count-=1
l[i+2*count]='%'
l[i+2*count+1]='2'
l[i+2*count+2]='0'
return ''.join(l)
从尾到头打印链表
根据题意,使用递归完成。
class Solution:
# 返回从尾部到头部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
# write code here
return self.printListFromTailToHead(listNode.next) + [listNode.val] if listNode else []
重建二叉树
-
前序遍历: 根节点 | 左子树 | 右子树: [1 | 2 4 5 | 3 6 7], 中序遍历: 左子树 | 根节点 | 右子树: [4 2 5 | 1 | 6 3 7]
-
因此,可用中序遍历中根节点位置将前序遍历化为三段。对左子树和右子树分别可以进行相同的划分,例如 左子树前序遍历[2 | 4 | 5]和中序遍历[4 | 2 | 5],递归即可生成树。
-
以下代码中,pre.pop(0)代表此递归对应树的前序遍历,中序遍历list用于辅助确定当前节点的值。
class Solution:
def reConstructBinaryTree(self, pre, tin):
if not tin: return
root = TreeNode(pre.pop(0))
for i in range(len(tin)):
if root.val == tin[i]: break
root.left = self.reConstructBinaryTree(pre, tin[:i])
root.right = self.reConstructBinaryTree(pre, tin[i+1:])
return root
用两个栈实现队列
-
根据题意,需要用两个栈来模拟队列的push和pop,其他例如获取队列内容等不需要考虑。
-
因此,push则向que最后添加node,pop则是将asi中的元素pop,其中asi可理解为que的reverse list。值得注意的是,当asi和que同时为空时应直接返回None。
class Solution:
def __init__(self):
self.que, self.asi = [], []
def push(self, node):
# write code here
self.que.append(node)
def pop(self):
# return xx
if not self.asi:
if not self.que: return
while self.que:
self.asi.append(self.que.pop())
return self.asi.pop()
旋转数组的最小数字
二分法+按顺序搜索
非减排序数组旋转后,数组等价于两个非减数组的拼合,当指针指向mid处时判断left和right大小关系,若left<mid,则指针肯定在第一个递增数组,此时将left=mid继续循环;否则right=mid;当right-left==1时,right即为第二个数组的第一个值,即整个数组的最小值。
值得注意的是,当left=mid=right时,无法判断mid此时在第一个递增数组还是第二个递增数组,如:
left | mid | right | |||||||
---|---|---|---|---|---|---|---|---|---|
原始 | 2 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
第一种旋转 | 3 | 2 | 3 | 3 | 3 | 3 | 3 | 3 | 3 |
第二种旋转 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 2 | 3 |
因此,当出现left=mid=right时,只能按顺序遍历[left,right]来找到最小数字。
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if len(rotateArray) == 0:
return 0
left = 0
right = len(rotateArray)-1
while True:
if right-left == 1:
return rotateArray[right]
mid = int((left+right)/2)
if rotateArray[mid] == rotateArray[right] and rotateArray[mid] == rotateArray[left]:
return minByOrder(rotateArray(left, right))
elif rotateArray[mid] >= rotateArray[left]:
left = mid
else:
right = mid
def minByOrder(rotateArray):
for i in range(len(rotateArray)-1, 0, -1):
if rotateArray[i-1] > rotateArray[i]:
return rotateArray[i]
return rotateArray[0]
斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39
# -*- coding:utf-8 -*-
class Solution:
def Fibonacci(self, n):
# write code here
if n == 0 or n == 1:
return n
a=0
b=1
for _ in range(n-1):
a,b = b,a+b
return b
跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
方法1:直接解法
n级台阶,可以分为n2个2阶和number-2*n2个1阶,需计算所有2阶台阶数量下的跳法总和。n2个2阶下,跳法情况是组合C c n2,即 c(c-1)…(c-n2+1)/n2!,此方法使用乘除法,计算效率较低,且复杂度O(n2)。
# -*- coding:utf-8 -*-
class Solution:
def jumpFloor(self, number):
# write code here
total_count = 0
for n2 in range(int(number/2)+1): #2的数量是0~int(number/2)+1
c = number - n2 #此情况下数字总数
numer = 1
deno = 1
for i in range(c-n2+1,c+1): #计算此2数量下的count
numer*=i
for i in range(1,n2+1):
deno*=i
total_count+=numer/deno
return total_count
方法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 number
a=0
b=1
for _ in range(number):
a,b = b,a+b
return b
变态跳台阶
看起来很变态,其实和跳台阶思路差不多,先找f(n)和f(n-1)…f(1)的关系,再由前面的项相加即可,对于n级台阶:
先跳 | 后面可能性 |
---|---|
1 | f(n-1) |
2 | f(n-2) |
… | … |
n-1 | 1 |
n | 1 |
如果用迭代做,重复计算太多了,因此考虑继续分解:
f(x) | = |
---|---|
f(0) | = 1 |
f(1) | = 1 |
f(2) | = f(0) + f(1) |
f(3) | = f(0) + f(1) + f(2) = 2[f(0)+f(1)] |
f(4) | = f(0) + f(1) + f(2) + f(3) = 4[f(0)+f(1)] |
… | = … |
f(n) | = 2f(n-1) = 2^n-2 * [f(0)+f(1)] |
因此, f(n) = 2^n-1.
# -*- coding:utf-8 -*-
class Solution:
def jumpFloorII(self, number):
# write code here
return pow(2,number-1)
矩形覆盖
其实是斐波那契数列
设n个矩形有f(n)种放法,则将一个矩形如下放置,剩下矩形有f(n-1)种放法。
1 | 2 | … | n |
---|---|---|---|
√ | |||
√ |
则将一个矩形如下放置,剩下矩形有f(n-2)种放法,因为还有一个矩形必须放在×的位置,即有两个矩形固定。
1 | 2 | … | n |
---|---|---|---|
√ | √ | ||
× | × |
因此,f(n)=f(n-1)+f(n-2),是斐波那契数列。
# -*- coding:utf-8 -*-
class Solution:
def rectCover(self, number):
# write code here
if number == 0 or number == 1:
return number
a=0
b=1
for _ in range(number):
a,b = b,a+b
return b
二进制数中1的个数
方法1:
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1(self, n):
# write code here
n &= 0xffffffff
count = 0
while n:
count += 1
n = n & (n-1)
return count
方法2:
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1(self, n):
# write code here
n &= 0xffffffff
count = 0
while n:
if n & 1 == 1:
count+=1
n>>=1
return count
以下理解不知正确与否:
-
python存取一个数,长度是无限制的(和java等不同,int是32位的)。当存储负数时,python会符号位前也有无限个1。例如,java中-1是以补码0xffffffff,但在python中0xffffffff前还有fffff……,因此对于负数无法直接求1的个数,会陷入死循环。
-
对于负数,需要做预先处理,即n &= 0xffffffff,这个即是n与0xffffffff求交,输出负数n的无符号形式(负号前面的1全部变为0,因此变成了正数,负号的1变为此正数的最高位)
print(hex(1)) #0x1
print(hex(-1)) #-0x1 = 0xffffffff(补码,二进制第一位是符号位,前面还有无限个1)
print(hex(1& 0xffffffff)) #0x1 = 0x00000001
print(hex(-1& 0xffffffff)) #0xffffffff,相当于把前面无限个1变为0,从符号位至最后一位保持不变,但符号位的1被提出到与其他位等价。
print(-1& 0xffffffff) #4294967295 = 2^32-1 = -1的无符号形式
数值的整数次方
快速幂法:
- 原始情况下, b ^ e = b * b * … * b,可以分解成e个b的乘积,需要做e-1次乘法运算,效率很低。
- 然而我们发现 b ^ e = b ^ (1 + 2 + 4 + … + 2n),所有的e都可以分解成此数列,其本质上是一个数的二进制表示,如3 = 0011 = 1 + 2, 5 = 0101 = 1 + 4, 9 = 1001 = 1 + 8…
- 这样我们存储 b ^ 1, b ^ 2 , b ^ 4 … (即 base = base * base),通过 exp & 1 == 0 (判断exp最右位是否为1)来判断数字此位是否需要相乘,最终把相乘结果输出即可。
# -*- coding:utf-8 -*-
class Solution:
def Power(self, base, exponent):
# write code here
res, exp = 1, abs(exponent)
while exp != 0:
if exp & 1 != 0: res *= base
base *= base
exp >>= 1
return res if exponent > 0 else 1/res
调整数组顺序使奇数位于偶数前面
空间换时间,借用两个辅助数组分别填入奇数偶数并返回,时间空间复杂度均为O(N)。
# -*- coding:utf-8 -*-
class Solution:
def reOrderArray(self, array):
# write code here
odd, even = [], []
for a in array:
odd.append(a) if a & 1 else even.append(a)
return odd + even
链表中倒数第k个结点
声明两个指针n1, n2,指针n1先向前走k1步,找到链表第k+1个node,然后指针n1,n2一起走,当n2走过最后一个node时(指向None),n1即是倒数第k个node
class Solution:
def FindKthToTail(self, head, k):
# write code here
if not head or k < 1: return
node1, node2 = head, head
for _ in range(k):
if not node1: return
node1 = node1.next
while node1:
node1, node2 = node1.next, node2.next
return node2
反转链表
遍历链表,每次记录上次遍历点pre,三个指针交替向前行进。
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# write code here
pre = None
while pHead:
cur, pHead.next = pHead.next, pre
pre, pHead = pHead, cur
return pre
合并两个排序链表
借用一个链表头tmp,按照大小依次将head1,head2链表加入排序,最后将剩余部分加到链表尾部,返回tmp.next
# -*- 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
tmp = ListNode(0)
head = tmp
while pHead1 and pHead2:
if pHead1.val < pHead2.val:
tmp.next, pHead1 = pHead1, pHead1.next
else:
tmp.next, pHead2 = pHead2, pHead2.next
tmp = tmp.next
tmp.next = pHead2 if not pHead1 else pHead1
return head.next
树的子结构
- 先找到子树根节点value = 父树根节点value的节点;
- 判断以此节点为根节点时,是否是子结构(r2为空则代表是子结构);
- 遍历pRoot1,方可确定是否为子结