剑指offer刷题
文章目录
- 剑指offer刷题
- 1.二维数组中的查找
- 2.替换空格
- 3.从尾到头打印链表
- 4.重建二叉树
- 5.用两个栈实现队列
- 6.旋转数组的最小数字
- 7.斐波那契数列
- 8.台阶跳
- 9.变态台阶跳
- 10.矩形覆盖
- 11.二进制中的1的个数(未解决)
- 12.数值的整数次方
- 13.调整数组顺序使奇数位于偶数前
- 14.链表中倒数第k个节点
- 15.反转链表
- 16.链表合成
- 17.判断是否是子二叉树
- 18.盛水最多的容器
- 19.Z型打印
- 20.倒序输出数字
- 21.包含min函数的栈
- 22.栈的压入、弹出序列
- 23.从上往下打印二叉树
- 24.判断是否是二叉树的后序遍历
- 25.二叉树中和为某一值的路径
- 26.复杂链表的复制
- 27.二叉搜索树与双向链表
- 28.字符串的排列(未解决)
- 29.数组中出现次数超过一半的数字
- 30.最小的k个数
- 31.连续子数组的最大和
- 32.整数中1出现的次数(未解决)
- 33.把数组排成最小的数
- 34.丑数
- 35.第一个只出现一次的字符
- 36.数组中的逆序对
- 37.两个链表的第一个公共节点
- 38.数字在排序数组中出现的次数
- 39.二叉树的深度
- 40.平衡二叉树
- 41.数组中只出现一次的数字
- 42.和为S的连续正数序列
- 43.和为S的两个数字
- 44.左旋转字符串
- 45.反转单词顺序列
- 46.扑克牌顺子(未解决)
- 47.孩子们的游戏(未解决)
- 48.求1+2+3+...+n
- 49.不用加减乘除做加法(未解决)
- 50.把字符串转换成整数(未解决)
- 51.数组中重复的数字
- 52.构建乘积数组
- 53.正则表达式匹配
- 54.表示数值的字符串
- 55.字符流中第一个不重复的字符
- 56.链表中环的入口节点
- 57.删除链表中重复的节点(未解决)
- 58.二叉树的下一个节点
- 59.对称的二叉树
- 60.按之字形顺序打印二叉树
- 61.把二叉树打印成多行
- 62.序列化二叉树
- 63.二叉搜索树的第K个节点
- 64.数据流中的中位数
- 65.滑动窗口的最大值
- 66.矩阵中的路径
- 67.机器人的运动范围(未解决)
- 68.剪绳子
1.二维数组中的查找
- 题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数
- 思路:先遍历一维数组,拿到每个具体数组,然后拼成一个长的一维数组,遍历长数组,判断目标数字是否在内
class Solution:
def Find(self,target,array):
goal_array=[]
for ele in array:
goal_array.extend(ele)
for ele in goal_array:
if ele == target:
return True
return False
2.替换空格
- 题目:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy
- 思路:直接replace,或者遍历替换
class Solution:
def replaceSpace(self,s):
s=list(s)
for i in range(len(s)):
if s[i]==' ':
s[i]='%20'
return ''.join(s)
3.从尾到头打印链表
- 题目:输入一个链表,按链表从尾到头的顺序返回一个ArrayList
- 思路:遍历链表,插入一个数组,每次插入位置为0,返回列表
class Solution:
def printListFromTailToHead(self,listNode):
list_node=[]
head=listNode
while head:
list_node.insert(0,head.val)
head=head.next
return list_node
4.重建二叉树
- 题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回
- 思路:二叉树的前序遍历,每个开头的数字都是根节点,中序遍历根节点在中间,将前序遍历和中序遍历分别切割为左子树和右子树的前,中序遍历,采用递归的方法
class TreeNode:
def __init__(self,x):
self.val=x
self.left=None
self.right=None
class Solution:
def reConstructBinaryTree(self,pre,tin):
if len(pre)==0:
return None
if len(pre)==1:
return TreeNode(pre[0])
else:
root=TreeNode(pre[0])
i=tin.index(root.val)
tree_left=tin[:i]
tree_right=tin[i+1:]
root.left=self.reConstructBinaryTree(pre[i:i+1],tree_left)
root.right=self.reConstructBinaryTree(pre[i+1:],tree_right)
return root
5.用两个栈实现队列
- 题目:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型
- 思路:栈的特点是FILO(first in last out),队列的特点是FIFO(first in first out)用两个列表来模拟栈
class Solution:
def __init__(self):
self.stack1=[]
self.stack2=[]
# 栈的push是从从上压入,而队列push是入队尾,相当于List的append
def push(self,node):
self.stack1.append(node)
# 队列的pop是尾取
def pop(self):
if self.stack2==[]:
while self.stack1:
self.stack2.append(self.stack1.pop())
return self.stack2.pop()
6.旋转数组的最小数字
- 题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0 - 思路:其实就是找数组中的最小元素,快排,然后拿第一个值
class Solution:
def minNumberInRotateArray(self,rotateArray):
return min(rotateArray)
7.斐波那契数列
- 题目:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)n<=39
- 思路:最简单的方法是递归,也可以用while 循环
class Solution:
def Fibonacci(self,n):
temp=[0,1]
while len(temp)<=n:
temp.append(temp[-1]+temp[-2])
return temp[n]
-------------------------------------------------
# 解法二:
def Fibonacci(self,n):
if n==0:
return 0
if n==1:
return 1
return self.Fibonacci(n-1)+self.Fibonacci(n-2)
8.台阶跳
- 题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)
- 思路:同斐波那契数列
def Solution:
def jumpFloor(self,number):
temp=[1,1]
while len(temp)<=number:
temp.append(temp[-1]+temp[-2])
return temp[number]
9.变态台阶跳
- 题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法
- 思路:2**(number-1)
class Solution:
def jumpFloorII(self,number):
return 2**(number-1)
10.矩形覆盖
- 题目:我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
- 思路:斐波那契数列思想,递归
class Solution:
def rectCover(self,number):
temp=[0,1,2]
while len(temp)<=number:
temp.append(temp[-1]+temp[-2])
return temp[number]
11.二进制中的1的个数(未解决)
- 题目:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。、
- 思路:
12.数值的整数次方
- 题目:给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方,保证base和exponent不同时为0
- 思路:简单
class Solution:
def Power(self,base,exponent):
temp=1
for _ in range(abs(exponent)):
temp*=base
return temp if exponent>0 else 1/temp
13.调整数组顺序使奇数位于偶数前
- 题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变
- 思路:初始化两个列表(奇,偶),遍历数组,如果数字是奇数,存入奇列表,如果是偶数,存入偶数列表,两个列表相加后返回
class Solution:
def reOrderArray(self,array):
odd,even=[],[]
for i in array:
odd.append(i) if i%2==1 else even.append(i)
return odd+even
14.链表中倒数第k个节点
- 题目:输入一个链表,输出该链表中倒数第k个结点
- 思路:遍历链表,将节点依次存入列表,返回[-k]
class Solution:
def FindKthToTail(self,head,k):
node_list=[]
while head:
node_list.append(head)
head=head.next
if len(node_list)<k or k<1:
return
return node_list[-k]
15.反转链表
-
题目:输入一个链表,反转链表后,输出新链表的表头
-
思路:
1.定义一个指向前节点的指针prev,一个头结点phead,一个当前节点curr
2.头节点遍历整个链表,直到phead为空为止;
3.当前节点始终指向头结点现在所在的位置,当前节点的.next指针指向prev
def ReerseList(phead): prev=None while phead: curr=phead phead=phead.next #当前的头节点就是反转后链表的尾节点,所以尾节点的.next=None curr.next=prev prev=curr return prev
16.链表合成
-
输入两个单调递增的链表,输出两个链表合成后的链表,要求,合成后链表满足单调不递减规则
-
思路:
1.先判断两个链表是否为空,如果为空,无需排序,直接返回另一个非空链表
2.采用递归的方法
def Merge(pHead1, pHead2): phead=ListNode(None) if not pHead1: return pHead2 if not pHead2: return pHead1 if pHead1.val<pHead2.val: phead=pHead1 phead.next=Merge(pHead1.next,pHead2) else: phead=pHead2 phead.next=Merge(pHead1,pHead2.next) return phead
-
非递归方法
def Merge(l1,l2): temp=ListNode(None) cur=temp while l1 and l2: if l1.val<l2.val: temp.next=l1 l1=l1.next else: temp.next=l2 l2=l2.next temp=temp.next temp.next=l1 or l2 return cur.next
17.判断是否是子二叉树
-
题目:输入两棵二叉树A,B,判断B是不是A的子结构(约定空树不是任意一棵树的子结构)
-
思路:采用递归的方法
class TreeNode: def __init__(self,x): self.val=x self.left=None self.right=None class Solution: def HasSubtree(self, pRoot1, pRoot2): #先判断A,B两棵树是否存在空树,若有,直接返回False if pRoot1==None or pRoot2==None: return False result=False if pRoot1.val==pRoot2.val: result=self.isSubtree(pRoot1,pRoot2) if result==False: #由于需要判断B是否是A的子结构,在根节点不相同的情况下,需要判断A的左子树和右子树是否和B有相同的结构 result=self.HasSubtree(pRoot1.left,pRoot2)|\ self.HasSubtree(pRoot1.right,pRoot2) return result def isSubtree(self,root1,root2): #判断顺序很重要,现在要判断的是B是否是A的子树,不用判断A是否是B的子树 #且在pRoot1.val==pRoot2.val的情况下,说明A,B的根节点是相同的,均不是空树 if root2==None: return True if root1==None: return False if root1.val==root2.val: return self.isSubtree(root1.left,root2.left)&\ self.isSubtree(root1.right,root2.right) return False
18.盛水最多的容器
-
给定n个非负整数a1,a2…an,每个数代表坐标中的一个点(i,ai)。在坐标内画n条垂线,垂线i的两端分别为(i,ai),和(i,0),找出其中的两条直线,使得他们与x轴共同构成的容器可以容纳最多的水
-
思路
1.可以使用两次for循环,将每个面积都求出来,放在列表内,通过max()函数找出最大值
2.可以在函数内动态维护一个最大面积
# 解法1:
def maxarea(target_list):
temp_s=[]
for i in range(len(target_list)-1):
for j in range(i+1,len(target_list)):
if target_list[i]>target_list[j]:
temp_s.append(target_list[j]*(j-i))
else:
temp_s.append(target_list[i]*(j-i))
return max(temp_s)
注:该种解法可以解决当前问题,但是时间复杂度为O(n^2),因为有两次for循环,时间复杂度较大
# 解法2:
left,right=0,len(target_list)-1
temp_s=0
while left<right:
width=right-left
area=(min(target_list[left]*width,target_list[right]*width))
if temp_s<area:temp_s=area
if target_list[left]>target_list[right]:
right-=1
else:
left+=1
return temp_s
19.Z型打印
- 比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
输出:LCIRETOESIIGEDHN
- 思路
1.字符串s是以Z字形为顺序存储的字符串,目标是按行打印
2.设numrows行字符串分别为s1,s2,…,sn,容易发现:按照顺序遍历字符串s时,每个字符c在Z字形中对应的行索引先从s1增大到sn,再从sn减小到s1…如此反复
- 时间复杂度O(N)
def convert(s,numRows):
if numRows<2:return s
res=["" for _ in range(numRows)] #res=["","",""]
i,flag=0,-1
for c in s:
res[i]+=c
if i==0 or i == numRows-1:flag=-flag
i+=flag
return "".join(res)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QBDtB1XV-1585654205504)(C:\Users\pxr\Desktop\一些文件夹\md图片\Z型打印图解.png)]
20.倒序输出数字
-
num=12345678 输出87654321负数要保留负号
-
思路:
先将int类型的数字转换成str类型,然后通过遍历的方法拿到目标数字
def func(x): flag=0 restr="" if x<0: flag=1 x=str(abs(x)) res=[] for i in x: res.append(i) for c in res[::-1]: restr+=c restr=int(restr) if flag==1: restr=-restr return restr
21.包含min函数的栈
-
题目:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。
-
思路:定义一个辅助栈B,压栈的时候,如果A栈的栈顶元素比待压入元素大,则B不压入,小于等于则A压入;出栈时,若AB栈顶元素不等,则B出A不出
class Solution:
def __init__(self):
self.stack=[] # 定义的辅助栈
self.stack_min=[]
def push(self,node):
if not self.stack_min or self.stack_min[-1]>=node: # A栈的栈顶元素大于压入元素,则需要压入
self.stack_min.append(node)
self.stack.append(node)
def pop(self): # 从栈顶依次弹出
if self.stack[-1]==self.stack_min[-1]:
self.stack_min.pop()
self.stack.pop()
def top(self):
return self.stack[-1]
def min(self):
return self.stack_min[-1]
22.栈的压入、弹出序列
- 题目:输入两个整数序列,第一个序列表示栈的压入顺序pushV,请判断第二个序列是否可能为该栈的弹出顺序popV。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
- 思路:借用一个辅助的栈,遍历压栈顺序,依次遍历压栈顺序,并append到辅助栈,同时判断辅助栈的最后一个元素是否和弹出栈的第一个元素相等,如果相等,则弹出相同元素,如果不等,则继续向辅助栈中按照压栈顺序添加元素,当上述循环结束后,如果辅助栈里没有元素,则返回True,否则返回False
class Solution:
def IsPopOder(self,pushV,popV):
if not pushV or len(pushV)!=len(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
return True
23.从上往下打印二叉树
- 题目:从上往下打印出二叉树的每个节点,同层节点从左至右打印
- 思路:二叉树的广度遍历,利用队列完成,创建一个空队列,每次从队列中取出第一个节点,并判断该节点是否存在左子树,若存在,则放入队列,若不存在,再判断该节点是否存在右子树,若存在,放入队列
class Solution:
def PrintFromTopToBottom(self,root):
if not root :
return None
nqueue=[root] #创建一个队列,里面存入根节点
node_list=[]
while nqueue: # 循环开始
cur_node=nqueue.pop(0) # 当前节点就是最先放进队列的元素,将其取出,并添加到列表中
node_list.append(cur_node.val)
if cur_node.left: # 根据题目要求,先左后右,如果左子树存在,则放入队列
nqueue.append(cur_node.left)
if cur_node.right: # 右子树同理
nqueue.appende(cur_node.right)
return node_list
24.判断是否是二叉树的后序遍历
- 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同
- 思路:由于需要判断是否是二叉搜索树,标明该二叉树符合二分查找顺序(左小右大),且该数组是后序遍历(左,右,中)的结果
class Solution:
def VerifySquenceOfBST(self,sequence):
if sequence==None or len(sequence)==0:
return False
length=len(sequence)
root=sequence[-1] # 由于是后序遍历,所以最后一个值是根节点的值
for i in range(length):
if sequence[i]>root: # 当找到数组中大于根节点的数,则标明进入了右子树,其后的所有节点都应该大于根节点
break
for j in range(i,length): # 进入右子树部分,判断是否所有节点都大于根节点
if sequence[j]<root:
return False
left=bool
if i>0: # 由于拆分出来的两部分仍然为有序二叉树,利用递归判断左子树
left=self.VerifySquenceOfBST(sequence[0:i])
right=bool
if i<length-1: # 利用递归判断右子树
right=self.VerifySquenceOfBST(sequenct[i,-1])
return left and right
25.二叉树中和为某一值的路径
- 题目:输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
- 思路:需要返回的是一个列表的集合,所以不是只找出一条路径,需要找出所有符合条件的路径,并且将路径最长的列表放在最前,先从根节点开始判断,如果根节点和目标数相等,则将其添加入列表,然后用递归,分别递归左子树和右子树,但是目标数需要减去响应已经添加的根节点
class Solution:
def FindPath(self,root,expectNumber):
if not root:
return []
if root and not root.left and not root.right and root.val=expectNumber: # 看根节点是否和目标值匹配
return [[root.val]]
res=[]
left=self.FindPath(root.left,expectNumber-root.val) # 递归判断左子树
right=self.FindPath(root.right,expectNumber-root.val) # 递归判断右子树
for i in left+right:
res.append([root.val]+i)
return res
26.复杂链表的复制
-
题目:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
-
思路:1.把复制的节点链接在原始链表的每一个对应节点后面
2.把复制的节点的random指针指向被复制节点的random指针的下一个节点
3.拆分两个链表,奇数位置为原链表,偶数位置为复制链表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o7uAlcma-1585654205505)(C:\Users\pxr\Desktop\一些文件夹\md图片\复杂链表复制(1).png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WnU41v5T-1585654205506)(C:\Users\pxr\Desktop\一些文件夹\md图片\复杂链表复制(2).png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rWxKrh8C-1585654205506)(C:\Users\pxr\Desktop\一些文件夹\md图片\复杂链表复制(3).png)]
class Solution: # dummy:复制的
def Clone(self,pHead):
if not pHead:
return None
# 第一步:将复制的节点放在原节点后,其本质是链表的遍历
dummy=pHead
while dummy:
copynode=RandomListNode(dummy.label) # 复制节点
# 复制节点的下一个指针,指向原链表的下一个节点,见图1(A'->B)
copynode.next=dummy.next
# 而原节点的下一个指针不再指向链表的原本下一个节点,而是指向复制节点,见图1(A->A')
dummy.next=copynode
# 由于已经改变链表的原有结构,dummy.next指向的不是原链表的下一个节点,所以需要dummy.next.next才是原来节点的指针指向
dummy=dummy.next.next
# 第二步:规范复制后节点的random指针的指向,步骤和第一步一样,遍历链表
dummy=pHead
while dummy:
copynode=dummy.next # 在完成了第一步后,每一个输入节点的后面都是复制节点
if dummy.random: # 由于是随机指针,需要判断指针指向的下一个节点是否存在
copynode.random=dummy.random.next
dummy=copynode.next
# 第三步:取出复制的链表,由于完成前两步后两个链表仍拼接在一起见图2(A->A'),而正确的链表指针应该是A->B,所以需要拆分
dummy=pHead
copyHead=pHead.next # 分别定为表头
while dummy:
copyNode=dummy.next # 由于第一步,复制节点是在原节点之后
dummy.next=copyNode.next # 原节点的指针应该指向B,也就是复制节点之后
if dummy.next: # 判断是否是尾节点
copyNode.next=dummy.next.next
else:
copyNode.next=node # 给复制链表的尾节点复制(None)
dummy=dummy.next
return copyHead
27.二叉搜索树与双向链表
-
题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向
-
思路:1.将左子树构造成双链表,并返回链表头结点
2.定位至左子树双链表最后一个节点
3.如果左子树链表不为空的话,将当前root追加到左子树链表
4.将右子树构造成双链表,并返回头结点
5.如果右子树链表不为空的话,将该链表追加到root节点后
6.根据左子树链表是否为空,确定返回的节点
class Solution:
def Convert(self,pRootOfTree):
if not pRootOfTree:
return None
if pRootOfTree and not pRootOfTree.left and not pRootOfTree.right:
return pRootOfTree
# 递归处理左子树
self.Convert(pRootOfTree.left)
left=pRootOfTree.left
if left: # 如果左子树存在
while left.right: # 如果左子树的右子树存在
left=left.right # 找到左子树的右子树的叶节点
# 由于有序二叉树,左子树的所有值都比根节点小,当找到根节点左子树的右子树的叶节点后,就是该左子树中最大的节点,所以pRootOfTree.left=left(注意:此时的left就是左子树的右子树的叶子结点),left.right=pRootOfTree
pRootOfTree.left,left.right=left,pRootOfTree
# 递归处理右子树
self.Convert(pRootOfTree.right)
right=pRootOfTree.right
if right:
while right.left: # 如果右子树的左子树存在
right=right.left # 找到右子树的左子树的叶节点
pRootOfTree.right,right.left=right,pRootOfTree # 这里同理左子树
while pRootOfTree.left: # 如果根节点的左子树存在
pRootOfTree=pRootOfTree.left # 则向下遍历,直至找到左子树的叶子结点,该节点就是整个链表的头节点(因为最小)
return pRootOfTree
28.字符串的排列(未解决)
- 题目:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba
- 思路:略…
29.数组中出现次数超过一半的数字
-
题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
-
思路:假设有这个数字,那么他的数量一定比其他所有数字之和还要多
1.先要找到这个数字
2.再看这个数字是否出现次数超过半数
tip:因为如果一个数一旦出现次数过半,最极端的情况是插空分布在各个数字之间,最后的count计数也不会为0,所以只要找到count计数不为0的那个数字,就是目标数字,当然,要排除极端情况,也就是末尾囤积了大量相同数字
class Solution:
def MoreThanHalfNum_Solution(self,numbers):
if not numbers:
return 0
num=numbers[0] # 从第一个数字开始,找到出现过半的数字
count=1 # 并且开始计数
for i in range(1,len(numbers)):
if numbers[i]==num: # 从第二个数开始遍历数组,若数字相等,则计数+1,反之-1
count+=1
else:
count-=1
if count==0: # 若当前计数为0,则将标准数字设定为你当前数字,并且计数重新为1
num=numbers[i]
count=1
# 重新开始计数,计算num的出现次数
count=0
for i in numbers:
if i ==num:
count+=1
return num if count>len(numbers)/2 else 0
30.最小的k个数
- 题目:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,
- 思路:排序,取k个
class Solution:
def GetLeastNumbers_Solution(self,tinput,k):
if tinput is None:
return None
if len(tinput)>k:
return[]
tinput=sorted(tinput)
return tinput[:k]
31.连续子数组的最大和
-
题目:HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
-
思路:动态维护一个最大数组
1.以dp[i]表示以元素array[i]结尾的最大连续子数组和,以[-2,-3,4,-1,-2,1,5,-3]为例
2.dp[0]=-2 dp[1]=-3 dp[2]=4 dp[3]=3
3.dp[i]=max(dp[i-1]+array[i],array[i])
class Solution:
def FindGreatestSumOfSubArray(self,array):
length=len(array)
dp=[i for i in array]
for i in range(1,length):
dp[i]=max(dp[i-1]+array[i],array[i])
return max(dp)
32.整数中1出现的次数(未解决)
- 题目:求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)
- 思路:找规律
33.把数组排成最小的数
- 题目:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323
- 思路:由于str类型可以排序,且排序规则为按照字节序排序,所以可以将整数数组中的所有数字转换成str类型,再进行排序
class Solution:
def PrintMinNumber(self,numbers):
if not numbers:return ''
# 先用map函数将所有数字变成str类型,再变成列表
numbers=list(map(str,numbers))
# 构造匿名函数,比较相邻两个数以不同顺序相加的大小,并按照从小到大排列
numbers.sotr(cmp=lambda x,y:cmp(x+y,y+x))
# lstrip()用于截掉字符串左边的空格或指定字符串
return ''.join(numbers).listrip('0') or '0'
34.丑数
-
题目:把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数
-
思路:
质因子:也叫质因数,指能整除给定正整数的质数,连个没有共同质因子的正整数称为互质
一个丑数的因子只有2,3,5,那么丑数p = 2 ^ x * 3 ^ y * 5 ^ z,也就是说,一个丑数一定有另一个丑数乘以2或3或5得到,那么从1开始,乘以2,3,5得到2,3,5三个丑数,从这三个丑数触发,得到4,6,10,6,9,15,10,15,25九个丑数,但是通过这种方法得到的丑数会有重复,所以可以维护三个队列:
(1)丑数数组:1
乘以2的队列:2
乘以3的队列:3
乘以5的队列:5
选择三个队列头最小的数2加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列
(2)丑数数组:1,2
乘以2的队列:4
乘以3的队列:3,6
乘以5的队列:5,10
选择三个队列头最小的数3加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列
(3)丑数数组:1,2,3
乘以2的队列:4,6
乘以3的队列:6,9
乘以5的队列:5,10,15
选择三个队列头里最小的数4加入丑数数组,溶蚀将该最小的数乘以2,3,5放入三个队列
(4)丑数数组:1,2,3,4
乘以2的队列:6,8
乘以3的队列:6,9,12
乘以5的队列:10,15,20
选择三个队列头里最小的数5加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列
…
class Solution:
def GetUglyNumber_Solution(self,index):
if index<1:
return 0
res=[1] # 创建一个结果列表
t2=t3=t5=0 # t2,t3,t5分别是三个队列(乘以2或3或5)
nextindex=1
while nextindex<index:
# 将每次乘以2,3,5的数取最小,加入到结果列表中
minNumber=min(res[t2]*2,res[t3]*3,res[t5]*5)
res.append(minNumber)
# 判断此次加入列表的数是哪个队列(乘以2,还是3,还是5),并将对应队列的游标+1
while res[t2]*2<=minNumber:
t2+=1
while tes[t3]*3<=minNumber:
t3+=1
while tes[t5]*5<=minNumber:
t5+=1
nextindex+=1
return res[nextindex-1]
35.第一个只出现一次的字符
- 题目:在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)
- 思路:创建一个哈希表,用来存储每个字母的出现次数
class Solution:
def FirstNotRepeatingChar(self,s):
dict_s={}
# 遍历字符串,将字母依次存入字典,如果该字母第一次出现,则计数为1,反之则+1
for item in s:
dict_s[item]=1 if item not in dict_s else dict_s[item]+1
# 再次遍历字符串,找到出现次数为1的字符,将其索引返回
for item in s:
if dict_s[item]==1:
return s.index(item)
return -1
36.数组中的逆序对
- 题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007(默认不会出现相同数字)
- 思路:拷贝该数组后对拷贝的数组排序,计算数组中的最小值在原始数组中出现的位置,统计原始数组中最小值前面的个数,之后再原始数组中去掉最小值,重复上述步骤
class Solution:
def InversePairs(self,data):
count=0
# 现将数组排序,采用快排
sort_data=self.quick_sort(data)
# 遍历排序后的数组,找到排序数组中数字在原数组中的索引,由于是按照从小到大排序的,所以该数字以前的数字都比他小,也就是逆序对,所以该数字的索引就是逆序对的个数
for ele in sort_data:
pos=data.index(ele)
count+=pos
# 将该数字从原数组中删除
data.pop(pos)
return count%1000000007
def quick_sort(self,data):
if len(data)<2:
return data
base=data[0]
left=[i for i in data if i<base]
right=[i for i in data if i>base]
return self.quikc_sort(left)+[base]+self.quick_sort(right)
37.两个链表的第一个公共节点
- 题目:输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
- 思路:利用连个链表的长度差,如果两个链表有公共节点,那两个链表共用公共节点之后的部分,计算两个链表的长度差diff,让较长的链表前进diff后,这两个链表同时同步向后移动,直到两个链表的节点相等。
class Solution:
def FindFirstCommonNode(self,pHead1,pHead2):
len1=self.getLength(pHead1)
len2=self.getLength(pHead2)
if len2>len1:
pHead1,pHead2=pHead2,pHead1
diff=abs(len1-len2)
while diff>0:
pHead1=pHead1.next
diff-=1
while pHead1!=pHead2:
pHead1=pHead1.next
pHead2=pHead2.next
return pHead1
def getLength(self,Head):
count=0
while Head:
count+=1
Head=Head.nex
return count
---------------------------------------------------------------
# 解法二:
def FindFirstCommonNode(self, pHead1, pHead2):
p1, p2 = pHead1, pHead2
while p1 != p2:
p1 = p1.next if p1 != None else pHead2
p2 = p2.next if p2 != None else pHead1
return p1
38.数字在排序数组中出现的次数
-
题目:统计一个数字在排序数组中出现的次数
-
思路:采用哈希表,由于是排序数组,将数字存入字典,出现一次,则计数+1(但是这种方法的时间复杂度是O(n))
正解:由于是有序数组,可用二分查找叨叨给定数字及其坐标,以该坐标为中点,向前向后找到这个数字的始 - 终位置
class Solution:
def GetNumberOfK(self,data,k):
dict_data={}
for ele in data:
# 遍历数组,将数组中的元素存入哈希表,如果是第一次出现,计数为1,反之计数+1
dict_data[ele]=1 if ele not in data else dict_data[ele]+1
for ele in data:
if ele == k:
return dict_data[ele]
return 0
-----------------------------------------------------------
# 解法二:
def GetNumberOfK(self,data,k):
if len(data)<1:
return 0
mid=len(data)//2 # 找到索引在中间的数
if data[mid]==k:
start,end=mid,mid # 如果该数恰好等于k,则从该数开始,start,end都等于mid
for i in range(mid,-1,-1): # 从中间数开始向前找,找数值等于k的数
if data[i]==k:start=-1 # 每找到一个,start-1
for j in range(mid+1,len(data)): # 向后找同理
if data[j]==k:end+=1
return end-start # 返回end-start就是该数字在数组中的出现次数
elif data[mid]>k: # 接下来是二分查找,递归调用
return self.GetNumberOfK(data[:mid],k)
else:return self.GetNumberOfK(data[mid+1:],k)
39.二叉树的深度
- 题目:输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度
- 思路:自底而上,每当从子节点回退到其父节点时,返回左右子节点中最深的分支值
class Solution:
def TreeDeep(self,pRoot):
if not pRoot:
return 0
# 左子树递归,每次递归一个节点,计数+1,出口在not pRoot
left=self.TreeDeep(pRoot.left)+1
right=self.TreeDeep(pRoot.right)+1
return max(left,right)
40.平衡二叉树
-
题目:输入一棵二叉树,判断该二叉树是否是平衡二叉树
-
思路:
平衡二叉树:它是一颗空树,或者它的左右两个子树的高度差绝对值不超过1,并且左右两个子树都是平衡二叉树
上题求二叉树深度,从下到上,分别判断某节点的左右子树是否是平衡二叉树,即:根据该节点的左右子树高度差判断是否平衡
class Solution:
def IsBalanced_Solution(self,pRoot):
self.flag=True
self.Tree_depth(pRoot)
return self.flag
# 先求出二叉树的深度,然后分别在每个节点判断左右子树是否是平衡二叉树,并改变flag的值
def Tree_depth(self,pRoot):
if not pRoot or self.flag==False:
return 0
left=self.Tree_depth(pRoot.left)+1
right=self.Tree_depth(pRoot.right)+1
if abs(left-right)>1:
self.flag=False
return max(left,right)
41.数组中只出现一次的数字
-
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字
-
思路:哈希表,将数字存入哈希表,键为数字,值为出现次数
解法二:用异或求解。
异或运算:任何一个数字异或他自己都等于0,也就是说,如果数组中只有一个数字出现过一次,其余数字都出现了两次,这样的话,如果从头到尾一次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些出现过两次的数字全部在异或中抵消了。如果能够把原数组分为两个子数组,在每个子数组中,包含一个只出现一次的数字,而其他数字都出现两次,在两个子数组中分别求出这两个只出现一次的数字
还是从头到尾一次异或数组中的每个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果,因为其他数字都出现了两次,在异或中全部抵消。由于这两个数字肯定不同,那么这个异或结果肯定不是0,也就是说,在这个结果数字的二进制表示作用至少就有一位为1,我们在结果数字中找到第一个为1的位的位置,记为第N位。现在我们以第N位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N位都为1,而第二个子数组的每个数字的第N位都为0.现在我们已经把原数组分成了两个数组,每个数组都包含一个只出现一次的数字,而其他数字都出现了两次
class Solution:
def FindNumsAppearOnce(self,array):
if len(array)<2:
return None
dict={}
for ele in array:
dict_arr[ele]=1 if ele not in dict_arr else dict_arr[ele]+1
res=[k for k in dict_arr if dict_arr[k]==1]
return res
----------------------------------------------------------
# 解法二:
def FindNumsApperOnce(self,array):
if len(array)<2:
return None
remain,index=0,1
for num in array:
remain=remain ^ num
while (remain & index)==0:
index=index << 1
res1,res2=0,0
for num in array:
if(num & index)==0:
res1=res1 ^ num
else:
res2=res2 ^ num
return [res1,res2]
42.和为S的连续正数序列
- 题目:小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
- 思路:设定一个大小不固定的窗口来圈住目标列表,如果列表中的数据满足就添加到答案中,否则就动态的移动该滑动窗口
class Solution:
def FindContinuousSequenct(self,tsum):
if tsum<3:
return []
p_low=1
p_high=2
ans=[]
while p_low<p_high:
cur_sum=sum(range(p_low,p_high+1))
if cur_sum==tsum:
ans.append(range(p_low,p_high+1))
# 移动滑动窗口,并寻找下一组解
p_high+=1
elif cur_sun<tsum:
p_high+=1
else:
p_low+=1
return ans
43.和为S的两个数字
- 题目:输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的
- 思路:需要注意的是,输入的数组是已经排序好的,递增的数组,也就是说,如果b>a,且存在a+b=s,(a-m)+(b+m)=s,则(a-m)(b+m)=ab-(b-a)m-m*m<ab,也就是说,外层乘积更小,采用左右夹逼法
class Solution:
defFindNumbersWithSum(self,array,tsum):
start,end=0,len(array)-1
while start<end:
if array[start]+array[end]==tsum:
return array[start],array[end]
elif array[start]+array[end]<tsum:
start+=1
else:end-=1
return []
44.左旋转字符串
- 题目:汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
- 思路:切片,然后加在末尾
class Solution:
def LeftRotateString(self,s,n):
s=list(s)
s=s+s[:n]
return ''.join(s[n:])
45.反转单词顺序列
- 题目:牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
- 思路:反转,再合并
class Solution:
def ReverseSentence(self,s):
s=s.split(' ')
s=reversed(s)
return " ".join(s)
46.扑克牌顺子(未解决)
-
题目:LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0
-
思路:需要满足两个条件
1.除0外没有重复的数
2.max-min<5
class Solution:
def IsContinuous(self,numbers):
min,max,flag=14,-1,0
lf len(numbers)!=5:return False
for num in numbers:
if num==0:continue
if (flag>>num)&1==1:return False
flag=flag | (1<<num)
if num<max:max=num
if num<min:min=num
if max-min>=5:return Flase
return True
47.孩子们的游戏(未解决)
-
题目:每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)如果没有小朋友,请返回-1
-
思路:约瑟夫环
n个人的编号为n ~ n-1,当数到m-1的那个人出列
第一次出列:一开始,所有人的编号排成序列模式为:0,1,2,3,4…n-2,n-1,那么第一次出列的人的编号为(m-1)%n1,那么在第一个人出列后,从他的下一个人又开始从0报数,为方便,设k1=m%n1(n1为当前序列的总人数),那么在第一个人出列后,k1则是下一次新的编号序列的首位元素,那么我们得到的新的编号序列为k1,k1+1,k1+2,…,n-2,n-1,0,1,2,…,k1-3,k1-2(k1-1第一次已出列),那么在这个新的序列中,第一个人依旧是从0开始报数,每人报的相应数字为0,1,2,3,…,n-2,第二次每个人报的相应数字与第一次时自己相应的编号对应起来关系为:
0 -----> k1
1 -----> k1+1
2 -----> k1+2
…
n-2 ----> (k1+n-2)%n1 (n1为当前序列的总人数,因为是循环的序列,k1+n-1可能大于总人数)
那么要解决的就是n-1个人报数的问题,即n-1约瑟夫环的问题
class Solution:
def LastRemaining_Solution(self,n,m):
if n<1:
return -1
res,i=0,2
while i<=n:
res=(res+m)%i
i+=1
return res
48.求1+2+3+…+n
- 题目:求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)
- 思路:不能用那些,但是可以用递归啊!!!!
class Solution:
def Sum_Solution(self,n):
if n==1:
return n
res=n
return res+self.Sum_Solution(n-1)
49.不用加减乘除做加法(未解决)
- 题目:写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号
- 思路:
50.把字符串转换成整数(未解决)
- 题目:将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
- 思路:
51.数组中重复的数字
- 题目:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2
- 思路:哈希表,遍历数组存入哈希表,找出第一个值不是1的数字
class Solution:
def duplicate(self,numbers,duplication):
dict_num={}
for ele in numbers:
dict_num[ele]=1 if ele not in dict_num else dict_num[ele]+1
for ele in dict_num:
if dict_num[ele]!=1:
duplication[0]=ele
return True
return False
52.构建乘积数组
-
题目:给定一个数组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[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];) 好贱啊!!不能用除法,用除法老子分分钟搞定…
-
思路:B[0]=A[1]*…A[n-1] 没有A[0]
B[1]=A[0]*…A[n-1] 没有A[1]
class Solution:
def multiply(self,A):
B=[]
for i in range(len(A)):
temp=A[i]
b=1
for j in range(len(A)):
A[i]=1 # 因为B不要A[i],所以将A[i]置为1,然后连乘解决问题
b*=A[j]
B.append(b)
A[i]=temp
return B
53.正则表达式匹配
-
题目:请实现一个函数用来匹配包括’.‘和’*‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
-
思路:当模式中的第二个字符不是*时:
1.如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的
2.如果字符串第一个字符和模式中的第一个字符不匹配,直接返回False
当模式中的第二个字符是*时
如果字符串第一个字符跟模式第一个字符不相匹配,则模式后移2个字符,继续匹配,如果字符串第一个字符跟模式第一个字符匹配,可以由3中匹配方式:
1.模式后移2个字符,相当于x*被忽略;
2.字符串后移1字符,模式后移2字符;
3.字符串后移1字符,模式不变,即继续匹配字符下一位,因为*可以匹配多位(该种情况被1,2包含)
class Solution:
def match(self,s,pattern):
if s==pattern: # 最好的情况,完全匹配
return True
if len(pattern)>1 and pattern[1]=='*':
if s and (s[0]==pattern[0] or pattern[0]=='.'):
return self.match(s,pattern[2:]) or self.match(s[1:],pattern)
else:
return self.match(s,pttern[2:])
elif s and pattern and (s[0]==pattern[0] or pattern[0]=='.'):
return self.match(s[1:],pattern[1:])
return False
54.表示数值的字符串
- 题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是
- 思路:正则,将所有的可能性搞定
class Solution:
def isNumber(self,s):
import re
pattern=r'^[\+-]?[\d]*(\.[\d]*)?([eE][\+-]?[\d]+)?$'
match=re.match(pattern,s)
return match
55.字符流中第一个不重复的字符
- 题目:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"
- 思路:哈希,找到值为1的那个字符
class Solution:
def __init__(self):
self.s=''
self.dict1={}
def FirstAppearingOnce(self):
for i in self.s:
if self.dict1[i]==1:
return i
return '#'
def Insert(self,char):
self.s+=char
if char in self.dict1:
self.dict1[char]+=1
else:
self.dict1[char]=1
56.链表中环的入口节点
-
题目:给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null
-
思路:首先要明确什么是环
如何确定链表中是否有环:
利用两个速度不同的指针,一个指针一次走一步,另一个指针一次走两步,如果走的快的指针追上了走得慢的指针,说明链表中有环,如果走的慢的指针走到了链表的末尾都还没又被走得快的指针追上,那么链表中没有环
如何找到环的入口节点:
使用两个指针,指针之间的距离为环的长度。也就是说,如果环中有n个节点,则第一个节点先在链表中移动n步,然后两个指针以相同的速度向前移动,当第二个指针指向环的入口节点时,第一个指针也已经走完这个环,并且重新指向入口节点了,那么,我们怎么知道链表中的环有几个节点呢?获取链表里的环中的节点数量是在第一步中实现的,当第一步中快、慢指针相遇后,从这两个指针指向的节点出发,并开始计数,当再次回到这个节点时,就得到了环中的节点数了
class ListNode(object):
def __init__(self,value,next_node=None):
self.value=value
self.next=next_node
def entry_node_of_loop(head):
finded_nodes={}
cur=head
while cur:
# 字典的get()方法,通过键找值,如果相应的键不存在,则返回None
if finded_nodes.get(cur.value)!=1:
finded_nodes[cur.value]=1
else:
return cur
cur=cur.next
return
- Summary:链表一般用到双指针有两种情况一种是两个指针速度一样,但始终间隔一定距离,另一种就是两个指针速度不一样,一快一慢,BTW,如果条件允许,可以使用额外的空间来记录链表,例如字典,因为字典中根据key找value的时间福再度是O(1),所以一般用字典来降低时间复杂度。
57.删除链表中重复的节点(未解决)
- 题目:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
- 思路:直接移动链表的指针到不重复的节点,中间被跳过的节点自然被删除
class Solution:
def deleteDuplication(self,pHead):
first=ListNode(-1)
first.next=pHead
last=first
while pHead and pHead.next:
if pHead.val==pHead.next.val:
val=pHead.val
while pHead and val==pHead.val:
pHead=pHead.next
last.next=pHead
else:
last=pHead
pHead=pHead.next
return first.next
58.二叉树的下一个节点
-
题目:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针
-
思路:中序遍历就是:左,根,右,有三种情况:
1.给定的节点为空,返回空
2.给定的节点有右子树,沿着该右子树,返回右子树的第一个左叶子节点
3.给定的节点没有右子树,如果位于某个节点的左子树中,则上溯直至找到该节点,否则返回空(因为按照中序遍历,“左中右”的遍历方式,该节点没有右子树时,要么遍历完毕,下一个节点为空,要么某个子树的左子树遍历完毕,下一个节点是子树的根节点)
class Solution:
def GetNext(self,pNode):
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 # 沿着父节点向上遍历
return
59.对称的二叉树
- 题目:请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的
- 思路:对比左,右子树的大小,递归
class Solution:
def isSymmetrical(self,pRoot):
if not pRoot:
return True
return self.recursiveTree(pRoot.left,pRoot.right)
def recursiveTree(self,left,right):
if not left and not right:
return True
if not left or not right:
return False
if left.val==right.val:
return self.recursiveTree(left.left,right.right) and self.recursiveTree(left.right,right.left)
return False
60.按之字形顺序打印二叉树
- 题目:请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推
- 思路:用两个栈来实现,利用栈先进后出的特点,两个栈分别存储奇数层和偶数层的树节点,如:根节点进入栈1,栈1中的元素依次出栈,每出栈一个元素,就将该元素的子节点压入栈2,要注意栈2的入栈顺序是先左子节点,后右子节点(栈先进后出,右子节点后入栈,打印的时候才能先出栈1)。栈2中的元素依次出栈,并将元素的子节点压入栈1中,栈1的入栈顺序使先右子节点后左子节点,这样两个栈依次交替,直到两个栈都为空
class Solution:
def print(self,pRoot): 1
if pRoot==None:
return [] 2 3
stack1=[pRoot] # 奇数栈 4 5 6 7
stack2=[] # 偶数栈
result=[]
while stack1 or stack2:
ret1=[] # 用来暂时存储各层节点的值
ret2=[] # 用来暂时存储各层节点的值
while stack1:
node=stack1.pop()
# 先左后右入偶数栈2
if node.left:
stack2.append(node.left)
if node.right:
stack2.append(node.right)
ret1.append(node.val)
# 上述while循环结束后,二叉树的所有节点就都按照先左后右的顺序进入了偶数栈
# 并且各层节点的值都存入了ret1
if len(ret1)!=0:
result.append(ret1)
while stack2:
# 先右后左入栈1
node=stack.pop()
if node.right:
stack1.append(node.right)
if node.left:
stack1.append(node.left)
ret2.append(node.val)
if len(ret2)!=0:
result.append(ret2)
return result
#-----------------------------------------------------------------
# 第一个while循环开始,stack1里面的内容是[root],将其pop出来 node=stack1.pop()
# 经过第一个while循环后stack2里面的内容是[2,3],然后将node里面的内容存入ret1 ret1.append(node.val)
# result.append(ret1) 其实添加的是根节点列表,此时的result里面的内容[[root.val]]
# 然后开始第二个while循环,经过第一个while循环后,stack2里面已经有了内容,将其pop出,注意,这里的pop是尾取,也就是说,先拿[3],然后判断3是否存在右,左子树,并将其放入stack1,在这里需要特别注意,因为奇数层需要反向输出,所以先存入的是右节点,所以经过第二个while循环后,stack1里面的内容[7,6,5,4]
# ret2里面的内容是[3,2],此时result里面的内容[[1],[3,2]]
# 然后最外层循环的判定条件是stack1 or stack2不为空,此时stack1里面仍有内容为[7,6,5,4]
# 一次尾取pop,放入ret1,此时ret1内容为[4,5,6,7],存入result
# 整个循环结束后,result内容为[[1],[3,2],[4,5,6,7]]
61.把二叉树打印成多行
-
题目:从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行
-
思路:二叉树的广度遍历,将每一行节点放入一个列表,append到一个结果列表即可
采用队列,每次头取队列中的节点pop,如果该节点存在左,右子树,则尾加到队列
class Solution:
def Print(self,pRoot):
result=[]
nqueue=[pRoot]
if not pRoot:
return []
while nqueue:
temp=[]
# 这一步的循环是将当前队列的所有元素存入临时列表
for ele in nqueue:
temp.append(ele.val)
# 这一步是最重要的,每次遍历的都是当前队列的长度,保证能将当前队列里的所有节点取出,并将其中节点的左右子节点加入队列
for i in range(len(nqueue)):
node=nqueue.pop(0)
if node.left:
nqueue.append(node.left)
if node.right:
nqueue.append(node.right)
result.append(temp)
return result
62.序列化二叉树
-
题目:请实现两个函数,分别用来序列化和反序列化二叉树
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树
-
思路:根据二叉树的广度遍历和深度遍历的任意一种方式,将二叉树的所有节点变成一个只有值的列表,其中空值用 “#” 表示,然后,再根据序列化的方式,还原二叉树,类似于二叉树的重建(第四题),这里采用先序遍历的方式实现,字符串间用’,‘ 隔开
class Solution:
def Serialize(self,root):
if not root:
return '#'
return str(root.val)+','+self.Serialize(root.left)+','+self.Serialize(root.right)
def Deserialize(self,s):
list=s.split(',')
return self.deserializerTree(list)
def deserializerTree(self,list):
if len(list)<=0:
return None
val=list.pop(0)
root=None
if val!='#':
root=TreeNode(int(val))
root.left=self.deserializerTree(lsit)
root.right=self.deserializerTree(list)
return root
63.二叉搜索树的第K个节点
- 题目:给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4
- 思路:二叉搜索树的中序遍历,0是根节点,然后给一个计数器
class Solution:
count=0
def KthNode(self,pRoot,k):
if not pRoot:
return None
node=self.KthNode(pRoot.left,k)
if node:
return node
self.count+=1
if self.count==k:
return pRoot
node=self.KthNode(pRoot.right,k)
if node:
return node
64.数据流中的中位数
- 题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数
- 思路:排序,对2取余,然后返回
class Solution:
data=[]
def Insert(self,num):
self.data.append(num)
self.data.sort()
def GetMedian(self,data):
length=len(self.data)
if length%2==0:
return self.data[int(length)//2]
else:
return (self.data[length//2]+self.data[length//2-1])/2
65.滑动窗口的最大值
- 题目:给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}
- 类似于有效卷积,只不过找的是窗口里的最大值,可以每次用max()函数找到当前数组里的最大值,然后添加到一个列表中
class Solution:
def maxInWindows(self,num,size):
if size<=0:
return []
res=[]
start,end=0,size-1
while end<len(num):
res.append(max(num[start:end+1]))
start+=1
end+=1
return res
66.矩阵中的路径
-
题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子
a b c e
s f c s
a d e e
矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子
-
思路:回溯算法
0.根据给定数组,初始化一个标志位数组,初始化为false,表示未走过,true表示已经走过,不能走第二次
1.根据行数和列数,遍历数组,先找到一个与path字符串第一个元素相匹配的矩阵元素,进入判断
2.根据 i 和 j 先确定一维数组的位置,因为给定的matrix是一个一维数组
3.确定递归终止条件:越界,当前找到的矩阵值不等于数组对应位置的值,已经走过的,这三类情况,都直接false,说明这条路不通
4.若k,就是待判定的字符串path的索引已经判断到了最后一位,此时说明是匹配成功的
5.递归不断地寻找周围四个格子是否符合条件,只要有一个格子符合条件,就继续再找这个符合条件的格子的四周是否存在符合条件的格子,直到 k 达到末尾或者不满足递归条件就停止
6.走到这一步,说明本次是不成功的,我们要还原一下表示位数组index处的标志位,进入下一轮判断
class Solution:
def hasPath(self,matrix,rows,cols,path):
# 首先需要明确,输入的矩阵有rows行,cols列
if not matrix:
return False
if not path:
return True
# 创建一个列表,列表中的每个元素是矩阵的每一行,也就是[[x,x,x,x],[x,x,x,x]]的形式
x=[list(matrix[cols*i:cols*i+cols] for i in range(rows))]
# 用一个双for循环遍历整个矩阵,判断矩阵的每个元素是否是path中的元素
for i in range(rows):
for j in range(cols):
# 将 x,i,j,path传入函数,路径是否存在
if self.exits_helper(x,i,j,path):
return True
return False
# 定义一个函数,用来找矩阵中是否存在路径,主要思想是递归
def exist_helper(self,matrix,i,j,p):
# 如果矩阵的第一个元素就是路径的开头,且路径中只有一个元素,返回True
if matrix[i][j]==p[0]:
if not p[1:]:
return True
# 讨论路径不止一个元素的情况,分四种情况
matrix[i][j]=''
if i > 0 and self.exist_helper(matrix,i-1,j,p[1:]):
return True
if i < len(matrix)-1 and self.exist_helper(matrix,i+1,j,p[1:]):
return True
if j > 0 and self.exist_helper(matrix,i,j-1,p[1:]):
return True
if j < len(matrix[0])-1 and self.exist_helper(matrix,i,j+1,p[1:]):
return True
matrix[i][j]=p[0]
return False
else:return False
67.机器人的运动范围(未解决)
- 题目:地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子
- 思路:
68.剪绳子
- 题目:给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]xk[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18
- 思路:显然我们不希望最后的绳子有1存在,也就是说,希望绳子的长度>1,假设现在绳子的长度都是由2,3组成,3米的绳子是否还需要再剪一刀变成2米,不用!!因为3米的绳子剪过后,多出1米,对乘法结果来说没有作用,并且n>1,也就是说,最少要剪2刀,所以我们的最终目的是在这根绳子中尽可能的多剪出3米的绳子,直到绳子的总长度<=4米
class Solution:
def cutRope(self,number):
if number<2:
return 0
if number==2:
return 1
if number==3:
return 2
count=0
while number>4:
count+=1
number-=3
return 3**count*number
# 将 x,i,j,path传入函数,路径是否存在
if self.exits_helper(x,i,j,path):
return True
return False
# 定义一个函数,用来找矩阵中是否存在路径,主要思想是递归
def exist_helper(self,matrix,i,j,p):
# 如果矩阵的第一个元素就是路径的开头,且路径中只有一个元素,返回True
if matrix[i][j]==p[0]:
if not p[1:]:
return True
# 讨论路径不止一个元素的情况,分四种情况
matrix[i][j]=''
if i > 0 and self.exist_helper(matrix,i-1,j,p[1:]):
return True
if i < len(matrix)-1 and self.exist_helper(matrix,i+1,j,p[1:]):
return True
if j > 0 and self.exist_helper(matrix,i,j-1,p[1:]):
return True
if j < len(matrix[0])-1 and self.exist_helper(matrix,i,j+1,p[1:]):
return True
matrix[i][j]=p[0]
return False
else:return False
## 67.机器人的运动范围(未解决)
* 题目:地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子
* 思路:
## 68.剪绳子
* 题目:给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],...,k[m]。请问k[0]xk[1]x...xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18
* 思路:显然我们不希望最后的绳子有1存在,也就是说,希望绳子的长度>1,假设现在绳子的长度都是由2,3组成,3米的绳子是否还需要再剪一刀变成2米,不用!!因为3米的绳子剪过后,多出1米,对乘法结果来说没有作用,并且n>1,也就是说,最少要剪2刀,所以**我们的最终目的是在这根绳子中尽可能的多剪出3米的绳子,直到绳子的总长度<=4米**
~~~python
class Solution:
def cutRope(self,number):
if number<2:
return 0
if number==2:
return 1
if number==3:
return 2
count=0
while number>4:
count+=1
number-=3
return 3**count*number