剑指offer解答(第一部分)

0.实现二分查找
# -*- coding:utf-8 -*-
class Solution:
    def Find(self, target, array):
        return self.fff(target, array, 0, len(array)-1)

    def fff(self, target, array, start, end):
        if end < start:  # 子数组长度是0
            return False
        elif end == start:  # 子数组长度是1
            if array[start] == target:
                return True
            else:
                return False
        if array[start] > target or array[end] < target:  # 不在范围内
            return False

        mid = int((start + end)/2)
        if array[mid] == target:
            return True
        elif array[mid] < target:
            return self.fff(target, array, mid+1, end)
        elif array[mid] > target:
            return self.fff(target, array, start, mid-1)


s = Solution()
a = [1, 2, 3, 4, 5, 6, 7, 8]
print(s.Find(8, a))

 

 

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

两种思路
第一种是:
当m<=n时,
把每一行看成有序递增的数组,利用二分查找,
通过遍历每一行得到答案,时间复杂度是O(mlogn)
当m>n时,
把每一列看成有序递增的数组,利用二分查找,
通过遍历每一列得到答案,时间复杂度是O(nlogm)

另外一种思路是:
从左下角元素开始查找,当要查找数字比左下角数字大时。右移
要查找数字比左下角数字小时,上移
时间复杂度是O(m+n)


对于m=1024 != n=4  (m远大于n时)
mlogn=1024*2=2048
nlogm=4*10=40 (最小)
m+n=1028
对于m==n
比较nlogn和2n,当n>=4时,用第二种方法。
可以事先比较mlogn、nlogm、m+n的大小,取最小时间复杂度的方法


# -*- coding:utf-8 -*-
class Solution:
    # array 二维列表
    def Find(self, target, array):
        m, n = len(array), len(array[0])
        i, j = m-1, 0
        while True:
            if target>array[i][j]:
                j += 1
                if j == n:
                    return False
            elif target<array[i][j]:
                i -= 1
                if i == -1:
                    return False
            elif target==array[i][j]:
                return True

 

 


2.请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
# -*- coding:utf-8 -*-
class Solution:
    # s 源字符串
    def replaceSpace(self, s):
        return "%20".join(list(s.split(" ")))

        
3.输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
# 比如3->2->1
# python数组[2]后接一个数3可以写成[2]+[3]
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        if listNode is None:
            return []
        return self.printListFromTailToHead(listNode.next) + [listNode.val]


    

4.输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
解:递归处理(自己写的,python求子数组可以用pre[index : index+length])
# -*- 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
        i = 0
        while tin[i] != pre[0]:
            i += 1
        root = TreeNode(pre[0])
        root.left = self.reConstructBinaryTree(pre[1:i+1], tin[0:i])
        root.right = self.reConstructBinaryTree(pre[i+1:len(tin)], tin[i+1:len(tin)])
        return root

5.用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
解:设有2个栈A和B。元素x进队列时,两个栈所有元素移入A,再将x压入A;
出队列时,两个栈所有元素移入B,再弹出B栈顶的元素。


6.把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非减排序(可以理解为递增排序,如[1, 2, 2, 4])的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
解:二分查找!!!!
比如
3 4 4 1 1 2
考察a[start]、a[mid]、a[end]和它们左右两边(如果有)的数
最左边的2个数是3 4,它递增
中间的3个数是4 4 1,它不递增,所以最小值肯定是 min(a[mid], a[mid+1])
最右边边的2个数是1 2,它递增

比如
3 4 4 4 1 2
考察a[start]、a[mid]、a[end]和它们左右两边(如果有)的数
最左边的2个数是3 4,它递增
中间的3个数是4 4 4,它递增
最右边边的2个数是1 2,它递增
由于a[mid]>a[end],所以最小值在a[mid]右边
如果a[mid]<=a[end],则最小值在a[mid]左边

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        return self.fff(rotateArray, 0, len(rotateArray) - 1)

    def fff(self, array, start, end):
        if end < start:  # 子数组长度是0
            return 0
        elif end == start:  # 子数组长度是1
            return array[start]
        elif end == start + 1:  # 子数组长度是2
            return min(array[start], array[start+1])
        elif end == start + 2:  # 子数组长度是3
            mini = min(array[start], array[start+1])
            return min(mini, array[start+2])

        mid = int((start + end) / 2)
        if array[start] > array[start + 1]:  # 最左边的2个数不递增
            return array[start + 1]
        if array[mid - 1] > array[mid]:  # 中间的3个数不递增
            return array[mid]
        if array[mid] > array[mid + 1]:  # 中间的3个数不递增
            return array[mid + 1]
        if array[end - 1] > array[end]:  # 最右边的2个数不递增
            return array[end]

        if array[mid] > array[end]:
            return self.fff(array, mid + 1, end -1)
        elif array[mid] <= array[end]:
            return self.fff(array, start + 1, mid - 1)
