- 归档:剑指offer
牛客网剑指Offer_编程题python实现
导语
2019/4/9,目前一共66道,我也是小白,定个目标半个月完成,让我们一起开始吧!
1 Fibonacci数列及其应用(考察:递归与循环)
斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39
-
思路:
就是用python写这个常用数列啦! -
解法:
class Solution:
def Fibonacci(self, n):
# write code here
F = [0,1,1]
if n < 3:
return F[n]
for i in range(3,n+1):
num = F[i-1]+F[i-2]
F.append(num)
return F[n]
跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
-
思路:
斐波那契数列的应用。对于本题,前提只有 一次 1阶或者2阶的跳法。
a.如果两种跳法,1阶或者2阶,那么假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1);
b.假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2)
c.由a\b假设可以得出总跳法为: f(n) = f(n-1) + f(n-2)
d.然后通过实际的情况可以得出:只有一阶的时候 f(1) = 1 ,只有两阶的时候可以有 f(2) = 2
e.可以发现最终得出的是一个斐波那契数列:
f ( n ) = { 1 , n = 1 2 , n = 2 f ( n − 1 ) + f ( n − 2 ) , n > 2 f(n)= \left\{\begin{matrix} 1,n=1\\ 2,n=2\\ f(n-1)+f(n-2) ,n>2 \end{matrix}\right. f(n)=⎩⎨⎧1,n=12,n=2f(n−1)+f(n−2),n>2 -
解法:
# -*- coding:utf-8 -*-
class Solution:
def jumpFloor(self, n):
# write code here
res=[1,2,3] #台阶从1开始,到n
while len(res)<= (n-1): #列表计数从0开始,则n-1结束
res.append(res[-1]+res[-2])
return res[n-1] #n-1对应实际的最后一个台阶
变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
-
思路:
在上一题上的变种,但不是斐波那契数列的应用,更像是找规律。(链接:https://www.nowcoder.com/questionTerminal/22243d016f6b47f2a6928b4313c85387)
关于本题,前提是n个台阶会有一次n阶的跳法。分析如下:
f(1) = 1
f(2) = f(2-1) + f(2-2) //f(2-2) 表示2阶一次跳2阶的次数。
f(3) = f(3-1) + f(3-2) + f(3-3)
…
f(n) = f(n-1) + f(n-2) + f(n-3) + … + f(n-(n-1)) + f(n-n)
说明:
1)这里的f(n) 代表的是n个台阶有一次1,2,…n阶的 跳法数。
2)n = 1时,只有1种跳法,f(1) = 1
3 ) n = 2时,会有两个跳得方式,一次1阶或者2阶,这回归到了问题(1) ,f(2) = f(2-1) + f(2-2)
4 ) n = 3时,会有三种跳得方式,1阶、2阶、3阶,那么就是第一次跳出1阶后面剩下:f(3-1);第一次跳出2阶,剩下f(3-2);第一次3阶,那么剩下f(3-3)。因此结论是f(3) = f(3-1)+f(3-2)+f(3-3)
5 ) n = n时,会有n中跳的方式,1阶、2阶…n阶,得出结论:
f(n) = f(n-1)+f(n-2)+…+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + … + f(n-1)
6 ) 由以上已经是一种结论,但是为了简单,我们可以继续简化:
f(n-1) = f(0) + f(1)+f(2)+f(3) + … + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + … + f(n-2)
f(n) = f(0) + f(1) + f(2) + f(3) + … + f(n-2) + f(n-1) = f(n-1) + f(n-1)
可以得出:
f(n) = 2*f(n-1)
7 ) 得出最终结论,在n阶台阶,一次有1、2、…n阶的跳的方式时,总得跳法为:
f ( n ) = { 1 , n = 0 1 , n = 1 2 ∗ f ( n − 1 ) , n > = 2 f(n)= \left\{\begin{matrix} 1,n=0\\ 1,n=1\\ 2*f(n-1) ,n>=2 \end{matrix}\right. f(n)=⎩⎨⎧1,n=01,n=12∗f(n−1),n>=2 -
解法:
# -*- coding:utf-8 -*-
class Solution:
def jumpFloorII(self,number):
# write code here
if(number < 3):
return number
else:
return 2**(number - 1)
矩形覆盖
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
- 思路:
在分析前不知道是什么序列,所以先看了n=1,n=2,n=3,n=4的情况摸索规律,主要是看 n 和 n-1 的隐含联系。(2*1 指 长宽)图片说明:
- 解法:
# -*- coding:utf-8 -*-
class Solution:
def rectCover(self, number):
# write code here
f=[0,1,2]
for i in range(3,number+1):
num=f[i-1] + f[i-2]
f.append(num)
return f[number]
2 数组
二维数组中的查找
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
-
思路:
-
解法:
在这里插入代码片
数组中重复的数字
在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
-
思路:
-
解法:
在这里插入代码片
构建乘积数组
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。
- 思路:
B[0] = A[1] * A[2] * A[3] * A[4] … A[n-1] ;(没有A[0])
B[1 ]= A[0] * A[2] * A[3] * A[4] *…*A[n-1] ;(没有A[1])
B[2] = A[0] * A[1] * A[3] * A[4] *…*A[n-1] ;(没有A[2])
…
即B[i]项等于A数组所有数的乘积,但是去除A[i]项。1
B[i]的值可以看作下图的矩阵中每行的乘积。下三角用连乘可以很容求得,上三角,从下向上也是连乘。因此我们的思路就很清晰了,先算下三角中的连乘,即我们先算出B[i]中的一部分,然后倒过来按上三角中的分布规律,把另一部分也乘进去。
- 解法:
# -*- coding:utf-8 -*-
class Solution:
def multiply(self,A):
# write code here
size_A=len(A)
b=[1]
if size_A == 0 or size_A ==1:
return A
for i in range(1,size_A):
num = A[i-1] * b[i-1]
b.append(num)
temp = 1
for j in range(size_A-1,0,-1):
b[j] = temp*b[j]
temp = temp*A[j]
b[0] = temp*b[0]
return b
3 字符串
正则表达式匹配
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配。
-
思路:
来源:https://www.nowcoder.com/questionTerminal/45327ae22b7b413ea21df13ee7d6429c
解这题需要把题意仔细研究清楚,反正我试了好多次才明白的。
首先,考虑特殊情况:
1>两个字符串都为空,返回true
2>当第一个字符串不空,而第二个字符串空了,返回false(因为这样,就无法匹配成功了,而如果第一个字符串空了,第二个字符串非空,还是可能匹配成功的,比如第二个字符串是“aaaa”,由于 “ * ” 之前的元素可以出现0次,所以有可能匹配成功)之后就开始匹配第一个字符,这里有两种可能:匹配成功或匹配失败。但考虑到pattern下一个字符可能是 “ * ”, 这里我们分两种情况讨论:pattern下一个字符为“ * ” 或不为“ * ” :
1>pattern下一个字符不为“ * ” :这种情况比较简单,直接匹配当前字符。如果匹配成功,继续匹配下一个;如果匹配失败,直接返回false。注意这里的 “匹配成功”,除了两个字符相同的情况外,还有一种情况,就是pattern的当前字符为‘‘ . ’,同时str的当前字符不为 ‘\0’。
2>pattern下一个字符为“ * ” 时,稍微复杂一些,因为“ * ” 可以代表0个或多个。这里把这些情况都考虑到:
a>当“ * ” 匹配0个字符时,str当前字符不变,pattern当前字符后移两位,跳过这个“ * ” 符号;
b>当“ * ” 匹配1个或多个时,str当前字符移向下一个,pattern当前字符不变。(这里匹配1个或多个可以看成一种情况,因为:当匹配一个时,由于str移到了下一个字符,而pattern字符不变,就回到了上边的情况a;当匹配多于一个字符时,相当于从str的下一个字符继续开始匹配)之后再写代码就很简单了。 -
解法:
# -*- coding:utf-8 -*-
class Solution:
# s, pattern都是字符串
def match( self,s, pattern):
if len(s)==0 and len(pattern)==0:
return True
if len(s)>0 and len(pattern)==0:
return False
if len(pattern)==1 and len(s)==0 and ( pattern[0]!='.'):
return False
if len(pattern)>1:
if pattern[1]!='*':
if len(s)>0 and (s[0]==pattern[0] or pattern[0]=='.'):
return self.match(s[1:],pattern[1:])
else:
return False
if pattern[1]=='*':
print(len(s))
if len(s)>0 and (s[0]==pattern[0] or pattern[0]=='.'):
return self.match(s[1:],pattern) or self.match(s[1:],pattern[2:]) or self.match(s,pattern[2:])
else:
return self.match(s,pattern[2:])
if (pattern[0]=='.' or s[0]==pattern[0]) and len(s)>0:
return self.match(s[1:],pattern[1:])
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):
if len(s)==0:
return False
new_s=''
count=0
s_dict='0123456789'
if s[0]=='+' or s[0]=='-':
s=s[1:]
i=0
ret=True
while i <len(s) and ret:
if s[i] in s_dict:
new_s +=s[i]
elif s[i]=='.':
count +=1
elif s[i]=='+' or i=='-':
ret= False
elif s[i]=='e' or s[i]=='E':
if i==len(s)-1:
ret=False
break
if (s[i+1] in s_dict) or (s[i+1]=='+')or s[i+1]=='-':
ret= True
if s[i+1]=='+' or s[i+1]=='-':
i += 1
if '.' in s[i:]:
ret = False
else:
ret=False
i += 1
if count>1:
ret= False
return ret
4 查找与排序
旋转数组中的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
- 思路:
非递减数组旋转之后最小值,也就是寻找分界点,分界点前后都是非递减数组,分界点后面的非递减数组比分界点前面的数组都要小,因此对旋转数组按顺序查找,当出现后一个数比前一个小时,这个数就是最小值,若没有出现后一个数比前一个数小的情况,这说明这个数组所有的数都相等,返回数组第一个数即可。注意考虑数组为空的情况,返回0。2
- 解法:
# -*- coding:utf-8 -*-
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here应该用二分查找
if rotateArray == []:
return 0
low=rotateArray[-1]
for i in range(len(rotateArray)-2,0,-1):
if rotateArray[i] <= low:
low = rotateArray[i]
else:
return low
5 树
旋转数组中的最小数字
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{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):
if pre==[]:
return None
root=TreeNode(pre[0])
root.left=self.reConstructBinaryTree(pre[1:tin.index(pre[0])+1],tin[:tin.index(pre[0])])
root.right=self.reConstructBinaryTree(pre[tin.index(pre[0])+1:],tin[tin.index(pre[0])+1:])
return root
树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
- 思路:
链接:牛客网
对于两棵二叉树来说,要判断B是不是A的子结构,首先当Tree1和Tree2都不为零的时候,才进行比较,否则直接返回false。第一步在树A中查找与B根节点的值一样的节点。通常对于查找树中某一个节点,我们都是采用递归的方法来遍历整棵树。第二步就是判断树A中以R为根节点的子树是不是和树B具有相同的结构。这里同样利用到了递归的方法,如果节点R的值和树的根节点不相同,则以R为根节点的子树和树B肯定不具有相同的节点;如果它们值是相同的,则递归的判断各自的左右节点的值是不是相同。
递归的终止条件是我们达到了树A或者树B的叶节点。有地方要重点注意,DoesTree1haveTree2()函数中的两个 if 判断语句 不能颠倒顺序 。因为如果颠倒了顺序,会先判断pRoot1 是否为None, 其实这个时候,pRoot1 的节点已经遍历完成确认相等了,但是这个时候会返回 False,判断错误。
- 解法:
# -*- 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):
if pRoot1 !=None and pRoot2 !=None:
if pRoot1.val==pRoot2.val:
l1=self.issubtree(pRoot1,pRoot2)
l2=self.issubtree(pRoot1.left,pRoot2)
l3=self.issubtree(pRoot1.right,pRoot2)
return l1 or l2 or l3
else:
return False
def issubtree(self,pre,tin):
if tin==None: #pRoot2遍历到叶子结点
return True
if pre==None and tin!=None:
return False
if pre.val!= tin.val:
return False
return self.issubtree(pre.left,tin.left) and self.issubtree(pre.right,tin.right)
二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像。
-
思路:
遍历这棵树的每个结点,如果遍历到的结点有子结点,就交换它的两个子节点,当交换完所有的非叶子结点的左右子结点之后,就得到了树的镜像。 -
解法:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回镜像树的根节点
def Mirror(self, root):
# 先考虑空的情况
if root == None:
return None
else:
root.left,root.right = root.right,root.left
self.Mirror(root.left)
self.Mirror(root.right)
return root
对称的二叉树
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
-
思路:
首先根节点以及其左右子树非空,左子树的左子树和右子树的右子树相同,左子树的右子树和右子树的左子树相同即可,采用递归非递归都可,采用栈或队列存取各级子树根节点。 -
解法:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSymmetrical(self, pRoot):
if pRoot==None:
return True
return self.compare(pRoot.left,pRoot.right)
def compare(self,ptree,qtree):
if ptree==None and qtree==None:
return True
if ptree==None or qtree==None:
return False
if ptree.val==qtree.val:
if self.compare(ptree.left,qtree.right) and self.compare(ptree.right,qtree.left):
return True
else:
return False
从上到下打印二叉树
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
-
思路:
二叉树的广度优先遍历 -
解法:
# -*- 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):
if root == None:#没有的话直接返回
return []
d=[]
ret=[]
d.append(root)
print(d)
while d:
root=d.pop(0)
if root.left != None:
d.append(root.left)
if root.right != None:
d.append(root.right)
ret.append(root.val)
return ret
把二叉树打印成多行
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
-
思路:
每次循环都要用两个数组,浪费了空间 -
思路:
# -*- 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): #指向左右子树
if pRoot == None:#没有的话直接返回
return []
d=[pRoot] #每一次的每一行节点
result=[]
while d:
new_d=[]
new_res=[]
for i in d:
new_res.append(i.val)
if i.left!=None:
new_d.append(i.left)
if i.right!=None:
new_d.append(i.right)
result.append(new_res)
d=new_d
return result
按之字形顺序打印二叉树
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
-
思路:
在上一个题目把二叉树打印成多行的基础上,增加了判断:如果列表索引为偶数,列表顺序不变;如果列表索引为奇数,列表[::-1]。加入到新的result中输出。 -
解法:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def Print(self, pRoot):
if pRoot == None:#没有的话直接返回
return []
d=[pRoot] #树的根
result=[]
while d:
new_d=[]
new_res=[]
for i in d:
new_res.append(i.val)
if i.left!=None:
new_d.append(i.left)
if i.right!=None:
new_d.append(i.right)
result.append(new_res)
d=new_d
new_result=[]
for i, element in enumerate(result):
if i%2==0:
new_result.append(element)
else:
new_result.append(element[::-1])
return new_result
二叉树的下一个节点
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
- 思路:
分析二叉树的下一个节点,一共有以下情况:
- 二叉树为空,则返回空;
- 节点右孩子存在,则设置一个指针从该节点的右孩子出发,一直沿着指向左子结点的指针找到的叶子节点即为下一个节点;
- 节点不是根节点。如果该节点是其父节点的左孩子,则返回父节点;否则继续向上遍历其父节点的父节点,重复之前的判断,返回结果。
- 解法:
# -*- 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):
if pNode==None:
return
#if pNode.next==None:
# return None
if pNode.right:
p=pNode.right
while p.left:
p=p.left
return p
while pNode.next:
if pNode.next.left==pNode:
return pNode.next
pNode=pNode.next
else:
return None
二叉搜索树的后序遍历序列
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
- 思路:
已知条件:后序序列最后一个值为root;二叉搜索树左子树值都比root小,右子树值都比root大。
1、确定root;
2、遍历序列(除去root结点),找到第一个大于root的位置,则该位置左边为左子树,右边为右子树;
3、遍历右子树,若发现有小于root的值,则直接返回false;
4、分别判断左子树和右子树是否仍是二叉搜索树(即递归步骤1、2、3) - 解法:
# -*- coding:utf-8 -*-
class Solution:
def VerifySquenceOfBST(self, sequence):
if sequence == None or len(sequence)==0:
return False
#root = sequence[len(sequence)-1] #根
root = sequence[-1]
lens = len(sequence)
for i in range(lens):
if sequence[i] > root:
break
#left = sequence[:i]
for j in range(i,lens):
if sequence[j] < root:
return False
#right = sequence[i:(lens-1)]
#判断左右子树
boolleft = True
if i > 0:
boolleft = self.VerifySquenceOfBST(sequence[:i])
boolright = True
if i < (lens-1):
boolright = self.VerifySquenceOfBST(sequence[i:-1])
return boolleft and boolright
- 2019.6.29更:说好半个月完成的,5月整个月都在返修论文,6月找实习租房加入职,要继续不忘初心,每天一题
二叉树中和为某一值的路径
输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径
- 思路:
递归先序遍历树, 把结点加入路径。
若该结点是叶子结点则比较当前路径和是否等于期待和。
弹出结点,每一轮递归返回到父结点时,当前路径也应该回退一个结点 - 解法:
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回二维列表,内部每个列表表示找到的路径
def __init__(self):#如果不定义在外部,递归调用左右子树就会改变path和sumpath
self.sumpath = []
self.path = []
def FindPath(self, root, expectNumber):
# write code here
#递归先序遍历树, 把结点加入路径。
#若该结点是叶子结点则比较当前路径和是否等于期待和。
#弹出结点,每一轮递归返回到父结点时,当前路径也应该回退一个结点
if root == None:
return self.sumpath
#不为空的情况
self.path.append(root.val)
expectNumber -= root.val
if expectNumber == 0 and root.left == None and root.right == None:
templist = []
for _ in self.path:
templist.append(_)
self.sumpath.append(templist)
self.FindPath(root.left,expectNumber)
self.FindPath(root.right,expectNumber)
self.path.pop()
return self.sumpath
6 链表
从尾到头打印链表
输入一个链表,按链表值从尾到头的顺序返回一个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):
newlist=[]
while listNode:
newlist.append(listNode.val)
listNode=listNode.next
return newlist[::-1]
链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。
-
思路:
跟上面一题思路一样,只不过是倒数第k个。 -
解法:
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindKthToTail(self, head, k):
result=[]
if head==None:
return
while head!=None:
result.append(head)
head=head.next
if k>len(result) or k==0:
return None
return result[-k]
反转链表
输入一个链表,反转链表后,输出新链表的表头。
-
思路:
easy题,要注意表头不仅有值,还有指向下一个节点的指针。 -
解法:
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
if pHead == None or pHead.next == None:
return pHead
last = None
while pHead: #{1,2,3,4,5}
fore = pHead.next #fore现在为2的地址
pHead.next = last #1指向了None
last = pHead #1和它的指针赋给last
pHead = fore #2为首的链表赋给pHead
return last
每K个一组反转链表(笔试真题)
给出一个链表,每 k 个节点一组进行翻转,并返回翻转后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么将最后剩余节点保持原有顺序。
-
思路:
每k个节点进行一次反转,用两个指针记录k个区间的首尾节点,写一个reverse函数反转该局部内的节点指向,接着继续向后走,不停的取k个进行反转,如果不够k个就返回。。 -
解法:
def reverse(array, left, right, k):
cishu = k // 2
while cishu > 0:
array[left], array[right] = array[right], array[left]
left += 1
right -= 1
cishu -= 1
array = list(map(int, input().split()))
k = int(input())
beishu = len(array) // k
left = 0
right = k-1
for i in range(beishu):
reverse(array, left, right, k)
left += k
right += k
print(" ".join(str(i) for i in array))
合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
- 思路:
比较两个链表的首结点,哪个小的的结点则合并到结果链表尾结点,并向前移动一个结点。
步骤一结果会有一个链表先遍历结束,或者没有
结果链表尾结点指向剩余未遍历结束的链表
返回结果链表首结点 - 解法:
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
#普通解法
temp=ListNode(0) #初始化一个节点
result=temp #这里的时候result和temp是一样的
while pHead1 and pHead2: #这里只能是and而不是or
if pHead1.val <= pHead2.val:
temp.next = pHead1
pHead1 = pHead1.next
else:
temp.next = pHead2
pHead2 = pHead2.next
temp=temp.next
if pHead1 == None: #如果链表1结束,接链表2
temp.next = pHead2
if pHead2 == None: #如果链表2结束,接链表1
temp.next = pHead1
return result.next #因为temp的头节点值为0
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
#递归解法
if pHead1 == None:
return pHead2
if pHead2 == None:
return pHead1
while pHead1 and pHead2:
if pHead1.val <= pHead2.val:
pHead1.next = self.Merge(pHead1.next, pHead2)
return pHead1
else:
pHead2.next = self.Merge(pHead1, pHead2.next)
return pHead2
复杂链表的复制
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。
- 思路:
- 把复制的结点链接在原始链表的每一对应结点后面
- 把复制的结点的random指针指向被复制结点的random指针的下一个结点
- 拆分成两个链表,奇数位置为原链表,偶数位置为复制链表,注意复制链表的最后一个结点的next指针不能跟原链表指向同一个空结点None,next指针要重新赋值None(判定程序会认定你没有完成复制)
# -*- 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 not pHead: #空链表返回none
return None
temp = pHead #为了重新让dummy指向表头,为了下一步从表头开始操作准备
#第一步:复制N'
while temp: #复制了一个复杂链表的副本
tempnext = temp.next
copynode = RandomListNode(temp.label)
copynode.next = tempnext
temp.next = copynode
temp = tempnext
#第二步:random指向random
temp = pHead
while temp:
temprandom = temp.random
copynode = temp.next
if temprandom:
copynode.random=temprandom.next
temp = copynode.next
#第三步:两部分分离
temp = pHead
copyHead = pHead.next#第二个节点开始为复制后的
while temp:
copynode = temp.next
tempnext = copynode.next
temp.next = tempnext
if tempnext:
copynode.next = tempnext.next
else:
copynode.next = None
temp = tempnext#每次操作都是从原N开始
return copyHead
7 位运算
二进制中1的个数
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
- 思路:
- 这里剑指offer上面写的很好,一般人最可能想到的是容易引起死循环的做法:先判断整数二进制表示中最右边一位是不是1(整数和1做位与:结果是1,表明该整数最右边一位是1,否则是0);接着把二进制右移一位,再判断是不是1;直到整个整数变为0为止。
Note:不能把右移换成/,因为移位的效率比除法快得多
输入负数会陷入死循环 - 避免死循环的做法
不移位输入数字n,左移1。
Note:这种一般存在算法复杂度过大,运行超时问题,循环次数=整数二进制位数 - 有几个1就循环几次的做法(给面试官带来惊喜的做法)
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1(self, n):
count=0
if n==0:
return 0
if n<0:
n=n&0XFFFFFFFF
return bin(n).count('1')
while (n!=0):
count=count+1
n=(n-1)&n
return count
- 相关知识:
关于二进制的原码反码补码
位运算符
数值的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
- 思路:
一、利用python自带的 ** 或者pow直接出结果,但是没体现算法思想,所以不推荐
二、充分分类讨论:
考虑1、base为0,exponent<0,无效的输入,2、指数为正,3、指数为负,4、指数为0四种情况即可 - 解法:
# -*- coding:utf-8 -*-
class Solution:
def Power(self, base, exponent):
#return base**(exponent) #但是没用到算法思想
#第二种思想:分情况讨论
result=1
if base==0:
return 0
if exponent==0:
return 1
if exponent>0:
for _ in range(exponent):
result=result*base
return result
if exponent<0:
for _ in range(abs(exponent)):
result=result*base
return 1/result
调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
- 思路:
遍历元素,奇数放一个列表。偶数放一个列表,最后返回奇+偶 - 解法:
# -*- coding:utf-8 -*-
class Solution:
def reOrderArray(self, array):
single=[] #存放奇数
double=[] #存放偶数
for i in array:
if i%2==1:
single.append(i)
else:
double.append(i)
return single+double
8 画图 / 举例 / 分解
顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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.
- 思路:
首先充分考虑测试用例:多行多列,一行,一列,一个数
- 解法:
首先推荐一位牛客大佬的解法:需要了解zip函数
a = [1,2,3]
b = [4,5,6]
zipped = zip(a,b) # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)] #output
# -*- coding:utf-8 -*-
#链接:https://www.nowcoder.com/questionTerminal/9b4c81a02cd34f76be2659fa0d54342a
#来源:牛客网
#打印第一行,删除第一行,逆时针转动90度。重复以上步骤,直到矩阵为空。
class Solution:
def printMatrix(self, matrix):
# write code here
s=[]
while matrix:
s+=matrix[0]
del matrix[0]
matrix=zip(*matrix)[::-1]
return s
我的大众解法:
分为两个步骤:
- 打印一圈
a. 从左到右打印一行(这是一定有的)
b.当有两行及以上时,存在从上到下打印
c.当有两行及以上并有两列及以上时,存在从右到左
d.当有两列并有三行及以上时,存在从下到上打打印 - 起点进入下一圈,即进入下一个循环
初始几点为(0, 0), 打印一圈后有(1, 1), 再打印一圈为(2, 2)……都存在行数 > 起点 * 2 并且列数 > 起点 * 2
简单来说就是, 如果把二维数组等分为四个部分 中点都为中心,那么起点肯定都位于左上部分。根据这个条件可以判断是否还有圈数
# -*- coding:utf-8 -*-
class Solution:
# matrix类型为二维列表,需要返回列表
def printMatrix(self, matrix):
# Q1:如何求矩阵的维数
result=[] #存储结果
rows = len(matrix) #行数
columns = len(matrix[0]) #列数
start = 0
while (rows>start*2) and (columns>start*2):#保证可以循环的条件
end_y = columns-1-start #本次循环结束列
end_x = rows-1-start #本次循环结束行
#从左到右打印一行
for i in range(start,end_y+1): #还是要把起点终点写上,因为动态变化
result.append(matrix[start][i])
#从上到下打印一列
if start < end_x:
for j in range(start+1,end_x+1):
result.append(matrix[j][end_y])
#从右到左
if start < end_y and start < end_x:
for m in range(end_y-1,start-1,-1):
result.append(matrix[end_x][m])
#从下到上打印
if (start+1)<end_x and start<end_y:
for n in range(end_x-1,start,-1):
result.append(matrix[n][start])
start=start+1
return result
包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))
-
思路:
链接:https://www.nowcoder.com/questionTerminal/4c776177d2c04c2494f2555c9fcc1e49
来源:牛客网
我们可以设计两个栈:一个就是普通的栈,另外一个存储push进来的最小值。
Note:不能用python自带的min函数,因为时间复杂度应为O(n) -
解法:
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.stack=[] #定义一个空列表为进行操作的栈
self.stack_min=[] #存放小值
def push(self, node):
if not self.stack_min:#判断是否为空
self.stack_min.append(node)#如果为空直接append
else: #不为空
if node < self.stack[-1]:#比较node和栈顶元素
self.stack_min.append(node)#node小就把node直接append
else:
self.stack_min.append(self.stack_min[-1])#为了保证长度一致append之前的栈顶最小元素
self.stack.append(node)#这一步是stack必须做的
def pop(self):
if self.stack:
self.stack_min.pop()#要注意pop会直接返回列表删除元素
return self.stack.pop()
def top(self):#这个函数貌似没用
if self.stack:
return self.stack[-1]
def min(self):
if self.stack_min:
return self.stack_min[-1]
栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
- 思路:
判断序列是不是栈的弹出序列的规律:如果下一个弹出的数字刚好是栈顶数字,那么直接弹出;如果下一个弹出的数字不在栈顶,把压栈序列中还没入栈的数字压入辅助栈,直到把下一个需要弹出的数字压入栈顶为止。如果所有数字都压入栈仍然没有找到下一个弹出的数字,那么该序列不可能是一个弹出序列。
Note:链接:https://www.nowcoder.com/questionTerminal/d77d11405cc7470d82554cb392585106
来源:牛客网
借用一个辅助的栈,遍历压栈顺序,先讲第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。
举例
入栈1,2,3,4,5
出栈4,5,3,2,1
首先1入辅助栈,此时栈顶1≠4,继续入栈2
此时栈顶2≠4,继续入栈3
此时栈顶3≠4,继续入栈4
此时栈顶4=4,出栈4,弹出序列向后一位,此时为5,,辅助栈里面是1,2,3
此时栈顶3≠5,继续入栈5
此时栈顶5=5,出栈5,弹出序列向后一位,此时为3,,辅助栈里面是1,2,3
….
依次执行,最后辅助栈为空。如果不为空说明弹出序列不是该栈的弹出顺序。 - 解法:
# -*- coding:utf-8 -*-
class Solution:
def IsPopOrder(self, pushV, popV):
# write code here
stack = []#辅助栈
for psh in pushV: #遍历入栈[1,2,3,4,5
stack.append(psh)
if stack[-1] == popV[0]:#相等都删除
popV.pop(0)
stack.pop()
for pp in popV:#此时popV剩下[3,2,1]
if pp == stack[-1]:
stack.pop()
return stack == []
https://blog.csdn.net/tinkle181129/article/details/79326023
https://www.cnblogs.com/yanmk/p/9130681.html