剑指offer 在线编程(08-05)【1】

Date: 2019-08-05

1.  二叉树的深度

题目描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

# version: python 2.7
# -*- coding:utf-8 -*-

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def TreeDepth(self, pRoot):
        if not pRoot:
            return 0
        return self.getpath(pRoot)
    def getpath(self,root):
        if not root.left and not root.right:
            return 1
        elif not root.left:
            return self.getpath(root.right) +1
        elif not root.right:
            return self.getpath(root.left) +1
        else:
            return max(self.getpath(root.left), self.getpath(root.right)) +1

2.  二维数组中的查找

题目描述

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

下面的解决方案是:直接将二维数组当成一个大的list,其中每一个元素是一个list,因此遍历每一个list元素,判断target是否在array中,如果在,则flag='true‘,同时break.   另外需要注意的是:输入的引入和类中函数的应用。

# version: python2.7
# -*- coding:utf-8 -*-
class Solution:
    # array 二维列表
    def Find(self, target, array):
        n=len(array)
        flag='false'
        for i in range(n):
            if target in array[i]:
                flag='true'
                break
        return flag
while True:
    try:
        S=Solution()
        L=list(eval(raw_input()))
        target=L[0]
        array=L[1]
        print(S.Find(target, array))
    except:
        break

3.  替换空格

题目描述

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

# version: Python 2.7
# -*- coding:utf-8 -*-
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        return s.replace(" ","%20")

# the second way
# -*- coding:utf-8 -*-
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        # write code here
        s = list(s)
        count=len(s)
        for i in range(0,count):
            if s[i]==' ':
                s[i]='%20'
        return ''.join(s)

4.  从尾到头打印链表

题目描述

输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

方法一真的是绝了:(利用list的负索引方法—)将arraylist写到一个list中,然后[::-1]即可实现反向打印单向链表

# version: python 2.7
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        mlist=list()
        while listNode:
            mlist.append(listNode.val)
            listNode = listNode.next
        return mlist[::-1]

或者在将arraylist保存到新的list中时,就实现每次保存到第一位置。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
 
class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        # write code here
        l = []
        head = listNode
        while head:
            l.insert(0, head.val)
            head = head.next
        return l

再或者利用reverse()函数进行操作

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
 
class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        # write code here
        lis = []
        while   listNode:
            lis.append(listNode.val)
            listNode = listNode.next
        lis.reverse()
        return  lis

5.  重建二叉树

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

分析思路:根据前序和中序构建二叉树,主要是利用链表去可视化链表,所以对于每一个节点需要获取他的左节点和右节点。

*  首先需要考虑特殊情况的时候,序列中没有元素时;序列中只有一个元素时(树根节点);序列中元素个数大于2;

*  当元素大于2时,前序列pre中第一个元素即是根节点,然后基于这个根节点不断递归建立左子树和右子树。

左子树:递归调用的第一个元素是前序列中的到中序列中pre[0]的索引,第二个元素是中序列中的到中序列的pre[0]的索引-1

右子树: 递归调用的第一个元素是前序列中的中序列中pre[0]的索引+1:最后,第二个元素是中序列中的pre[0]的索引:最后

注意:索引具体有所区别!

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        if len(pre) == 0:
            return None
        elif len(pre) == 1:
            return TreeNode(pre[0])
        else:
            head = TreeNode(pre[0])
            head.left = self.reConstructBinaryTree(pre[1:tin.index(pre[0])+1], tin[:tin.index(pre[0])])
            head.right = self.reConstructBinaryTree(pre[tin.index(pre[0])+1:], tin[tin.index(pre[0])+1:])
            return head

6. 用两个栈实现队列

题目描述

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

分析思路:首先借用list实现两个空栈,push:对栈2(前续栈)进行填充元素;pop:1)如果两个栈都为空,则None;2)如果栈1(后续栈)非空,先释放栈内存;3)当栈2(前续栈)中元素个数大于0时,则将栈2元素弹出,进入到栈1(key)中。;;;最后进行整体栈1的弹出。即可实现先进先出的队列。

# version:python2.7
# -*- coding:utf-8 -*-
class Solution:
    stack1=[]
    stack2=[]
    def push(self, node):    # 先把栈2进行填元素
        self.stack2.append(node)
    def pop(self):
        if len(self.stack1) == 0 and len(self.stack2) == 0:  # 如果两个栈都为空,则None
            return None
        elif len(self.stack1) != 0:     # 如果栈1(后续栈)不为空,先pop()清空
            return self.stack1.pop()
        while len(self.stack2) > 0:     # 如果栈2(前续栈)有元素,
            self.stack1.append(self.stack2.pop())  # key:栈2弹出元素,压入栈1中。
        return self.stack1.pop()  # 最后栈1进行整体的弹出,即可实现先进先出。