7.大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39
# -*- coding:utf-8 -*-
class Solution:
    def Fibonacci(self, n):
        if n == 0:
            return 0
        if n == 1 or n == 2:
            return 1
        else:
            a, b, c = 1, 1, 2
            for i in range(2, n, 1):
                c = a + b
                a = b
                b = c
            return c

 

8.一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
假设当有n个台阶时假设有f(n)种跳法。
最后一步有2种跳法   f(n)=f(n-1)+f(n-2)。显然f(1)=1,f(2)=2。这是一个类似斐波那契数列的数列(a2=2)。
# -*- coding:utf-8 -*-
class Solution:
    def jumpFloor(self, n):
        if n == 1:
            return 1
        if n == 2:
            return 2
        else:
            a, b, c = 1, 2, 3
            for i in range(2, n, 1):
                c = a + b
                a = b
                b = c
            return c

9.一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
假设当有n个台阶时假设有f(n)种跳法。
最后一步有n种跳法   f(n)=f(n-1)+f(n-2)+f(n-3)+...+f(2)+f(1)+1
同理                f(n-1) =    f(n-2)+f(n-3)+...+f(2)+f(1)+1
所以                f(n)=2*f(n-1)
f(1)=1, 所以        f(n)=2^(n-1)    是一个等比数列。

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloorII(self, n):
        return 2 ** (n-1)
        
10.我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
覆盖2*n的大矩形的方法数设为f(n),则
f(n)=f(n-1)+f(n-2)
f(1)=1,f(2)=2

# -*- coding:utf-8 -*-
class Solution:
    def rectCover(self, n):
        if n == 0:
            return 0
        if n == 1:
            return 1
        if n == 2:
            return 2
        else:
            a, b, c = 1, 2, 3
            for i in range(2, n, 1):
                c = a + b
                a = b
                b = c
            return c

11.输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
负数的补码是其绝对值的原码最高位符号位不变,其它位取反,再加1
求-7的补码
先写出-7的原码:1111——1(符号位)111(数值位)
除符号位外逐位取反:1000
最后再末尾+1:1000+1=1001

    补码        |          原码    补码    
1   0001        |   -1     1001    1111     
2   0010        |   -2     1010    1110     
3   0011        |   -3     1011    1101     
4   0100        |   -4     1100    1100     
5   0101        |   -5     1101    1011     
6   0110        |   -6     1110    1010     
7   0111        |   -7     1111    1001     
0   0000        |   -8     1000    1000     

观察负数的补码(对比正数的),设c(n)是整数n补码的1的数量,有
c(1)=c(-7)-1
c(2)=c(-6)-1
...
c(7)=c(-1)-1
c(0)=c(-8)-1
设n<0,即有c(n) = 1 + c(n+8)
用4位来表示整数时,上面的结论才成立。用32位来表示-1时,-1的补码中1的个数显然是32个。
像Java等编程语言中,Integer的取值范围(-2^31~2^31-1)
所以若n<0,即有c(n) = 1 + c(n+2^31)

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1(self, n):
        if n < 0:
            return 1 + self.NumberOf1( n + 2147483648 )
        elif n == 0:
            return 0
        elif n == 1:
            return 1
        else:
            temp = 2
            while temp <= n:
                temp *= 2
            temp /= 2
            return 1 + self.NumberOf1(n-temp)

或者
# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1(self, n):
        return sum([(n>>i & 1) for i in range(0,32)])


12.给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
# -*- coding:utf-8 -*-
class Solution:
    def Power(self, base, exponent):
        if exponent == 0:
            return 1
        elif exponent > 0:
            r = 1.0
            for i in range(exponent):
                r *= base
            return r
        elif exponent < 0:
            return 1.0/self.Power(base, -exponent)
或者
# -*- coding:utf-8 -*-
class Solution:
    def Power(self, base, exponent):
        return base ** exponent

13.输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
解:冒泡排序是稳定的排序算法,快速排序不是稳定的排序算法。根据题意,需要保持“稳定性”。
不妨开辟一个新数组,遍历2次原数组,第一次遍历时遇到奇数就插入新数组,第二次遍历时遇到偶数就插入新数组。
这样的话时间复杂度和空间复杂度都是O(n)
python的话可以只遍历1次:

# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        odd, even = [], []
        for i in array:
            odd.append(i) if i%2 == 1 else even.append(i)
        return odd + even
