剑指offer答案(python版)3~28题

3.二维数组中的查找

问题:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
算法思想:我们知道每一行都是递增排序的,每一列也是从上到下递增排序的,所以左上角的数是最小的,而右下角的数是最大的
1、选取右上角的数;
2,、判断比较右上角的数和传入的数值的大小,如果等于该参数的值,查找结束;
3、如果大于参数,则可以把这一列剔除;
4、如果小于参数,就剔除该数值所在的行

# -*- coding:utf-8 -*-  
class Solution:      
    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()))  
        array=L[1]  
        target=L[0]  
        print(S.Find(target, array))  
    except:  
        break  

4.替换空格

问题:请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
算法思想1:开辟新的空间,从前往后替换:

# -*- coding:utf-8 -*-
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        # write code here
        if len(s)<=0:
            return s
        result=""
        for i in s:
            if i.strip():
                result+=i
            else:
                result+="%20"
        return result

算法思想2:
在原有的字符串中修改:从后往前添加,每个字符只需移动一次,遇到空格,替换%20,效率高,移动次数少;
先计算出原有的字符串空格的个数,扩展原有字符串长度,新长度=原有长度+空格个数*2,往后移动字符,遇到空格就替换

5.从尾到头打印链表

问题:输入一个链表,从尾到头打印链表每个节点的值。
算法思想:转换成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
        newlist=[]
        while listNode:
            newlist.append(listNode.val)
            listNode=listNode.next
        return newlist[::-1]

6.重建二叉树

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
算法思想:递归实现
1.根据前序找到根节点;
2.找到根节点在中序中的位置;
3.截取左右子树;
4.左右字数分别递归调用;
5.递归返回节点值

# -*- 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):
        # write code here
        if len(pre)==0:
            return None
        if len(pre)==1:
            return TreeNode(pre[0])
        else:
            root=TreeNode(pre[0])
            post=tin.index(pre[0])

            root.left=self.reConstructBinaryTree(pre[1:post+1],tin[:post])
            root.right=self.reConstructBinaryTree(pre[post+1:],tin[post+1:])
        return root

7.两个栈实现队列

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

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.stack1=[]
        self.stack2=[]
    def push(self, node):
        # write code here
        self.stack1.append(node)
    def pop(self):
        # return xx
        if self.stack2==[]:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
        return self.stack2.pop()

8.旋转数组中最小数

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

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        length=len(rotateArray)
        if length<=0 or rotateArray==None:
            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
                print(left)
            elif rotateArray[mid]<rotateArray[right]:
                right=mid
            else:
                right-=1
            if left>=right:
                break
        return rotateArray[left]

9.斐波那契数列问题:

9.1 斐波那契数列

现在要求输入一个整数n,请你输出斐波那契数列的第n项。

算法思想一:使用递归:但是如果f(n)=f(n-1)+f(n-2),这样从上往下递归计算的话,会有很多重复的节点,而节点会随着n的增大急剧而增多,意味着计算量急剧增加。

算法思想二:通过循环实现,从f0+f1开始,算出f(2),再有f(1)+f(2)算出f(3),以此类推,算出第n项, 即从下往上计算,每次保存中间列的结果,这样的事件复杂度为O(n)

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloorII(self, number):
        # write code here
        if number <= 0:
            return 0
        else:
            return pow(2,number-1)

9.2 青蛙跳台阶:

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

算法思想:依然是斐波那切数列问题,当跳一节台阶时还有n-1个台阶,当第一次跳两个台阶时,还有n-2个台阶要跳。同样转化为斐波那契数列问题。

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloor(self, number):
        if number <= 0:
            return 1
        elif number <= 2:
            return number
        else:
            f1,f2 = 1,2
            for i in xrange(2,number+1):  
                f1,f2=f2,f1+f2
        return f1

9.3 变态青蛙跳台阶问题:

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

