剑指offer刷题-3
一个链表中包含环,请找出该链表的环的入口结点。
使用一个列表保存遍历过的节点,遍历单链表判断是否在列表中。
class Solution:
def EntryNodeOfLoop(self, pHead):
# 遍历链表,环的存在,遍历遇见的第一个重复的即为入口节点
tempList = []
if not pHead or not pHead.next:
return None
node = pHead
while node:
if node in tempList:
return node
else:
tempList.append(node)
node = node.next
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
首先判断边界条件,当满足条件时,判断下当前节点的下一个节点是否等于当前节点,不等于的话递归继续得到下一个节点,当等于时,循环直到等于空或者不等于当前节点值,使当前节点的下一个节点指向不等于当前节点的节点。重复判断下一个节点是否等于当前节点
class Solution:
def deleteDuplication(self, pHead):
if pHead is None or pHead.next is None:
return pHead
head1 = pHead.next
# 如果下一个节点不等于当前节点
if head1.val != pHead.val:
# 递归得到下一个节点
pHead.next = self.deleteDuplication(pHead.next)
else:
# 如果下一个节点等于当前节点并且下一个节点不为空
while pHead.val == head1.val and head1.next is not None:
# 使下一个节点为下下个节点
head1 = head1.next
if head1.val != pHead.val:
pHead = self.deleteDuplication(head1)
else:
return None
return pHead
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
分析中序遍历的特点,判断当前是否有左右子树,当有右子树时,则找出右子树的最左节点。当没右子树,则找第一个当前节点是父节点左孩子的节点
class Solution:
def GetNext(self, pNode):
if not pNode:
return pNode
# 如果有右子树,则找右子树的最左节点
if pNode.right:
left1 = pNode.right
while left1.left:
left1 = left1.left
return left1
# 没右子树,则找第一个当前节点是父节点左孩子的节点
while pNode.next:
tmp = pNode.next
if tmp.left == pNode:
return tmp
pNode = tmp
# 退到了根节点仍没找到,则返回null
return None
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
创建一个新方法,传入两颗数,分别判断头节点和左右子树的边界条件,然后递归判断当前左子树和右子树是否为对称的
class Solution:
def isSymmetrical(self, pRoot):
return self.isSymBT(pRoot, pRoot)
def isSymBT(self, tree1, tree2):
if tree1 is None and tree2 is None:
return True
if tree1 is None or tree2 is None:
return False
if tree1.val != tree2.val:
return False
return self.isSymBT(tree1.left, tree2.right) and self.isSymBT(tree1.right, tree2.left)
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
分别使用列表来保存遍历过的节点,下一层节点,结果。并且在设置一个标识符来判断当前应该是从左到右遍历还是从右向左遍历。
class Solution:
def Print(self, pRoot):
root = pRoot
if not root:
return []
level = [root]
# 使用一个列表保存结果
result = []
# 使用一个标识符标识左右遍历顺序
left_to_right = False
while level:
# 保存当前节点值的列表
cur_values = []
# 保存下一层节点列表
next_level = []
# 遍历层级节点
for i in level:
# 将当前遍历的节点值保存在当前节点值列表中
cur_values.append(i.val)
# 判断当前节点是否有左右子树,依次添加到下一层节点列表
if i.left:
next_level.append(i.left)
if i.right:
next_level.append(i.right)
# 判断当前从左到右遍历还是从右到左遍历
if left_to_right:
cur_values.reverse()
# 将遍历过节点值放入总结果列表中
if cur_values:
result.append(cur_values)
# 将下一层节点付给当前层
level = next_level
# 将标识符倒置
left_to_right = not left_to_right
return result
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
和上一题一样的思路,但是比之前少了一个顺序标识符,只需要从左到右遍历即可
class Solution:
# 返回二维列表[[1,2],[4,5]]
def Print(self, pRoot):
if not pRoot:
return []
result = []
root = pRoot
level = [root]
while level:
cur_value = []
next_level = []
for i in level:
cur_value.append(i.val)
if i.left:
next_level.append(i.left)
if i.right:
next_level.append(i.right)
if cur_value:
result.append(cur_value)
level = next_level
return result
请实现两个函数,分别用来序列化和反序列化二叉树
序列化,就是将整个二叉树转换为字符串,这里将空节点转为‘#’每个节点之间使用‘,’分割
反序列化,将序列化后的字符串创建一个二叉树
均使用递归解决,注意边界条件
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
flag = -1
def Serialize(self, root):
"""
对于序列化:使用前序遍历,递归的将二叉树的值转化为字符,并且在每次二叉树的结点
不为空时,在转化val所得的字符之后添加一个' , '作为分割。对于空节点则以 '#' 代替
:param root:
:return:
"""
if not root:
return '#'
return str(root.val) + ',' + self.Serialize(root.left) + ',' + self.Serialize(root.right)
def Deserialize(self, s):
"""
对于反序列化:按照前序顺序,递归的使用字符串中的字符创建一个二叉树
:param s:
:return:
"""
self.flag += 1
l = s.split(',')
if self.flag >= len(s):
return None
root = None
if l[self.flag] != '#':
root = TreeNode(int(l[self.flag]))
root.left = self.Deserialize(s)
root.right = self.Deserialize(s)
return root
给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
首先根据二叉搜索树的特点得到整个节点的有序列表,然后在节点中取出相应节点即可
class Solution:
# 返回对应节点TreeNode
def KthNode(self, pRoot, k):
if pRoot is None or k == 0:
return None
n = self.isorder(pRoot)
if len(n) < k:
return None
else:
# 返回第k个节点
return n[k - 1]
def isorder(self, pRoot):
re = []
if not pRoot:
return None
if pRoot.left:
# 如果左子树存在,递归得到所有子节点放入列表中
re.extend(self.isorder(pRoot.left))
# 将根节点放入列表中
re.append(pRoot)
if pRoot.right:
# 如果右子树存在,递归得到所有子节点放入列表中
re.extend(self.isorder(pRoot.right))
return re
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
在插入时,将其插入列表中并排序,然后根据奇数偶数求中位数
class Solution:
x = []
def Insert(self, num):
# 将数字添加到列表中并排序
self.x.append(num)
self.x.sort()
def GetMedian(self, x):
# 得到长度
n = len(self.x)
# 判断奇数偶数
if n % 2 == 1:
return self.x[n // 2]
else:
return (self.x[n // 2 - 1] + self.x[n // 2]) / 2.0
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
首先判断边界条件,然后使用一个列表保存最大值,根据滑动的特点,每次将其向其向右移动,并求最大值,将其加入
class Solution:
def maxInWindows(self, num, size):
if size == 0 or size > len(num):
return []
max_num = []
for i in range(len(num) - size + 1):
max_num.append(max(num[i:i + size]))
return max_num
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)
本题由于有了负数的影响,在求序列之和时,会产生一些麻烦,最简单的思路,就是分别求出子序列的和并保存,最后得到最大的子序列之和,为了排除负数的影响,将值改为0即可。
class Solution:
def FindGreatestSumOfSubArray(self, array):
if len(array) <= 0:
return []
temp_sum = 0
list_sum = []
for i in array:
# 将当前值累加
temp_sum = temp_sum + i
# 将当前的和放入列表中
list_sum.append(temp_sum)
# 如果当前和是大于0的,继续遍历
if temp_sum > 0:
continue
# 否则将当前和赋值为0,避免负数影响
else:
temp_sum = 0
# 得到和列表中的最大和值
return max(list_sum)
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。 输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出 。 即输出P%1000000007 输入描述: 题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
先将原序列排序,然后从排完序的数组中取出最小的,它在原数组中的位置表示有多少比它大的数在它前面,每取出一个在原数组中删除该元素,保证后面取出的元素在原数组中是最小的,这样其位置才能表示有多少比它大的数在它前面,即逆序对数
class Solution:
def InversePairs(self, data):
"""
先将原序列排序,然后从排完序的数组中取出最小的,
它在原数组中的位置表示有多少比它大的数在它前面,
每取出一个在原数组中删除该元素,保证后面取出的元素在原数组中是最小的,
这样其位置才能表示有多少比它大的数在它前面,即逆序对数
:param data:
:return:
"""
count = 0
copy = []
for i in data:
copy.append(i)
copy.sort()
for i in range(len(copy)):
count += data.index(copy[i])
data.remove(copy[i])
return count % 1000000007
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的运气如何。为了方便起见,你可以认为大小王是0。
先统计王的数量,再把牌排序,如果后面一个数比前面一个数大于1以上,那么中间的差值就必须用王来补了。看王的数量够不够,如果够就返回true,否则返回false。
class Solution:
def IsContinuous(self, numbers):
# 如果列表为空
if not numbers:
return numbers
# 将列表中大于0的元素生成新列表
new_list = [i for i in numbers if i > 0]
# 排序
new_list.sort()
# 如果新列表长度为1
if len(new_list) == 1:
return True
n = 0
# 遍历列表
for j in range(len(new_list) - 1):
# 如果后一个元素减去前一个元素大于0
if (new_list[j + 1] - new_list[j]) > 0:
# 将加入他们的差
n += (new_list[j + 1] - new_list[j])
else:
return False
if n <= 4:
return True
else:
return False
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数….这样下去….直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
将n个小朋友抽象成一个成环的列表,使用取模的方式求出当前m的索引值,然后弹出该索引上的元素,返回列表中的第一个元素。
class Solution:
def LastRemaining_Solution(self, n, m):
if not m or not n:
return -1
# 将n个小朋友索引转为列表
res = range(n)
i = 0
# 当列表长度大于1
while len(res) > 1:
# 由于是环,可以用取模的方式得到m的索引
i = (m + i - 1) % len(res)
# 移除i位置的元素
res.pop(i)
# 返回第一个元素
return res[0]
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串”bcced”的路径,但是矩阵中不包含”abcb”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
首先,在矩阵中任选一个格子作为路径的起点。如果路径上的第i个字符不是ch,那么这个格子不可能处在路径上的第i个位置。如果路径上的第i个字符正好是ch,那么往相邻的格子寻找路径上的第i+1个字符。除在矩阵边界上的格子之外,其他格子都有4个相邻的格子。重复这个过程直到路径上的所有字符都在矩阵中找到相应的位置。由于回朔法的递归特性,路径可以被开成一个栈。当在矩阵中定位了路径中前n个字符的位置之后,在与第n个字符对应的格子的周围都没有找到第n+1个字符,这个时候只要在路径上回到第n-1个字符,重新定位第n个字符。由于路径不能重复进入矩阵的格子,还需要定义和字符矩阵大小一样的布尔值矩阵,用来标识路径是否已经进入每个格子。当矩阵中坐标为(row,col)的格子和路径字符串中相应的字符一样时,从4个相邻的格子(row,col-1),(row-1,col),(row,col+1) 以及(row+1,col)中去定位路径字符串中下一个字符如果4个相邻的格子都没有匹配字符串中下一个的字符,表明当前路径字符串中字符在矩阵中的定位不正确,我们需要回到前一个,然后重新定位。一直重复这个过程,直到路径字符串上所有字符都在矩阵中找到合适的位置
class Solution:
def hasPath(self, matrix, rows, cols, path):
"""
首先,在矩阵中任选一个格子作为路径的起点。如果路径上的第i个字符不是ch,那么这个格子不可能处在路径上的
第i个位置。如果路径上的第i个字符正好是ch,那么往相邻的格子寻找路径上的第i+1个字符。
除在矩阵边界上的格子之外,其他格子都有4个相邻的格子。重复这个过程直到路径上的所有字符都在矩阵中找到相应的位置。
由于回朔法的递归特性,路径可以被开成一个栈。当在矩阵中定位了路径中前n个字符的位置之后,
在与第n个字符对应的格子的周围都没有找到第n+1个字符,这个时候只要在路径上回到第n-1个字符,重新定位第n个字符。
由于路径不能重复进入矩阵的格子,还需要定义和字符矩阵大小一样的布尔值矩阵,用来标识路径是否已经进入每个格子。
当矩阵中坐标为(row,col)的格子和路径字符串中相应的字符一样时,从4个相邻的格子(row,col-1),(row-1,col),(row,col+1)
以及(row+1,col)中去定位路径字符串中下一个字符如果4个相邻的格子都没有匹配字符串中下一个的字符,
表明当前路径字符串中字符在矩阵中的定位不正确,我们需要回到前一个,然后重新定位。
一直重复这个过程,直到路径字符串上所有字符都在矩阵中找到合适的位置
:param matrix:
:param rows:
:param cols:
:param path:
:return:
"""
# 遍历行列
for i in range(rows):
for j in range(cols):
# 如果矩阵中的对应元素等于路径的第一个元素
if matrix[i * cols + j] == path[0]:
if self.find(list(matrix), rows, cols, path[1:], i, j):
return True
def find(self, matrix, rows, cols, path, i, j):
"""
四个方向依次递归判断
:param matrix:
:param rows:
:param cols:
:param path:
:param i:
:param j:
:return:
"""
if not path:
return True
matrix[i * cols + j] = '0'
if j + 1 < cols and matrix[i * cols + (j + 1)] == path[0]:
return self.find(list(matrix), rows, cols, path[1:], i, j + 1)
elif j - 1 >= 0 and matrix[i * cols + (j - 1)] == path[0]:
return self.find(list(matrix), rows, cols, path[1:], i, j - 1)
if i + 1 < rows and matrix[(i + 1) * cols + j] == path[0]:
return self.find(list(matrix), rows, cols, path[1:], i + 1, j)
if i - 1 >= 0 and matrix[(i - 1) * cols + j] == path[0]:
return self.find(list(matrix), rows, cols, path[1:], i - 1, j)
else:
return False
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
将每次遍历过的格子使用字典记录下来,编写一个递归函数,递归判断当前遍历的格子向上下左右四个方向,在递归函数中还需判断各种边界条件
class Solution:
def __init__(self):
# 使用一个字典存储行列为键,值为1的键值对
self.vis = {}
def movingCount(self, threshold, rows, cols):
return self.moving(threshold, rows, cols, 0, 0)
def moving(self, threshold, rows, cols, row, col):
# 计算行坐标和列坐标的数位之和是否大于
if row / 10 + row % 10 + col / 10 + col % 10 > threshold:
return 0
# 判断开始的行列是否大于总的行列
if row >= rows or col >= cols or row < 0 or col < 0:
return 0
# 判断行列是否在字典中
if (row, col) in self.vis:
return 0
# 将当前遍历的行列存入字典中
self.vis[(row, col)] = 1
# 将四个方向递归并且加上开始的第一个格子
return 1 + self.moving(threshold, rows, cols, row - 1, col) \
+ self.moving(threshold, rows, cols, row + 1, col) \
+ self.moving(threshold, rows, cols, row, col - 1) \
+ self.moving(threshold, rows, cols, row, col + 1)