或者
# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        return sorted(array, key=lambda c:c%2, reverse=True)  # reverse = True降序,即奇数在前


14.输入一个链表,输出该链表中倒数第k个结点。
遍历2次链表
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def FindKthToTail(self, head, k):
        n = 0
        node = head
        while node is not None:
            node = node.next
            n += 1
        if n-k < 0:
            return None
        node = head
        for i in range(n-k):
            node = node.next
        return node

        
第二种方法(别人写的):或者设两个指针,一前一后前面的比后面的先走k步,但是会在第一次遍历中引入多余的判断语句
第一种方法里循环里执行的语句数n*2+(n-k)
第二种方法里循环里执行的语句数n*3+(n-k)
所以省去一次遍历,却引来了多余的判断语句,反而使得性能下降。
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def FindKthToTail(self, head, k):
        n = 0
        node1, node2 = head, head
        while node1 is not None:
            node1 = node1.next
            n += 1
            if n > k:
                node2 = node2.next
        if n-k < 0:
            return None
        return node2

 

15.输入一个链表,反转链表后,输出新链表的表头。
遍历一次,改指针
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        if pHead is None:  # 空表
            return None
        if pHead.next is None:  # 只有一个元素
            return pHead
        node1 = pHead
        node2 = node1.next
        node3 = node2.next
        
        node1.next = None
        node2.next = node1
        while node3 is not None:
            node1 = node2
            node2 = node3
            node3 = node3.next
            node2.next = node1
        return node2

图解:         
  a   ->   b   ->   c
node1    node2    node3

  a   <-   b        c
node1    node2    node3

进入while循环:
  a   <-   b        c
         node1    node2    node3

  a   <-   b   <-   c
         node1    node2    node3

16.输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
递归处理
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        if pHead1 is None:
            return pHead2
        if pHead2 is None:
            return pHead1
        node1, node2 = pHead1, pHead2
        
        if node1.val > node2.val:
            root = node2
            root.next = self.Merge(node1, node2.next)
            return root
        else:
            root = node1
            root.next = self.Merge(node1.next, node2)
            return root

18.操作给定的二叉树,将其变换为源二叉树的镜像。
二叉树的镜像定义:源二叉树
            8
           /  \
          6   10
         / \  / \
        5  7 9 11
        镜像二叉树
            8
           /  \
          10   6
         / \  / \
        11 9  7  5
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回镜像树的根节点
    def Mirror(self, root):
        if root is None:
            return
        temp = root.left
        root.left = root.right
        root.right = temp
        self.Mirror(root.left)
        self.Mirror(root.right)
        
19.输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下
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]

# -*- coding:utf-8 -*-
class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        start_i, start_j = 0, 0
        r, c = len(matrix), len(matrix[0])

        result = []

        while start_i < r and start_j < c:
            i, j = start_i, start_j

            # 只剩下1行
            if i == r-1:
                while j < c:
                    result.append(matrix[i][j])
                    j += 1
                break

            # 只剩下1列
            if j == c-1:
                while i < r:
                    result.append(matrix[i][j])
                    i += 1
                break

            while j < c:
                result.append(matrix[i][j])
                j += 1
            j -= 1
            i += 1
            while i < r:
                result.append(matrix[i][j])
                i += 1
            i -= 1
            j -= 1
            while j > start_j - 1:
                result.append(matrix[i][j])
                j -= 1
            j += 1
            i -= 1
            while i > start_i:
                result.append(matrix[i][j])
                i -= 1

            start_i += 1
            start_j += 1
            r -= 1
            c -= 1
        return result


20.定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
两个数组,一个用来实现栈,一个用来存储最小元素
# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.array = []
        self.miniarray = []
        
    def push(self, node):
        if len(self.array) == 0:
            self.miniarray.append(node)
        elif node <= self.miniarray[len(self.miniarray)-1]:
            self.miniarray.append(node)
        self.array.append(node)
        
    def pop(self):
        if self.top() == self.miniarray[len(self.miniarray)-1]:
            self.miniarray = self.miniarray[0: len(self.miniarray)-1]
        if len(self.array) == 0:
            return
        self.array = self.array[0: len(self.array)-1]
        
    def top(self):
        if len(self.array) == 0:
            return None
        return self.array[len(self.array)-1]
    
    def min(self):
        return self.miniarray[len(self.miniarray)-1]


21.输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
解:比如
1 2 3 4 5
4 5 3 2 1是。
4出栈的时候,证明栈里面有1 2 3 ,那么这3个数一定是先进后出。5 3 2 1符合,转为判断
1 2 3 5
5 3 2 1