7.  旋转数组的最小数字

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

该题实际上就是寻找最小数字。

第一种方法最直接就是顺序查找方法(O(n)的时间复杂度);第二种方法参考二分查找的方法(O(logn))。

顺序查找如下:

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if len(rotateArray) == 0:
            return 0
        pre = -7e20
        for num in rotateArray:
            if num < pre :
                return num
            pre = num
             
        return rotateArray[0]

类似二分查找的方法有点难,分析如下:

剑指Offer中有这道题目的分析。这是一道二分查找的变形的题目。

 

旋转之后的数组实际上可以划分成两个有序的子数组:前面子数组的大小都大于后面子数组中的元素

注意到实际上最小的元素就是两个子数组的分界线。本题目给出的数组一定程度上是排序的,因此我们试着用二分查找法寻找这个最小的元素。

思路:

(1)我们用两个指针left,right分别指向数组的第一个元素和最后一个元素。按照题目的旋转的规则,第一个元素应该是大于最后一个元素的(没有重复的元素)。

但是如果不是旋转,第一个元素肯定小于最后一个元素。

(2)找到数组的中间元素。

中间元素大于第一个元素,则中间元素位于前面的递增子数组,此时最小元素位于中间元素的后面。我们可以让第一个指针left指向中间元素。

移动之后,第一个指针仍然位于前面的递增数组中。

中间元素小于第一个元素,则中间元素位于后面的递增子数组,此时最小元素位于中间元素的前面。我们可以让第二个指针right指向中间元素。

移动之后,第二个指针仍然位于后面的递增数组中。

这样可以缩小寻找的范围。

(3)按照以上思路,第一个指针left总是指向前面递增数组的元素,第二个指针right总是指向后面递增的数组元素。

最终第一个指针将指向前面数组的最后一个元素,第二个指针指向后面数组中的第一个元素。

也就是说他们将指向两个相邻的元素,而第二个指针指向的刚好是最小的元素,这就是循环的结束条件。

到目前为止以上思路很耗的解决了没有重复数字的情况,这一道题目添加上了这一要求,有了重复数字。

因此这一道题目比上一道题目多了些特殊情况:

我们看一组例子:{1,0,1,1,1} 和 {1,1, 1,0,1} 都可以看成是递增排序数组{0,1,1,1,1}的旋转。

这种情况下我们无法继续用上一道题目的解法,去解决这道题目。因为在这两个数组中,第一个数字,最后一个数字,中间数字都是1。

第一种情况下,中间数字位于后面的子数组,第二种情况,中间数字位于前面的子数组。

因此当两个指针指向的数字和中间数字相同的时候,我们无法确定中间数字1是属于前面的子数组(绿色表示)还是属于后面的子数组(紫色表示)。

也就无法移动指针来缩小查找的范围。

具体以这个例子来讲:

mid = low + (high - low)/2

需要考虑三种情况:

(1)array[mid] > array[high]:

出现这种情况的array类似[3,4,5,6,0,1,2],此时最小数字一定在mid的右边。

low = mid + 1

(2)array[mid] == array[high]:

出现这种情况的array类似 [1,0,1,1,1] 或者[1,1,1,0,1],此时最小数字不好判断在mid左边

还是右边,这时只好一个一个试 ,

high = high - 1

(3)array[mid] < array[high]:

出现这种情况的array类似[2,2,3,4,5,6,6],此时最小数字一定就是array[mid]或者在mid的左

边。因为右边必然都是递增的。

high = mid

注意这里有个坑:如果待查询的范围最后只剩两个数,那么mid 一定会指向下标靠前的数字

比如 array = [4,6]

array[low] = 4 ;array[mid] = 4 ; array[high] = 6 ;

如果high = mid - 1,就会产生错误, 因此high = mid

但情形(1)中low = mid + 1就不会错误

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        length = len(rotateArray)
        if length == 0:
            return 0
        if length == 1:
            return rotateArray[0]
        left, right = 0, length- 1
         
        while left<= right:
            mid = (left+ right) >> 1
            if rotateArray[mid] > rotateArray[right]:
                left = mid+ 1
            elif rotateArray[mid] < rotateArray[right]:
                right = mid
            else:
                right -= 1
            if left >= right:
                break
        return rotateArray[left]

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值