剑指offer-Python3版(二)

剑指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)))
    
    
    
    
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值