Date : 2019-08-07
1. 反转链表 (考查知识点:链表)
题目描述
输入一个链表,反转链表后,输出新链表的表头。
分析思路:本题实际上只需要打印出最后一个元素即可。但是同时需要进行链表的反转,则可以在反转的时候直接打印最后遍历的一个元素。反转主要的思想是:首先设置初始Last = None, 然后当pHead非空时,进行如下操作:i) 保存pHead.next到tmp,tmp=pHead.next; ii) 将pHead.next = last(链表的顺序反转); iii) 然后往后走更新last= pHead 且pHead = tmp.
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# write code here
if not pHead or not pHead.next:
return pHead
last = None
while pHead:
tmp = pHead.next
pHead.next = last
last = pHead
pHead = tmp
return last
2. 合并两个排序的链表 (考察知识点:链表)
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
我的想法是首先根据现有的两个链表建立合并后的链表的表头(需要判断四种情况:p1 p2均为空,p1 p2均非空,p1非空,p2非空四种情况进行链表头的建立),然后后续同样进行后续四种情况进行比较,得到head.next.但是这样的情况没有通过;
通过的代码所给出的:没有通过四种情况进行比较建立链表表头,而是直接初始化一个,然后四种情况进行判断得到后续的元素。
# version of myself python2.7 没有通过 难道是表头的建立方式有问题。
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
if not pHead1 and not pHead2: # p1 p2均空
return None
elif pHead1 and pHead2: # p1 p2 均非空
if pHead1.val <= pHead2.val:
head = pHead1
pHead1 = pHead1.next
else:
head = pHead2
pHead2 = pHead2.next
elif pHead1: # p1 非空
head = pHead1
pHead1 = pHead1.next
elif pHead2: # p2 非空
head = pHead2
pHead2 = pHead2.next
cur = head
while pHead1 and pHead2:
if pHead1.val <= pHead2.val:
head.next = pHead1
head = head.next
pHead1 = pHead1.next
else:
head.next = pHead2
head = head.next
pHead2 = pHead2.next
if pHead1:
head.next = pHead1
elif pHead2:
head.next = pHead2
else:
return cur.next
# 已修正:已经通过,是因为最后的return写错了
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
if not pHead1 and not pHead2: # p1 p2均空
return None
elif pHead1 and pHead2: # p1 p2 均非空
if pHead1.val <= pHead2.val:
head = pHead1
pHead1 = pHead1.next
else:
head = pHead2
pHead2 = pHead2.next
elif pHead1: # p1 非空
head = pHead1
pHead1 = pHead1.next
elif pHead2: # p2 非空
head = pHead2
pHead2 = pHead2.next
cur = head
while pHead1 and pHead2:
if pHead1.val <= pHead2.val:
head.next = pHead1
pHead1 = pHead1.next
else:
head.next = pHead2
pHead2 = pHead2.next
head = head.next # 在这一部分的整体结尾处,进行head的更新
if pHead1:
head.next = pHead1
elif pHead2:
head.next = pHead2
return cur
# 牛客上其他人的版本:
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合并后列表
def Merge(self, pHead1, pHead2):
# write code here
cur = head = ListNode(0)
while pHead1 and pHead2:
if pHead1.val <= pHead2.val:
head.next = pHead1
pHead1 = pHead1.next
else:
head.next = pHead2
pHead2 = pHead2.next
head = head.next
head.next = pHead1 or pHead2
return cur.next
3. 树的子结构(考察知识: 树)(二叉树B是否为树A的子结构)
题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
牛客上有人repo的思路:
# -*- 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):
# write code here
result = False
if pRoot1 != None and pRoot2 != None:
if pRoot1.val == pRoot2.val:
result = self.DoesTree1haveTree2(pRoot1, pRoot2)
if not result:
result = self.HasSubtree(pRoot1.left, pRoot2)
if not result:
result = self.HasSubtree(pRoot1.right, pRoot2)
return result
# 用于递归判断树的每个节点是否相同
# 需要注意的地方是: 前两个if语句不可以颠倒顺序
# 如果颠倒顺序, 会先判断pRoot1是否为None, 其实这个时候pRoot2的结点已经遍历完成确定相等了, 但是返回了False, 判断错误
def DoesTree1haveTree2(self, pRoot1, pRoot2):
if pRoot2 == None:
return True
if pRoot1 == None:
return False
if pRoot1.val != pRoot2.val:
return False
return self.DoesTree1haveTree2(pRoot1.left, pRoot2.left) and self.DoesTree1haveTree2(pRoot1.right, pRoot2.right)
思路:
1) 首先需要判断树A中是否有与树B的根节点一样的节点。(此处需要递归进行遍历树A)
2)如果树A中存在R节点,则判断A中以R节点为根节点的子树是否与B同结构(此处还需要递归进行遍历树B和以R为根节点的子树,判断其左右孩子节点是否相等,递归的终止条件是我们达到了树R或者树B的叶节点。 )。
# my version:
# -*- 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):
# write code here
result = False
if pRoot1 != None and pRoot2 != None:
if pRoot1.val == pRoot2.val:#如果存在相等的节点,则进入判断是否子树结构相等的函数体
result = self.is_subtree(pRoot1, pRoot2)
if not result: # 两棵树非空,且当前节点不相等,则向下遍历,直到相等存在
result = self.HasSubtree(pRoot1.left, pRoot2) or self.HasSubtree(pRoot1.right, pRoot2)
return result
def is_subtree(self, A, B):
if not B: # 如果上一B.left或者right为空了,则判断终止。
return True
if not A or A.val != B.val: # 如果B非空,但A非空或者AB相应值不等时,则不存在!
return False
return self.is_subtree(A.left,B.left) and self.is_subtree(A.right, B.right) #否则向下进行递归判断,直到进入上方的两个条件进行终止。
4. 二叉树的镜像 (考察知识:树+递归算法) 给出源树,返回相应树的镜像树。
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述:
二叉树的镜像定义:源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5
思路:之前没有接触过树的镜像,遇到有点懵,分析一下:例如根节点8,将节点8的左孩子和右孩子进行交换,直接利用自动打包和分配技术即可实现root.lfet, root.right = root.right, root.left.然后向下遍历左孩子和右孩子,同样进行相应左孩子和右孩子的交换。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回镜像树的根节点
def Mirror(self, root):
# write code here
if root == None:
return None
else:
root.left, root.right = root.right, root.left
self.Mirror(root.left)
self.Mirror(root.right)
return root
网上关于树的镜像的完整版代码:
class Solution:
# 给定一个二叉树,获得其镜像(轴对称)的镜像二叉树:
# 方式1:生成新的镜像二叉树
def getMirrorBST(self, root):
if root == None:
return
newTree = treeNode(root.val)
newTree.right = self.getMirrorBST(root.left)
newTree.left = self.getMirrorBST(root.right)
return newTree
# 方式2:改变给定的二叉树为镜像二叉树
def turnToMirror(self, root):
if root == None:
return
root.right, root.left = root.left, root.right
self.turnToMirror(root.left)
self.turnToMirror(root.right)
return root
# 给定二叉树的前序遍历和中序遍历,获得该二叉树
def getBSTwithPreTin(self, pre, tin):
if len(pre)==0 | len(tin)==0:
return None
root = treeNode(pre[0])
for order,item in enumerate(tin):
if root .val == item:
root.left = self.getBSTwithPreTin(pre[1:order+1], tin[:order])
root.right = self.getBSTwithPreTin(pre[order+1:], tin[order+1:])
return root
class treeNode:
def __init__(self, x):
self.left = None
self.right = None
self.val = x
if __name__ == '__main__':
flag = "turnToMirror"
solution = Solution()
preorder_seq = [1, 2, 4, 7, 3, 5, 6, 8]
middleorder_seq = [4, 7, 2, 1, 5, 3, 8, 6]
treeRoot1 = solution.getBSTwithPreTin(preorder_seq, middleorder_seq)
if flag == "mirrorBST":
newRoot = solution.getMirrorBST(treeRoot1)
print(newRoot)
if flag == "turnToMirror":
solution.turnToMirror(treeRoot1)
print(treeRoot1)
5. 顺时针打印矩阵 (考察知识点:数组)
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下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.
思路解析: 先取第一行元素,同时将原矩阵中的第一行数据删除;将剩下的矩阵进行逆时针旋转90度,再打印第一行元素并删除;再逆时针旋转剩下的矩阵90度,打印第一行并删除,……直到剩下的矩阵为空矩阵(数组)
# -*- coding:utf-8 -*-
class Solution:
# matrix类型为二维列表,需要返回列表
def printMatrix(self, matrix):
# write code here
new_matrix = []
while matrix:
new_matrix += matrix.pop(0)#矩阵以二维数组的形式存储,则第一个元素即第一行,同时删除
if matrix:
matrix = self.rotate(matrix) # 对剩下的矩阵进行逆时针旋转90度
return new_matrix
def rotate(self, matrix): # 矩阵旋转可以具体矩阵例子进行分析。
row = len(matrix)
col = len(matrix[0])
new_matrix = []
for i in range(col):
new_line = []
for j in range(row):
new_line.append(matrix[j][col-1-i])
new_matrix.append(new_line)
return new_matrix
网上完整例子的代码:
# coding:utf-8
class Solution(object):
def printMatrix(self, matrix):
# 打印矩阵
result = []
while matrix:
result += matrix.pop(0)
if matrix:
matrix = self.rotate(matrix)
return result
def rotate(self, matrix):
# 逆时针旋转矩阵
row = len(matrix)
col = len(matrix[0])
# 存放旋转后的矩阵
new_matrix = []
# 行列调换
for i in range(col):
new_line = []
for j in range(row):
new_line.append(matrix[j][col-1-i])
new_matrix.append(new_line)
return new_matrix
if __name__ == '__main__':
# 测试代码
matrix = [
[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]
]
solution = Solution()
result = solution.printMatrix(matrix)
print(result)
6. 包含min函数的栈 (考察知识点:栈)
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
分析:要实现查找的O(1)的复杂度,首先想到的是列表(便于查找和索引,但不适合插入和删除任意元素)进行存储和索引。因此自然就利用列表(数组)进行栈的存储,注意的是栈是先进后出的数据结构。l.pop()返回的是最后一个元素,并同时删除l的最后一个元素。
# -*- coding:utf-8 -*-
class Solution:
l = []
def push(self, node): # 给目前的栈增添元素
# write code here
self.l.append(node)
def pop(self): # 删除刚进入栈的元素,并返回该元素
# write code here
return self.l.pop()
def top(self): # 用作返回栈的顶端元素,最后进的元素
# write code here
return self.l[-1]
def min(self): # 对栈中的元素进行求最小值
# write code here
return min(self.l)
# 如果上述中所有的带有self.l的地方删除self,则不能通过测试。全局变量的问题,使得l成为了Solution的数据成员。
7. 栈的压入、弹出序列 (考察知识:栈)
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
分析思路:判断某一序列是否是一个栈的输出序列也常作为选择题作为考擦,本题的思考角度是:
i) 判断是否有进行函数输出的必要:如果pushV为空或者两个序列的长度不一致,直接给出不能返回popV序列的结果。
ii) 建立模拟栈stack, 首先将两个序列的顶端元素进行判断,是否相等?如果相等,不需要压入模拟栈,直接两个都进行pop(0);
如果不相等,则退而求其次的判断已经压入模拟栈的顶端元素和popV[0]元素是否相等,如果相等,两者分别弹出相应的元素;
如果仍不相等(此时:popv[0]和pushV以及模拟栈stack中的顶端元素都不相等(pushV[0]、stack[-1])),则继续将pushV中的元素压入模拟栈(需要pushV非空)。
如果以上都不能满足,则返回false,不是相应的弹出序列。
否则在最后return True !
# -*- coding:utf-8 -*-
class Solution:
def IsPopOrder(self, pushV, popV):
# write code here
if not pushV or len(pushV)!=len(popV): # 如果pushV为空或者两个序列的长度不一致,则返回False
return 0
stack = [] # 模拟栈,用于暂时存放不满足弹出条件的元素,相当于先压入栈
while popV:
if pushV and pushV[0] == popV[0]: # 如果直接判断两个栈顶的元素相等,则满足弹出条件,直接弹出两个栈的栈顶元素
pushV.pop(0)
popV.pop(0)
elif stack and stack[-1] == popV[0]: # 如果栈顶元素不满足,则判断已经压入模拟栈的元素顶的元素是否相等,相等则弹出
stack.pop()
popV.pop(0)
elif pushV: #否则将pushV中的元素压入模拟栈内
stack.append(pushV.pop(0))
else: # 如果将所有pushV的元素压入模拟栈stack,仍没有找到相应的元素,则false
return False
return True
8. 从上往下打印二叉树 (考察知识点:树和队列)
题目描述
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
分析思路:根据题意可以得出必须要额外的建立list,来进行处理。在实际情况中,不仅仅只需要建立一个list,需要建立两个list,一个list l用作存放打印顺序的结果,另一个list表示一个队列(先进先出):用于不断的压入放入l中的左右孩子节点。q.pop(0)和后续的q.append(t.left)和q.append(t.right)保证了:在不断进行压入节点到队列q中时,压入下一层时,是先压入该层的左孩子的左右孩子,然后再压入该层的右孩子的左右孩子。
# -*- 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):
# write code here
if not root: # 首先判断是否有必要进行下面的函数体,如果树为空没有必要。
return []
l = [] # 用于存放打印顺序的结果list
q = [root] # 用于存放不断从上往下,从左往右压入左右孩子的队列(先进先出,所以是pop(0))
while len(q):
t = q.pop(0) # 先进先出的队列q
l.append(t.val)
if t.left: # 先压入左孩子,这样可以保证同一层在打印时,是先打印左边的。
q.append(t.left)
if t.right: # 再压入右孩子,保证了:下一层进行压入时,先压入了左孩子的左右孩子,然后再压入右孩子的左右孩子。
q.append(t.right)
return l
9. 二叉搜索树的后序遍历序列 (考察知识点:栈和树)
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
分析思路:
BST的后序序列的合法序列是,对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列。完美的递归定义 : ) 。
即如下操作:首先根节点(最后一个元素)是大于所有左子树的元素,小于右子树的元素;后序:(左右中)。找到第一个大于根节点的元素,其是左右子树的分割;再判断右子树是否合法(全部大于root的值);然后再判断左右子树是否合法(递归调用)。注意:左右子树递归判断时的条件。
class Solution:
def VerifySquenceOfBST(self, sequence):
# write code here
if sequence==None or len(sequence)==0:
return False
length=len(sequence)
root=sequence[length-1]
# 在二叉搜索 树中 左子树节点小于根节点,如果出现了一个节点的值大于root,则应该是右子树的节点
for i in range(length):
if sequence[i]>root:
break
# 二叉搜索树中右子树的节点都大于根节点,右子树的节点都应该大于root,如果出现了小于root的节点,则该右子树不合法
for j in range(i,length):
if sequence[j]<root:
return False
# 判断左子树是否为二叉树,判断左子树是否合法
left=True
if i>0:
left=self.VerifySquenceOfBST(sequence[0:i])
# 判断 右子树是否为二叉树,判断右子树是否合法
right=True
if i<length-1:
right=self.VerifySquenceOfBST(sequence[i:-1])
return left and right