牛客网剑指Offer_66道python(更新中)
- 归档:剑指offer
牛客网剑指Offer_编程题python实现
- 导语
- 1 Fibonacci数列及其应用(考察:递归与循环)
- 斐波那契数列
- 跳台阶
- 变态跳台阶
- 矩形覆盖
- 2 数组
- 二维数组中的查找
- 数组中重复的数字
- 构建乘积数组
- 3 字符串
- 正则表达式匹配
- 表示数值的字符串
- 4 查找与排序
- 旋转数组中的最小数字
- 5 树
- 旋转数组中的最小数字
- 树的子结构
- 二叉树的镜像
- 对称的二叉树
- 从上到下打印二叉树
- 把二叉树打印成多行
- 按之字形顺序打印二叉树
- 二叉树的下一个节点
- 二叉搜索树的后序遍历序列
- 二叉树中和为某一值的路径
- 6 链表
- 从尾到头打印链表
- 链表中倒数第k个结点
- 反转链表
- 每K个一组反转链表(笔试真题)
- 合并两个排序的链表
- 复杂链表的复制
- 7 位运算
- 二进制中1的个数
- 数值的整数次方
- 调整数组顺序使奇数位于偶数前面
- 8 画图 / 举例 / 分解
- 顺时针打印矩阵
- 包含min函数的栈
- 栈的压入、弹出序列
导语
2019/4/9,目前一共66道,我也是小白,定个目标半个月完成,让我们一起开始吧!
1 Fibonacci数列及其应用(考察:递归与循环)
斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39
思路:
就是用python写这个常用数列啦!解法:
class Solution:def Fibonacci(self, n):# write code hereF = [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 hereres=[1,2,3] #台阶从1开始,到nwhile 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 hereif(number < 3):return numberelse: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 heref=[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 heresize_A=len(A)b=[1]if size_A == 0 or size_A ==1:return Afor i in range(1,size_A):num = A[i-1] * b[i-1]b.append(num)temp = 1for 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 Trueif len(s)>0 and len(pattern)==0:return Falseif len(pattern)==1 and len(s)==0 and ( pattern[0]!='.'):return Falseif 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 Falseif 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 Falsenew_s=''count=0s_dict='0123456789'if s[0]=='+' or s[0]=='-':s=s[1:]i=0ret=Truewhile i <len(s) and ret:if s[i] in s_dict:new_s +=s[i] elif s[i]=='.':count +=1elif s[i]=='+' or i=='-':ret= Falseelif s[i]=='e' or s[i]=='E':if i==len(s)-1:ret=Falsebreakif (s[i+1] in s_dict) or (s[i+1]=='+')or s[i+1]=='-':ret= Trueif s[i+1]=='+' or s[i+1]=='-':i += 1if '.' in s[i:]:ret = Falseelse:ret=Falsei += 1if count>1:ret= Falsereturn 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 0low=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 Noneroot=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 l3else:return Falsedef issubtree(self,pre,tin):if tin==None: #pRoot2遍历到叶子结点return Trueif pre==None and tin!=None:return Falseif pre.val!= tin.val:return Falsereturn 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 Noneelse:root.left,root.right = root.right,root.leftself.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 Truereturn self.compare(pRoot.left,pRoot.right)def compare(self,ptree,qtree):if ptree==None and qtree==None:return Trueif ptree==None or qtree==None:return Falseif ptree.val==qtree.val:if self.compare(ptree.left,qtree.right) and self.compare(ptree.right,qtree.left):return Trueelse: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_dreturn 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_dnew_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 Noneif pNode.right:p=pNode.rightwhile p.left:p=p.leftreturn pwhile pNode.next:if pNode.next.left==pNode:return pNode.nextpNode=pNode.nextelse: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 = Trueif i > 0:boolleft = self.VerifySquenceOfBST(sequence[:i])boolright = Trueif 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和sumpathself.sumpath = []self.path = []def FindPath(self, root, expectNumber):# write code here#递归先序遍历树, 把结点加入路径。#若该结点是叶子结点则比较当前路径和是否等于期待和。#弹出结点,每一轮递归返回到父结点时,当前路径也应该回退一个结点if root == None:return self.sumpath#不为空的情况self.path.append(root.val)expectNumber -= root.valif 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 = Noneclass Solution:# 返回从尾部到头部的列表值序列,例如[1,2,3]def printListFromTailToHead(self, listNode):newlist=[]while listNode:newlist.append(listNode.val)listNode=listNode.nextreturn newlist[::-1]
链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。
思路:
跟上面一题思路一样,只不过是倒数第k个。解法:
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = Noneclass Solution:def FindKthToTail(self, head, k):result=[]if head==None:return while head!=None:result.append(head)head=head.nextif k>len(result) or k==0:return Nonereturn result[-k]
反转链表
输入一个链表,反转链表后,输出新链表的表头。
思路:
easy题,要注意表头不仅有值,还有指向下一个节点的指针。解法:
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:# 返回ListNodedef ReverseList(self, pHead):if pHead == None or pHead.next == None:return pHeadlast = Nonewhile pHead: #{1,2,3,4,5}fore = pHead.next #fore现在为2的地址pHead.next = last #1指向了Nonelast = pHead #1和它的指针赋给lastpHead = fore #2为首的链表赋给pHeadreturn last
每K个一组反转链表(笔试真题)
给出一个链表,每 k 个节点一组进行翻转,并返回翻转后的链表。k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么将最后剩余节点保持原有顺序。
思路:
每k个节点进行一次反转,用两个指针记录k个区间的首尾节点,写一个reverse函数反转该局部内的节点指向,接着继续向后走,不停的取k个进行反转,如果不够k个就返回。。解法:
def reverse(array, left, right, k):cishu = k // 2while cishu > 0:array[left], array[right] = array[right], array[left]left += 1right -= 1cishu -= 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 += kright += 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而不是orif pHead1.val <= pHead2.val:temp.next = pHead1pHead1 = pHead1.nextelse:temp.next = pHead2pHead2 = pHead2.nexttemp=temp.nextif pHead1 == None: #如果链表1结束,接链表2temp.next = pHead2if pHead2 == None: #如果链表2结束,接链表1temp.next = pHead1return 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 pHead2if pHead2 == None:return pHead1while pHead1 and pHead2:if pHead1.val <= pHead2.val:pHead1.next = self.Merge(pHead1.next, pHead2)return pHead1else: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:# 返回 RandomListNodedef Clone(self, pHead):# write code hereif not pHead: #空链表返回nonereturn Nonetemp = pHead #为了重新让dummy指向表头,为了下一步从表头开始操作准备#第一步:复制N'while temp: #复制了一个复杂链表的副本tempnext = temp.nextcopynode = RandomListNode(temp.label)copynode.next = tempnexttemp.next = copynodetemp = tempnext#第二步:random指向randomtemp = pHeadwhile temp:temprandom = temp.randomcopynode = temp.nextif temprandom:copynode.random=temprandom.nexttemp = copynode.next#第三步:两部分分离temp = pHeadcopyHead = pHead.next#第二个节点开始为复制后的while temp:copynode = temp.nexttempnext = copynode.nexttemp.next = tempnextif tempnext:copynode.next = tempnext.nextelse:copynode.next = Nonetemp = 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=0if n==0:return 0if n<0:n=n&0XFFFFFFFF return bin(n).count('1')while (n!=0):count=count+1n=(n-1)&nreturn 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=1if base==0:return 0if exponent==0:return 1if exponent>0:for _ in range(exponent):result=result*basereturn resultif exponent<0:for _ in range(abs(exponent)):result=result*basereturn 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 heres=[]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 = 0while (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+1return 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)#如果为空直接appendelse: #不为空if node < self.stack[-1]:#比较node和栈顶元素self.stack_min.append(node)#node小就把node直接appendelse: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 herestack = []#辅助栈for psh in pushV: #遍历入栈[1,2,3,4,5stack.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
来源:牛客网 ↩︎
来源:牛客网 ↩︎
牛客网剑指Offer_66道python(更新中)相关推荐
- 《牛客网 剑指Offer20--40道》
<剑指Offer> 牛客网 20到40题 面试题21:栈的压人弹出序列 面试题22:由上向下打印二叉树 广度优先遍历**** 面试题23:二叉搜索树的后序遍历 面试题24:二叉树中和为某一 ...
- Day5.牛客网剑指offer 67题之43-54题(java代码)
文章目录 Day5.牛客网剑指offer 67题之43-54题 43.左旋转字符串 44.翻转单词顺序列 45.扑克牌顺序 46.孩子们的游戏 47.求1+2+...+n 48.不用加减乘除做加法 4 ...
- 牛客网剑指offer编程实践1-10题
牛客网剑指offer编程实践1-10题 1.二维数组中的查找 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这 ...
- 牛客网剑指office系列:替换空格
牛客网剑指office系列:替换空格 本系列应用于想要快速提升算法水平的人,最好有一定基础 题目:替换空格 请实现一个函数,将一个字符串中的每个空格替换成"%20".例如,当字符串 ...
- Java算法:牛客网Java版剑指Offer全套算法面试题目整理及电子档,Java算法与数据结构面试题,面试刷题、背题必备!牛客网剑指offer
剑指offer(java版) 牛客网Java版剑指Offer全套题目67道 资源来源于网络 目录 1.二维数组中的查找 2.替换空格 3.从尾到头打印链表 4.重建二叉树 5.用两个栈实现队列 6.旋 ...
- 牛客网剑指offer java 全部题解
经过数月的努力,终于更完了牛客网的66道剑指offer,以下的顺序和大家在牛客网的顺序是一样的(排序也花了不少时间),希望对大家找工作/提高算法能力能起到些许帮助. 每天一道剑指offer-二维数组中 ...
- 《牛客网 剑指Offer前20题》
<剑指Offer> 牛客网 前20道题 前言知识 面试题1:二维数组中的查找 面试题2:二维数组中的查找 面试题3:从头到尾打印链表 面试题4:重建二叉树 ***** 面试题5:两个栈实现 ...
- 牛客网剑指offer(Python版)
剑指offer官网: https://www.nowcoder.com/ta/coding-interviews 写在前面的话 刷剑指offer的时候只需要提交函数核心部分,但是在公司实际笔试时却需 ...
- 牛客网剑指offer——Java题解
剑指offer JZ1 二维数组中的查找 题目描述 在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这 ...
最新文章
- 北大AI公开课2019 | 微软亚洲研究院周明:NLP的进步将如何改变搜索体验?
- 按键映射_第三章 中文注释及按键相关
- UI标签库专题十一:JEECG智能开发平台 DictSelect (数据字典下拉选择框)
- Windows 安装 sbt
- 英雄联盟更新防沉迷规则:未成年用户节假日每日限玩3小时
- python基本语句大全_python常见语句汇总
- win10 linux声音,win10电脑突然没有声音的10种修复方法
- 计算机程序设计类论文,计算机编程论文
- **JAVA实习周记(第三周):哦**
- 信息论实验一:信源熵的计算
- Reeder 5.0.3 将RSS阅读体验发挥到极致
- oc引导windows蓝屏_使用Opencore引导ubuntu以及Linux的步骤
- 用c语言输出英文字母表音标,26个英文字母表中文
- vue项目中实现价格被横线划掉,折扣价效果
- app.run 相关参数 flask配置文件
- 《谁的青春不迷茫》作者:刘同摘录
- SharePoint 2010 Webpart 部署 报错的解决方法
- 20140309_博瑞学习
- 给我5个带”一“字的成语
- SAP-PM设备模块-PM主数据之设备主数据