算法思想:依然是斐波那切数列问题
当n = 1 时, 只有一种跳法,即1阶跳:Fib(1) = 1;
当n = 2 时, 有两种跳的方式,一阶跳和二阶跳:Fib(2) = Fib(1) + Fib(0) = 2;
当n = 3 时,有三种跳的方式,第一次跳出一阶后,后面还有Fib(3-1)中跳法; 第一次跳出二阶后,后面还有Fib(3-2)中跳法;第一次跳出三阶后,后面还有Fib(3-3)中跳法
Fib(3) = Fib(2) + Fib(1)+Fib(0)=4;
当n = n 时,共有n种跳的方式,第一次跳出一阶后,后面还有Fib(n-1)中跳法; 第一次跳出二阶后,后面还有Fib(n-2)中跳法……………………..第一次跳出n阶后, 后面还有 Fib(n-n)中跳法.
Fib(n) = Fib(n-1)+Fib(n-2)+Fib(n-3)+……….+Fib(n-n)=Fib(0)+Fib(1)+Fib(2)+…….+Fib(n-1)
又因为Fib(n-1)=Fib(0)+Fib(1)+Fib(2)+…….+Fib(n-2)
两式相减得:Fib(n)-Fib(n-1)=Fib(n-1) =====》 Fib(n) = 2*Fib(n-1) n >= 2

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloorII(self, number):
        # write code here
        if number <= 0:
            return 0
        else:
            return pow(2,number-1)

9.4 矩形覆盖:

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

算法思想:当第一步是竖着放,右边还剩n-1个区域,当第一步横着放时,左下角应必须横着放,右边还剩n-2个区域,可以看出这仍斐波那切数列问题f(n)=f(n-1)+f(n-2)

# -*- coding:utf-8 -*-
class Solution:
    def rectCover(self, number):
        # write code here
        a,b=0,1
        result=0
        for i in range(number):
            result=a+b
            a=b
            b=result
        return result

10. 求二进制中1的个数

问题:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

算法思想一:
定义一个flag=1,和数字n做与运算,如果结果为1,则个数count+=1,然后flag做右移位移运算,得到的数在于n做与运算,知道最后;这种算法对于32位的整数就要循环32次

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1(self, n):
        # write code here
        count=0
        flag=1
        while flag<2**32:
            if n & flag:
                count+=1
            flag=flag << 1
        return count

算法思想二:
我们知道一个整数减去1,他的二进制最右边的1的左边的数字不变,右边的数字1变成了0,数字0变成了1,;基于这个思想我们可以把一个数和它减去1之后做与运算,亦可以得出结果

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1(self, n):
        # write code here
        count=0
        if n<0:
            n=n&0xFFFFFFFF #把负号去掉,如果负号在后面会陷入死循环
        return bin(n).count('1')
        while n:
            count+=1
            n = n & (n-1)
        return count

举一反三:

  • 用一条语句判断一个整数是不是2的整数次幂:
    如果是2的整数次幂,那么这个数的二进制中有且只有一个1,整个数减1后,再和自己做与运算,结果为0,当然要判断这个数要大于1
  • 判断m和n,经过改变n的二进制多少位可以得到m:
    这也就是判断m的二进制和n的二进制中有几位不同,可以先m与n做异或运算(相同为0,不同1),然后判断结果中1的位数。

11.数值的整数次方

问题:给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

方法一:直接计算

#-*- coding:utf-8 -*-
class Solution:
    def Power(self, base, exponent):
        # write code here
        if base==0:
            return False
        if exponent==0 or base==1:
            return 1
        if exponent==1:
            return base
        result=1.0
        for i in range(abs(exponent)):
            result *= base
        if exponent<0:
            result=1.0/result
        return result

方法二:判断exponent次方是偶数还是奇数

  • 如果是偶数,则base的exponent次方就等于 base的exponent/2次方 *
    base的exponent/2次方;(这个过程可以通过递归来实现)
  • 如果是奇数,则base的exponent次方就等于 base的(exponent-1)/2次方 *
    base的(exponent-1)/2次方,最后在乘以base
# -*- coding:utf-8 -*-
class Solution:
    def Power(self, base, exponent):
        # write code here
        flag=0
        if exponent<0:
            flag=1
        exponent=abs(exponent)
        if base==0:
            return False
        if exponent==0 or base==1:
            return 1
        if exponent==1:
            return base
        result=self.Power(base,exponent >> 1)    #这里使用右移运算来代替除2
        result*=result
        if (exponent & 1)==1:    #这里使用位与运算来代替求余
            result *= base
        if flag:
            result=1.0/result
        return result

14.调整数组顺序使得奇数位与偶数前面

问题:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分。

# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        # write code here
        beginInex=0
        endIndex=len(array)-1
        print(endIndex)
        while beginInex <endIndex:
            while beginInex <endIndex and ((array[beginInex] & 1) !=0):    #说明他是奇数
                beginInex+=1
            while beginInex <endIndex and ((array[endIndex] & 1)==0):    #偶数
                endIndex-=1
            if beginInex<endIndex:
                array[beginInex],array[endIndex]=array[endIndex],array[beginInex]
        return array

如果在上面的基础上,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
算法思想:开辟新的空间

# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        # write code here
        if len(array)<1 or not array:
            return array
        result=[]
        for i in range(len(array)):
            if (array[i] & 1) !=0:
                result.append(array[i])
        for i in range(len(array)):
            if (array[i] & 1) ==0:
                result.append(array[i])
        return result
# -*- coding:utf-8 -*-
from collections import deque
class Solution:
    def reOrderArray(self, array):
        # write code here
        if len(array)<1 or not array:
            return array
        result=deque()
        for i in range(len(array)):
            if (array[len(array)-i-1] & 1) !=0:
                result.appendleft(array[len(array)-i-1])
            if (array[i] & 1) ==0:
                result.append(array[i])
        return result

15 链表中倒数第k个结点:

问题:输入一个链表,输出该链表中倒数第k个结点。

算法思想一:定义连个指针,当第一个指针走了k步之后,第二个指针开始走,当第一个指针走到最后时,第二个指针的位置就是倒数第k个位置

# -*- coding:utf-8 -*-
class Solution:
    def FindKthToTail(self, head, k):
        # write code here
        if head==None or k==0:
            return None
        pHead=head
        pBehind=None
        for i in range(k-1):
            if pHead.next!=None:
                pHead=pHead.next
            else:return None
        pBehind=head
        while pHead.next!=None:
            pBehind=pBehind.next
            pHead=pHead.next
        return pBehind

算法思想二:开辟空间,把链表转化为列表或数组,然后利用list的切片,找到倒数第k个值

# -*- coding:utf-8 -*-
class Solution:
    def FindKthToTail(self, head, k):
        # write code here
        if head==None or k==0:
            return None
        l=[]
        while head!=None:
            l.append(head)
            head=head.next
        if k<1  or k>len(l):
            return None
        result=l[-k]
        return result

举一反三:

  1. 求链表的中间节点:
    定义两个指针,一个走一步,一个走两步,当走的快的指针走到最后,则走的慢的指针位与中间位置。
  2. 判断一个单向链表是否形成了一个环:
    定义两个指针,一个走一步,一个走两步,当走的快的指针追上走的慢的指针时,那么链表就是一个环形链表,当走的快的链表走到链表的末尾,都没有追上走到慢的指针,那么链表就不是环形链表。

16.反转链表

问题:输入一个链表,反转链表后,输出链表的所有元素。

# -*- coding:utf-8 -*-
class Solution:
   # 返回ListNode
    def ReverseList(self, pHead):
        # write code here
        if pHead==None or pHead.next==None:
            return pHead
        result=None
        while pHead!=None:
            temp=pHead.next    #保存下一个节点
            pHead.next=result    #当前节点放到结果的开头
            result=pHead    #当前节点的头
            pHead=temp    #head指向下一个节点
        return result

17.合并两个排序的链表:

问题:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

# -*- 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 pHead1==None:
            return pHead2
        elif pHead2==None:
            return pHead1
        result=None
        if pHead1.val<pHead2.val:
            result=pHead1
            result.next=self.Merge(pHead1.next,pHead2)
        else:
            result=pHead2
            result.next=self.Merge(pHead1,pHead2.next)
        return result

18. 树的子结构:

问题:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

算法思想:使用递归遍历两棵树

# -*- 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 not pRoot1 or not pRoot2:
            return result
        return self.DoseTree1HaveTree2(pRoot1, pRoot2) or self.DoseTree1HaveTree2(pRoot1.left, pRoot2) or self.DoseTree1HaveTree2(pRoot1.right, pRoot2)

    def DoseTree1HaveTree2(self,tree1, tree2):
        if not tree2 :
            return True
        if not tree1 or tree1.val!=tree2.val:
            return False
        return self.DoseTree1HaveTree2(tree1.left,tree2.left) and self.DoseTree1HaveTree2(tree1.right,tree2.right)

19.二叉树的镜像

问题:操作给定的二叉树,将其变换为源二叉树的镜像。

