劍指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)

 

 

 

 

 

 

 

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