5出栈的时候,证明栈里面有1 2 3 ,那么这3个数一定是先进后出。3 2 1符合,转为判断
1 2 3
3 2 1
......


比如
1 2 3 4 5
4 3 5 1 2
4出栈的时候,证明栈里面有1 2 3 ,那么这3个数一定是先进后出。3 5 1 2不符合,直接return False

# -*- coding:utf-8 -*-
class Solution:
    def IsPopOrder(self, pushV, popV):
        if len(pushV) == 1:
            if pushV[0] == popV[0]:
                return True
            else:
                return False

        p = popV[0]
        i = pushV.index(p)
        boo = self.fff(pushV[0:i], popV[1:len(popV)])
        if not boo:
            return False
        return self.IsPopOrder(pushV[0:i] + pushV[i+1:len(pushV)], popV[1:len(popV)])

    # 判断是否倒序1, 2, 3     5, 3, 2, 1
    def fff(self, a, b):
        i = len(a)-1
        j = 0

        # 有多少个数字已经倒序
        count = 0
        while j < len(b):
            if b[j] == a[i]:
                count += 1
                i -= 1
            j += 1
        return count == len(a)


22.从上往下打印出二叉树的每个节点,同层节点从左至右打印。
            8
           /  \
          6   10
         / \  / \
        5  7 9  11
[8, 6, 10, 5, 7, 9, 11]

广度优先遍历算法,广度优先一般需要一个辅助队列。这里用数组。
图用邻接链表表示时,每行表示这个节点的所有下一个节点。
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def PrintFromTopToBottom(self, root):
        if root is None:
            return []
        result = []
        self.fff([root], result)
        return result

    # 递归方法
    def fff(self, nodeList, result):
        newNodeList = []
        for node in nodeList:
            result.append(node.val)
            if node.left is not None:
                newNodeList.append(node.left)
            if node.right is not None:
                newNodeList.append(node.right)
        if len(newNodeList) == 0:
            return
        self.fff(newNodeList, result)

23.输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
            8
           /  \
          6   10
         / \  / \
        5  7 9  11
则后序遍历是
[5, 7, 6, 9, 11, 10, 8]
5, 7, 6都比8小
9, 11, 10都比8大
然后递归判断左子树、右子树

# -*- coding:utf-8 -*-
class Solution:
    def VerifySquenceOfBST(self, sequence):
        if len(sequence) == 0:
            return False
        if len(sequence) == 1:
            return True

        root = sequence[len(sequence) - 1]
        i = 0
        while sequence[i] < root:
            i += 1
        j = i
        while sequence[j] > root:
            j += 1

        if j != len(sequence) - 1:
            return False

        # 左子树为空,判断右子树
        if i == 0:
            return self.VerifySquenceOfBST(sequence[i:j])
        # 右子树为空,判断左子树
        if i == j:
            return self.VerifySquenceOfBST(sequence[0:i])
        return self.VerifySquenceOfBST(sequence[0:i]) and self.VerifySquenceOfBST(sequence[i:j])


24.输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

二叉树的深度优先遍历其实就是先序遍历。
问题可以转换成先序遍历生成所有的路径。(递归处理时难在分叉的处理上)
首先要明白一点,有多少叶子节点就有多少路径。
            8
           /  \
          6   10
         / \  / \
        5  7 9  11
比如输入expectNumber = 19,则输出
[[8, 6, 5]]

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回二维列表,内部每个列表表示找到的路径
    def FindPath(self, root, expectNumber):
        if root is None:
            return []
        paths = [[root.val]]
        self.dfs(root, paths, 0)
        newpaths = []
        for path in paths:
            sum = 0
            for node in path:
                sum += node
            if sum == expectNumber:
                newpaths.append(path)
        return sorted(newpaths, key=lambda c:len(c), reverse=True)

    # dfs深度优先,带记忆的深度优先,也是先序遍历
    def dfs(self, root, paths, path_index):
        if root.left is None and root.right is None:
            return
        elif root.left is None:  # 不分叉
            paths[path_index].append(root.right.val)
            self.dfs(root.right, paths, path_index)
        elif root.right is None:  # 不分叉
            paths[path_index].append(root.left.val)
            self.dfs(root.left, paths, path_index)
        else:  # 分叉,需要复制一份,python创建一个数组的副本可以使用a[:]
            newpath = paths[path_index][:]
            newpath.append(root.right.val)

            paths[path_index].append(root.left.val)
            paths.append(newpath)

            # 因为执行完self.dfs(root.left, paths, path_index)
            # 后paths长度可能会发生变化,所以这里先保存它的长度n
            n = len(paths)-1
            self.dfs(root.left, paths, path_index)
            self.dfs(root.right, paths, n)

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章