算法思想:前序遍历树的每个结点,如果遍历到的结点有子节点,就交换它的两个子节点,当交换完所有的非叶子结点的左右子结点后,就得到了树的镜像。(这里使用递归的思想)

# -*- 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 not root:
            return root
        if (not root.left) and (not root.right):
            return root
        root.left,root.right=root.right,root.left
        if root.left:
            self.Mirror(root.left)
        if root.right:
            self.Mirror(root.right)

20. 顺时针打印矩阵

问题:
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 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.

算法思想:前序遍历树的每个结点,如果遍历到的结点有子节点,就交换它的两个子节点,当交换完所有的非叶子结点的左右子结点后,就得到了树的镜像。(这里使用递归的思想)

# -*- coding:utf-8 -*-
class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
        rows=len(matrix)
        cols=len(matrix[0])
        result=[]
        if rows==1 and cols==1:
            result= [matrix[0][0]]
            return result
        for m in xrange((min(rows,cols)+1)//2):    #走的圈数:1xN即一行,走一圈;Nx1即一列,走一圈;
            [result.append(matrix[m][i]) for i in xrange(m,cols-m)]    #从左向右
            [result.append(matrix[j][cols-1-m]) for j in xrange(m,rows-m) if matrix[j][cols-1-m] not in result]    #从上到下
            [result.append(matrix[rows-1-m][k]) for k in xrange(cols-1-m,m-1,-1) if matrix[rows-1-m][k] not in result]    #从右到左
            [result.append(matrix[l][m]) for l in xrange(rows-1-m,m-1,-1) if matrix[l][m] not in result]    #从下到上
        return result

21.包含min函数的栈

问题:定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
算法思想:借助于中间栈
往数据栈压入数据时,同时往辅助栈中压入最小数。(比较当前数据和最小值的大小,如果比最小值到,往辅助栈压入最小值,如果比最小值小,压入辅助栈,并且更新最值);出栈时,每次数据栈弹出数据,辅助栈也弹出数据,则辅助栈的栈顶总是最下值。

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.stack=[]    #数据栈
        self.min_stack=[]    #辅助栈
    def push(self, node):
        # write code here
        self.stack.append(node)
        if not self.min_stack or node<=self.min_stack[-1]:
            self.min_stack.append(node)
    def pop(self):
        # write code here
        if self.stack[-1]==self.min_stack[-1]:
            self.min_stack.pop()
        self.stack.pop()
    def top(self):
        # write code here
        return self.stack[-1]
    def min(self):
        # write code here
        return self.min_stack[-1]

22.栈的压入、弹出序列

问题:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

算法思想:借用一个辅助的栈,遍历压栈顺序,先讲第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。

# -*- coding:utf-8 -*-
class Solution:
    def IsPopOrder(self, pushV, popV):
        # write code here
        if not pushV or len(pushV)!=len(popV):
            return False
        stack=[]
        for i in pushV:
            stack.append(i)
            while len(stack) and stack[-1]==popV[0]:
                stack.pop()
                popV.pop(0)
        if len(stack):
            return False
        return True

23.从上往下打印二叉树(二叉树广度优先遍历)

问题:从上往下打印出二叉树的每个节点,同层节点从左至右打印。
算法思想:使用队列

# -*- 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 []
        queue=[root]
        result=[]
        while len(queue):
            node=queue.pop(0)
            result.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        return result

24.二叉搜索树的后序遍历序列

问题:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
算法思想:二叉搜索树的特点,左子树比根节点小,右子树比根节点大;后序遍历就是左–>右–>根的顺序。使用递归思想,首先找到左右子树,然后递归。

# -*- coding:utf-8 -*-
class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        if not sequence:
            return False
        if len(sequence)==1:
            return True
        root=sequence[-1]
        leftLen=0
        while sequence[leftLen]<root:    #左子树
            leftLen+=1
        rightLen=leftLen
        for k in sequence[leftLen:len(sequence)-1]:
            if k<root:
                return False
        left_tr=sequence[:leftLen]
        right_tr=sequence[rightLen:len(sequence)-1]
        leftIs = True
        rightIs = True
        if len(left_tr):
            leftIs=self.VerifySquenceOfBST(left_tr)
        if len(right_tr):
            rightIs=self.VerifySquenceOfBST(right_tr)
        return leftIs and rightIs

猜你喜欢

转载自blog.csdn.net/john_bh/article/details/79267537