剑指offer-Python3版(二)
重建二叉树
Q: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
这个根据先根中根遍历拿到二叉树的问题,以前在学数据结构的时候经常遇到,我给个实例看一下整个流程是怎么走的,方便理解。
前序 G D A F E M H Z
中序 A D E F G H M Z
树: ?
一般来说,前序遍历的首个结点就是整个树的根结点,就比如说这个例子,你看到前序的第一个字母G ,就是整个树的根结点,(前中后遍历的顺序:反正就是根的顺序在动,比如中序,就是先读左子树 再读根结点 最后读右子树,后序,就是左->右->根这么读),根据中序遍历,这样G的左边(ADEF)都是左子树的结点,右边(HMZ)都是右子树的结点,
这样有个大概的概念
like this:
G
/ \
ADEF HMZ
下面的工作 就是上面的重复工作
先看左边
前序这边G后面是D,按照前序的逻辑,读完根结点就要读左子树,那D就是左子树的根结点,再看中序ADEF,按照中序的逻辑,D把A和EF分割成左右子树
like this:
G
/ \
D HMZ
/ \
A EF
那EF怎么放? 前序是FE 中序是EF
那 F就是根结点了,E怎么放?
F F
/ 还是 \
E E
按照根左右的逻辑,E应该是在F结点的左孩子结点上
like this:
G
/ \
D HMZ
/ \
A F
/
E
再看右边 中序 HMZ 前序 MHZ
你知道了,先找根结点,没有错,就是M,那HZ怎么放?
中序 M 把 H和Z 分了左右子树 那就行啦
like this:
M
/ \
H Z
最后
G
/ \
D M
/ \ / \
A F H Z
/
E
看到这里,对整个流程应该了解了吧。
see see code
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def reBinaryTree(self,pre,tin):
if len(pre)==0:
return None
if len(pre)==1:
return TreeNode(pre[0])
else:
rootnode = TreeNode(pre[0])
rootnode.left = self.reBinaryTree(pre[1:tin.index(pre[0])+1],tin[:tin.index(pre[0])])
rootnode.right = self.reBinaryTree(pre[tin.index(pre[0])+1],tin[tin.index(pre[0])+1:])
return rootnode
2个栈实现队列
Q:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
知道栈先进后出,队列先进先出就没啥问题
思路 就是用2个栈实现先进先出
当 stack2 是空的
stack1 push进来的是这个情况
4 |
---|
3 |
2 |
1 |
你要 输出1,2,3,4 那就把再把stack1的数字加到栈2里面,就像这样
stack2
1 |
---|
2 |
3 |
4 |
你再pop 就完了
要是stack2不是空的,直接pop
class Solution:
"""
push 就直接append
pop有2种情况:
--栈2空的情况,把栈1push进去的数字,append到stack2内,再pop出来
--栈2不空的情况,直接pop,反正不用stack1的东西
"""
def __init__(self):
self.stack1 = [] # 栈1当做入队列
self.stack2 = [] # 栈2当做出队列
def push(self, node):
self.stack1.append(node)
def pop(self):
if self.stack2 == []:
while self.stack1:
self.stack2.append(self.stack1.pop())
return self.stack2.pop()
return self.stack2.pop()
旋转数组的最小数字
Q:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
思路:一读发现,这个和旋转有个鸡毛关系,你错了,同学,暴力遍历是可以,直接min()就能完事,但是怎么能显示你的技术呢?
建议细读 非递减排序 这是个2个有序数组连一起的数组,前面数组的数字大小都比后面的大
用二分不好吗?
百度(1)确定该区间的中间位置K(2)将查找的值T与array[k]比较。若相等,查找成功返回此位置;否则确定新的查找区域,继续二分查找。
数组不同那就分情况:
target==右端数字 不可以用左端
-
arr[mid]>target {4,5,6,1,2,3}
arr[mid]==6 ,target==3 这种情况来说,你要找的数字是在mid+1到最后里面,∴first =mid+1
-
arr[mid]<target {5,6,1,2,3,4}
arr[mid]==1 ,target==4 这种情况来说,你要找的数字必然在first到mid之间,所以可以缩小范围,last =mid
-
arr[mid]==target {1,0,1,1,1} {1,1,1,0,1}
这个没办法 慢慢last-1遍历
class Solution: def min(self, rotateArray): # 如果空 if rotateArray == []: return 0 first = 0 last = len(rotateArray) - 1 mid = (first + last) // 2 while first < last: # 情况3 if rotateArray[first] == rotateArray[mid] == rotateArray[last]: last -= 1 # 情况2 右端大于中间 if rotateArray[last] > rotateArray[mid]: last = mid mid = (first + last) // 2 # 情况1 右端小于中间 elif rotateArray[last] < rotateArray[mid]: first = mid + 1 mid = (first + last) // 2 return rotateArray[first] if __name__ == '__main__': s = Solution() print(s.min((3,4,1,2)))