劍指offer全集python(2/3)第二大部分

目錄

23.從上往下打印二叉樹

24.二叉搜索樹的後序遍歷序列

25.二叉樹中和爲某一值的路徑

26.複雜鏈表的複製

27.二叉搜索樹和雙向鏈表

28.字符串的排列

29.數組中出現次數超過一半的數字

30.最小的k個數

31.連續數組的最大和

32.整數1出現的個數

33.把數組排成最小數

34.醜數

35.第一次只出現一次的字符

36.數組的逆序對

37.兩個鏈表的第一個公共結點

38.數字在排序數組中出現的次數

39.二叉樹的深度

40.平衡二叉樹

40.數組中只出現一次的數字

41.和爲s 的兩個數字

41.和爲s 的連續正序數列

42.左旋轉字符串

43.翻轉單詞序列

44.撲克牌的順子



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 []
        
        result = [] #存儲結果
        queue = [] #實現隊列
        queue.append(root)
        while queue:
            curNode = queue.pop(0)
            result.append(curNode.val)
            if curNode.left:
                queue.append(curNode.left)
            if curNode.right:
                queue.append(curNode.right)

        return result

24.二叉搜索樹的後序遍歷序列

輸入一個整數數組,判斷該數組是不是某二叉搜索樹的後序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同。(二叉搜索樹的中序遍歷是有序的)

根據後續遍歷的性質,尾元素必定是樹的根,同時小於尾元素的值是左子樹,大於尾元素的值爲右子樹,且序列前半部分均小於尾元素,後半部分均大於尾元素(如果同時存在左右子樹的話),可以將序列劃分左子樹序列和右子樹序列,然後遞歸比較是否每一段均滿足此性質。減少遞歸深度的辦法:某段的元素個數如果<=3,則返回True;某整段的最小元素不小於尾元素或者整段的最大元素不大於尾元素,說明僅有左子樹或者右子樹,返回True。

整數序列的最後一個值是根結點,然後比根結點小的值是左子樹,比根節點大的是右子樹,遞歸左右子樹

如序列: 1,3,2,5,6,7,4

# -*- coding:utf-8 -*-
class Solution:
    def VerifySquenceOfBST(self, sequence):
        # write code here
        length = len(sequence)
        if length == 0:
            return False
        else:
            root = sequence[-1] #原始數組中最後一個元素爲根節點
            left = 0
            while sequence[left] < root:  #找到最左邊的元素索引 加1,因爲 :left是開區間
                left += 1
            right = left 
            while right < length-1:
                if sequence[right] < root: #如果存在根節點都比右結點還大的數,則爲False
                    return False
                right += 1
            if left == 0:
                return True 
            else:
                left_ret =  self.VerifySquenceOfBST(sequence[:left])
            if left == right: 
                return True 
            else:
                right_ret = self.VerifySquenceOfBST(sequence[left: right])
                
            return left_ret and right_ret

25.二叉樹中和爲某一值的路徑

輸入一顆二叉樹的跟節點和一個整數,打印出二叉樹中結點值的和爲輸入整數的所有路徑。路徑定義爲從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。(注意: 在返回值的list中,數組長度大的數組靠前)

輸入一棵二叉樹和一個值,求從根結點到葉結點的和等於該值的路徑

#使用深度優先的方法

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None
class Solution:
    # 返回二維列表,內部每個列表表示找到的路徑
    def FindPath(self, root, expectNumber):
        # write code here
        #使用深度優先方法
        if not root:
            return []
        
        res, path, sums = [], [], [] #分別保存結果,結點路徑, 每個結點路徑和
        path.append(root)
        sums.append(root.val)
        
        def dfs(root):# 深度優先遍歷全部結點,
            if root.left:
                path.append(root.left)
                sums.append(sums[-1] + root.left.val)
                dfs(root.left)
                
            if root.right:
                path.append(root.right)
                sums.append(sums[-1] + root.right.val)
                dfs(root.right)
                
            if not root.left and not root.right:
                if sums[-1] == expectNumber:
                    res.append([p.val for p in path])
            print([p.val for p in path])        
            print(sums)
            path.pop() #去除做結點, 使其遍歷 右結點
            sums.pop()
            
        dfs(root)
        return res
    
#     def FindPath(self, root, expectNumber):
#         # write code here
#         if not root:
#             return []
#         result = []
#         def FindPathMain(root, path, currentSum):
#             currentSum += root.val
#             path.append(root)
#             isLeaf = root.left == None and root.right == None
#              
#             if currentSum == expectNumber and isLeaf:
#                 onePath = []
#                 for node in path:
#                     onePath.append(node.val)
#                 result.append(onePath)
#              
#             if currentSum < expectNumber:
#                 if root.left:
#                     FindPathMain(root.left, path, currentSum)
#                 if root.right:
#                     FindPathMain(root.right, path, currentSum)
#             path.pop()
#             
#         FindPathMain(root, [], 0)
#         return result

pNode1 = TreeNode(10)
pNode2 = TreeNode(5)
pNode3 = TreeNode(12)
pNode4 = TreeNode(4)
pNode5 = TreeNode(7)


pNode1.left = pNode2
pNode1.right = pNode3
pNode2.left = pNode4
pNode2.right = pNode5


S = Solution()
print(S.FindPath(pNode1, 22))

26.複雜鏈表的複製

輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果爲複製後複雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,否則判題程序會直接返回空)

此題有點複雜呀,指針指來指去的(記住每個while循環都要更新結點)

# -*- coding:utf-8 -*-
# class RandomListNode:
#     def __init__(self, x):
#         self.label = x
#         self.next = None
#         self.random = None
class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if not pHead:
            return None 
        self.cloneNodes(pHead)
        self.connectRandomNodes(pHead)
        ret = self.splitNodes(pHead)
        return ret
        
    def cloneNodes(self, pHead):
        #第一步驟:複製結點
        dummy = pHead 

        while dummy:
            dummyNext = dummy.next 
            copyNode = RandomListNode(dummy.label) #創建複製的結點
            #dummy.next = copyNode 
            #copyNode.next = dummyNext 搞錯方向
            copyNode.next = dummyNext 
            dummy.next = copyNode 

            dummy = dummyNext # 更新dummy結點
    
    def connectRandomNodes(self, pHead):
        #第二步驟,複製random結點
        dummy = pHead 
        while dummy:
            dummyRandom = dummy.random 
            copyNode = dummy.next 
            if dummyRandom: #如果random存在的話
                copyNode.random = dummyRandom.next 
            dummy = copyNode.next  #更新dummy結點

    def splitNodes(self, pHead):
        #第三步驟,分裂複製的鏈表
        dummy = pHead 
        cloneNode = pHead.next  #最後返回的複製結點的頭指針
        while dummy:
            copyNode = dummy.next 
            dummyNext = copyNode.next 
            dummy.next = dummyNext #取出原始的結點
            if dummyNext:
                copyNode.next = dummyNext.next  #1取出複製的結點
            else:
                copyNode.next = None
            dummy = dummyNext #更新結點
        return cloneNode

27.二叉搜索樹和雙向鏈表

輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。

我認爲最容易懂的代碼:

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def Convert(self, pRootOfTree):
        # write code here
        if not pRootOfTree:
            return None 
        self.li = []
        self.middle(pRootOfTree)
        for i in range(len(self.li) -1):
            self.li[i].right = self.li[i+1]
            self.li[i+1].left = self.li[i]
            
        return self.li[0]#返回首結點
        
    def middle(self, root):
        if not root:
            return 
        self.middle(root.left)
        self.li.append(root)
        self.middle(root.right)

參考的圖形:https://blog.csdn.net/u010005281/article/details/79657259

 

 

28.字符串的排列

輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。

輸入描述:

輸入一個字符串,長度不超過9(可能有字符重複),字符只包括大小寫字母。

依次取一個元素,然後依次和之前遞歸形成的所有子串組合,形成新的字符串

# -*- coding:utf-8 -*-
class Solution:
    def Permutation(self, ss):
        # write code here
        if not ss:
            return []
        if len(ss) == 1:
            return ss 

        ssList = list(ss)
        ssList.sort() #將 字符串轉換成列表並排序
        ret = []
        for i in range(len(ssList)):    #依次取出一個字符,其餘字符進行遞歸操作
            if i > 0 and ssList[i] == ssList[i-1]: #如果存在字符相同的情況
                continue 
            temp = self.Permutation(''.join(ssList[: i]) + ''.join(ssList[i+1: ]))
            for j in temp:
                ret.append(ssList[i] + j)

        return ret 

29.數組中出現次數超過一半的數字

數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度爲9的數組{1,2,3,2,2,2,5,4,2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。

#使用哈希表   思路: 使用hash,key是數字,value是出現的次數

# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here

        #使用哈希表
        if not numbers:
            return 0

        hashes = {}
        length = len(numbers)
        for num in numbers:
            if hashes.get(num): #如果num存在字典中
                hashes[num] += 1
            else:
                hashes[num] = 1
            if hashes[num] > (length // 2):
                return num 
        return 0

30.最小的k個數

輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。

兩種方法。

第一種方法是基於劃分的方法,如果是查找第k個數字,第一次劃分之後,劃分的位置如果大於k,那麼就在前面的子數組中進行繼續劃分,反之則在後面的子數組繼續劃分,時間複雜度O(n);

第二種方法是可以適用於海量數據的方法,該方法基於二叉樹或者堆來實現,首先把數組前k個數字構建一個最大堆,然後從第k+1個數字開始遍歷數組,如果遍歷到的元素小於堆頂的數字,那麼久將換兩個數字,重新構造堆,繼續遍歷,最後剩下的堆就是最小的k個數,時間複雜度O(nlog k)。

當數組較小的時候,直接使用庫返回最小的n 個元素

# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here

        import heapq 
        length = len(tinput)

        if k > length:
            return []
        return heapq.nsmallest(k, tinput)

使用最大堆

# -*- coding:utf-8 -*-
class Solution:
    def GetLeastNumbers_Solution(self, tinput, k):
        # write code here
        import heapq 
        length = len(tinput)
        if not tinput or length < k or k <= 0:
            return []
        if length == k:
            tinput.sort()
            return tinput
        
        output = []
        for num in tinput:
            if len(output) < k:
                output.append(num)
            else:
                output = heapq.nlargest(k, output)
                
                if output[0] < num:
                    continue 
                else:
                    output[0] = num 
                
        return output[::-1]

31.連續數組的最大和

HZ偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會後,他又發話了:在古老的一維模式識別中,常常需要計算連續子向量的最大和,當向量全爲正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,並期望旁邊的正數會彌補它呢?例如:{6,-3,-2,7,-15,1,2,2},連續子向量的最大和爲8(從第0個開始,到第3個爲止)。給一個數組,返回它的最大連續子序列的和,你會不會被他忽悠住?(子向量的長度至少是1)

# -*- coding:utf-8 -*-
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        if not array:
            return 
        ret = float('-inf')
        cur = 0
        for num in array:
            if cur <= 0: #如果計算得到前面所有值爲負的話,捨棄前面數值
                cur = num 
            else:
                cur += num 
            ret = max(ret, cur) #保存,並更新最大值
        return ret 

動態規劃解答:

# -*- coding:utf-8 -*-
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        # write code here
        if not array:
            return 
        
        # 使用動態規劃的方法
        ret = [0] * len(array)
        for i in range(len(array)):
            if i == 0 or ret[i-1] <= 0: 
                ret[i] = array[i]
            else:
                ret[i] = ret[i-1] + array[i] #
                
        return max(ret)

32.整數1出現的個數

求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?爲此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數(從1 到 n 中1出現的次數)。

1.將數字轉換成字符串

# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        # 將數字轉換成字符串
        count = 0
        for i in range(1, n+1):
            for s in str(i):
                if s == '1':
                    count += 1
        return count

 

33.把數組排成最小數

輸入一個正整數數組,把數組裏所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。例如輸入數組{3,32,321},則打印出這三個數字能排成的最小數字爲321323。



這裏自定義一個比較大小的函數,比較兩個字符串s1, s2大小的時候,先將它們拼接起來,比較s1+s2,和s2+s1那個大,如果s1+s2大,那說明s2應該放前面,所以按這個規則,s2就應該排在s1前面。(核心)

# -*- coding:utf-8 -*-
class Solution:
    def PrintMinNumber(self, numbers):
        # write code here
        if not numbers:
            return ''
        strNum = [str(num) for num in numbers]
        
        for i in range(len(numbers)-1):
            for j in range(i+1, len(numbers)):
                if strNum[i]+strNum[j] > strNum[j]+strNum[i]:
                    strNum[i], strNum[j] = strNum[j], strNum[i]
                    
        return ''.join(strNum)

34.醜數

把只包含質因子2、3和5的數稱作醜數(Ugly Number)。例如6、8都是醜數,但14不是,因爲它包含質因子7。 習慣上我們把1當做是第一個醜數。求按從小到大的順序的第N個醜數。


首先從醜數的定義我們知道,一個醜數的因子只有2,3,5,那麼醜數p = 2 ^ x * 3 ^ y * 5 ^ z,換句話說一個醜數一定由另一個醜數乘以2或者乘以3或者乘以5得到,那麼我們從1開始乘以2,3,5,就得到2,3,5三個醜數,在從這三個醜數出發乘以2,3,5就得到4,6,10,6,9,15,10,15,25九個醜數

# -*- coding:utf-8 -*-
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if not index: #如果index 爲0 時,則返回 0
            return 0 
        
        uglyNumbers = [1] * index
        
        index2, index3, index5 = 0, 0, 0
        nextIndex = 1
        while nextIndex < index: 
            minVal = min(uglyNumbers[index2] * 2, uglyNumbers[index3]*3, uglyNumbers[index5]*5)
            uglyNumbers[nextIndex] = minVal 
            
            while uglyNumbers[index2] *2 <= minVal: #如果小於最小值,則向前移動
                index2 += 1
            while uglyNumbers[index3] * 3 <= minVal:
                index3 += 1
            while uglyNumbers[index5] * 5 <= minVal:
                index5 += 1
                
            nextIndex += 1
        return uglyNumbers[-1]

35.第一次只出現一次的字符

在一個字符串(0<=字符串長度<=10000,全部由字母組成)中找到第一個只出現一次的字符,並返回它的位置, 如果沒有則返回 -1(需要區分大小寫).

#使用哈希表進行儲存

# -*- coding:utf-8 -*-
class Solution:
    def FirstNotRepeatingChar(self, s):
        # write code here
        if not s:
            return -1
        hashes = {} #使用哈希表進行存儲
        alist = list(s)
        length = len(alist)
        
        for i in range(length):
            if not hashes.get(alist[i]): #如果 i 不存在的話
                hashes[alist[i]] = 1
            else:
                hashes[alist[i]] += 1
        
        for i in range(length):
            if hashes[alist[i]] == 1:
                return i 
        return -1

36.數組的逆序對

在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007

輸入描述:

題目保證輸入的數組中沒有的相同的數字

數據範圍:

對於%50的數據,size<=10^4

對於%75的數據,size<=10^5

對於%100的數據,size<=2*10^5

示例1

輸入

1,2,3,4,5,6,7,0

輸出

7

要找到數組中的逆序對,可以看做對數據進行排序,需要交換數組中的元素的次數,但是防止相同大小的元素髮生交換,因此需要選擇一個穩定的排序方法,記錄發生交換的次數。那麼,基於比較的穩定的排序方法中,最快的方法就是歸併了,所以直接按照歸併排序的思路,將數組分解、合併、排序即可。但是需要注意的是,在常規歸併排序的時候,如果前一個元素大於後一個元素,直接進行交換即可,只進行了一次操作,但是對於這道題來講,對於每一次的歸併段,我們選擇從後向前遍歷,前面的歸併段的某一個數值left[i]如果大於後面的某一個數值right[j],因爲在right自己獨自排序的過程中,已經保證了right是有序的,所以j位置前面的數字全部小於right[j],所以在這裏逆序對的個數就會是 j-start-length,其中start是整個數組的起點,length是left的長度,然後再進行交換。

暫定,複習下七種排序算法

1.使用排序方法:時間複雜度: O(n)

li = [5, 6, 4, 3, 2, 7, 9] # 3, 3, 2, 1,
# li = [1, 2, 3, 4, 5, 6, 7, 0]
def reverseOrderNum(li):
    n = len(li)
    index = 0
    for i in range(n-1):
        for j in range(i+1, n):
            if li[i] > li[j]:
                index += 1
    return index
print(reverseOrderNum(li))

2.使用歸併方法:時間複雜度:O(nlogn)

做排序算法時,歸併都是返回一個排序好的數組

class Solution:

    def InversePairs(self, data):
        if not data or len(data) == 1:
            return data
        copy = [0] * len(data)
        count = self.InversePairsCore(data, copy, 0, len(data)-1)
        # print(copy)
        return count

    def InversePairsCore(self, data, copy, low, high):
        if low == high:
            return 0
        mid = (high + low) // 2
        leftCount = self.InversePairsCore(data, copy, low, mid) % 1000000007
        rightCount = self.InversePairsCore(data, copy, mid+1, high) % 1000000007
        count = 0
        i, j = mid, high
        locCopy = high
        while i >= low and j > mid:
            if data[i] > data[j]:
                count += j - mid
                copy[locCopy] = data[i]
                locCopy -= 1
                i -= 1
                if count >= 1000000007:
                    count %= 1000000007
            else:
                copy[locCopy] = data[j]
                locCopy -= 1
                j -= 1
        for ii in range(i, low-1, -1):
            copy[locCopy] = data[ii]
            locCopy -= 1
        for jj in range(j, mid-1, -1):
            copy[locCopy] = data[jj]
            locCopy -= 1
        for s in range(low, high+1):
            data[s] = copy[s]
        return leftCount + rightCount + count

 

37.兩個鏈表的第一個公共結點

輸入兩個鏈表,找出它們的第一個公共結點。(感覺牛客網出的題目描述的沒有leetcode清楚呀)

公共結點的意思是兩個鏈表相遇之後後面都是一樣的

思路: 先獲取到兩個鏈表的長度,然後長的鏈表先走多的幾步,之後一起遍歷

 

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
        if not pHead1 or not pHead2:
            return 
        
        move1, move2 = pHead1, pHead2
        length1, length2 = 0, 0
        while move1: #計算鏈表1 的長度
            length1 += 1
            move1 = move1.next 
            
        while move2: #計算鏈表2 的長度
            length2 += 1
            move2 = move2.next 
        
        while length1 > length2:
            pHead1 = pHead1.next 
            length1 -= 1
        
        while length2 > length1:
            pHead2 = pHead2.next 
            length2 -= 1
            
        while pHead1: #兩個鏈表一起往前走
            if pHead1 == pHead2:
                return pHead1
            pHead1, pHead2 = pHead1.next, pHead2.next 
            
        return None 

38.數字在排序數組中出現的次數

統計一個數字在排序數組中出現的次數。

使用二分法分別找到數組中第一個和最後一個出現的值的座標,然後相減 + 1

# -*- coding:utf-8 -*-
class Solution:
    def GetNumberOfK(self, data, k):
        # write code here
        #return data.count(k)
        if not data:
            return 0
        
        left = self.get_first_k(data, k)
        right = self.get_last_k(data, k)
        if left < 0 and right < 0:
            return 0
        if left < 0 or right < 0: # 如果左邊有一個不存在,代表另外一個是存在的,所以 返回1 個
            return 1 
        return right -left + 1
        
        
    def get_first_k(self, data, k):
        left, right = 0, len(data)-1
        while left <= right:
            mid = (left + right) // 2
            if data[mid] < k:
                if mid+1 < len(data) and data[mid+1] == k: #如果後一位數字等於k 的話
                    return mid + 1
                left = mid + 1
            elif data[mid] == k:
                if mid-1 < 0 or (mid-1 >= 0 and data[mid-1] < k):#確保是獲得左邊的第一個索引,如果mid是0的話,如果 前一個值小於k時
                    return mid 
                right = mid - 1
            else:
                right = mid - 1
        return -1
                
    def get_last_k(self, data, k):
        left, right = 0, len(data)-1
        while left <= right:
            mid = (left + right) // 2
            if data[mid] < k:
                left = mid + 1
            elif data[mid] == k:
                if mid + 1 == len(data) or (mid+1 < len(data) and data[mid+1] > k):#如果索引是length-1的話,如果後一位大於k的話
                    return mid 
                left = mid + 1
            else: #如果中間值 大於 k的時候
                if mid -1 >= 0 and data[mid-1] == k:
                    return mid -1 
                right = mid - 1
        return -1

 

39.二叉樹的深度

輸入一棵二叉樹,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度爲樹的深度。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def TreeDepth(self, pRoot):
        # write code here
        if not pRoot:
            return 0
        #if not pRoot.left and not pRoot.right: #可要可不要
           # return 1
        return 1 + max(self.TreeDepth(pRoot.left), self.TreeDepth(pRoot.right))

 

40.平衡二叉樹

輸入一棵二叉樹,判斷該二叉樹是否是平衡二叉樹。

基於二叉樹的深度,再次進行遞歸。以此判斷左子樹的高度和右子樹的高度差是否大於1,若是則不平衡,反之平衡。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def IsBalanced_Solution(self, pRoot):
        # write code here
        if not pRoot:
            return True 
        left = self.get_depth(pRoot.left)
        right = self.get_depth(pRoot.right) #獲取左右子樹的深度
        if abs(right - left) > 1:
            return False
        return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
        
    def get_depth(self, pRoot): #獲取樹的深度
        if not pRoot:
            return 0
        return 1 + max(self.get_depth(pRoot.left), self.get_depth(pRoot.right))

鏈接:https://www.nowcoder.com/questionTerminal/8b3b95850edb4115918ecebdf1b4d222
來源:牛客網

在判斷上層結點的時候,會多次重複遍歷下層結點,增加了不必要的開銷。如果改爲從下往上遍歷,如果子樹是平衡二叉樹,則返回子樹的高度;如果發現子樹不是平衡二叉樹,則直接停止遍歷,這樣至多隻對每個結點訪問一次。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def IsBalanced_Solution(self, pRoot):
        # write code here
        return self.helper(pRoot) != -1
        
    
    def helper(self, pRoot):
        if not pRoot:
            return 1 
        left = self.helper(pRoot.left)
        if left == -1:
            return -1
        right = self.helper(pRoot.right)
        if right == -1:
            return -1
        if abs(right - left) > 1: #如果左右結點的相對值大於 1時,則不是平衡樹
            return -1
        return 1+ max(self.helper(pRoot.left), self.helper(pRoot.right))

40.數組中只出現一次的數字

一個整型數組裏除了兩個數字之外,其他的數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。

使用哈希表

# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出現一次的兩個數字
    def FindNumsAppearOnce(self, array):
        # write code here’
        if not array:
            return 
        hashes = {}
        for num in array:
            if num not in hashes.keys():
                hashes[num] = 1
            else:
                hashes[num] += 1
        ret = []
        for num in array:
            if hashes[num] == 1:
                ret.append(num)
                
        return ret

使用異或

任何一個數字異或他自己都等於0,0異或任何一個數都等於那個數。數組中出了兩個數字之外,其他數字都出現兩次,那麼我們從頭到尾依次異或數組中的每個數,那麼出現兩次的數字都在整個過程中被抵消掉,那兩個不同的數字異或的值不爲0,也就是說這兩個數的異或值中至少某一位爲1。我們找到結果數字中最右邊爲1的那一位i,然後一次遍歷數組中的數字,如果數字的第i位爲1,則數字分到第一組,數字的第i位不爲1,則數字分到第二組。這樣任何兩個相同的數字就分到了一組,而兩個不同的數字在第i位必然一個爲1一個不爲1而分到不同的組,然後再對兩個組依次進行異或操作,最後每一組得到的結果對應的就是兩個只出現一次的數字。

# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出現一次的兩個數字
    def FindNumsAppearOnce(self, array):
        # write code here’
        if not array:
            return []
        
        D_value = 0
        for num in array: #獲得不同的兩個數的  差值
            D_value ^= num 
        #獲取bit 位置
        index = 0
        while D_value & 1 == 0 and index < 32: #獲取D_value 的 bit 位置
            index += 1
            D_value = D_value >> 1
        a, b = 0, 0
        for j in range(len(array)):
            if self.isBit1(array[j], index): #這裏將兩個不同的數分成了 兩半
                a ^= array[j]
            else:
                b ^= array[j]
        return [a, b]
        
    def isBit1(self, num, index): # num主要是爲了計算 a, b
        num = num >> index
        return num & 1

41.和爲s 的兩個數字

題目描述

輸入一個遞增排序的數組和一個數字S,在數組中查找兩個數,使得他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。

輸出描述:

對應每個測試案例,輸出兩個數,小的先輸出。
從左右一起查找
  # 因爲當兩個數的和一定的時候, 兩個數字的間隔越大, 乘積越小
  # 所以直接輸出查找到的第一對數即可
# -*- coding:utf-8 -*-
class Solution:
    def FindNumbersWithSum(self, array, tsum):
        # write code here
        if not array:
            return []
        low, high = 0, len(array)-1

        while high > low:
            curSum = array[low] + array[high]
            if curSum > tsum:
                high -= 1
            elif curSum < tsum:
                low += 1
            else:
                return [array[low], array[high]]
        return []

 

 

41.和爲s 的連續正序數列

小明很喜歡數學,有一天他在做數學作業時,要求計算出9~16的和,他馬上就寫出了正確答案是100。但是他並不滿足於此,他在想究竟有多少種連續的正數序列的和爲100(至少包括兩個數)。沒多久,他就得到另一組連續正數和爲100的序列:18,19,20,21,22。現在把問題交給你,你能不能也很快的找出所有和爲S的連續正數序列? Good Luck!

輸出描述:

輸出所有和爲S的連續正數序列。序列內按照從小至大的順序,序列間按照開始數字從小到大的順序

 

雙指針技術,就是相當於有一個窗口,窗口的左右兩邊就是兩個指針,我們根據窗口內值之和來確定窗口的位置和寬度

# -*- coding:utf-8 -*-
class Solution:
    def FindContinuousSequence(self, tsum):
        # write code here
        if tsum < 3:
            return []
        low, high = 1, 2
        ret = []
        while low < high:
            cur = (high + low) * (high-low+1) / 2 #求和公式
            if cur == tsum:
                ret.append(range(low, high+1))
                low += 1
            elif cur < tsum: #如果當前值比總值小,則前往前走 
                high += 1
            else:
                low += 1  
                
        return ret 

42.左旋轉字符串

彙編語言中有一種移位指令叫做循環左移(ROL),現在有個簡單的任務,就是用字符串模擬這個指令的運算結果。對於一個給定的字符序列S,請你把其循環左移K位後的序列輸出。例如,字符序列S=”abcXYZdef”,要求輸出循環左移3位後的結果,即“XYZdefabc”。是不是很簡單?OK,搞定它!

# -*- coding:utf-8 -*-
class Solution:
    def LeftRotateString(self, s, n):
        # write code here
        if not s:
            return ''
        if len(s) <= n:
            return s 
        return s[n:] + s[:n]

複製一個s

class Solution:
    def LeftRotateString(self, s, n):
        # write code here
        if not s:
            return ''
        if len(s) <= n:
            return s 
        length = len(s)
        s += s
        return s[n : length+n]

43.翻轉單詞序列

牛客最近來了一個新員工Fish,每天早晨總是會拿着一本英文雜誌,寫些句子在本子上。同事Cat對Fish寫的內容頗感興趣,有一天他向Fish借來翻看,但卻讀不懂它的意思。例如,“student. a am I”。後來才意識到,這傢伙原來把句子單詞的順序翻轉了,正確的句子應該是“I am a student.”。Cat對一一的翻轉這些單詞順序可不在行,你能幫助他麼?

# -*- coding:utf-8 -*-
class Solution:
    def ReverseSentence(self, s):
        # write code here
        if s == ' ':
            return ' '
        if s == None or len(s) <= 0:
            return ''

        ts = s.split()
        ret = ''
        for i in range(len(ts)-1, -1, -1):
            if i == 0:
                ret += ts[i]
            else:
                ret += ts[i] + ' '
        return ret 

44.撲克牌的順子

LL今天心情特別好,因爲他去買了一副撲克牌,發現裏面居然有2個大王,2個小王(一副牌原本是54張^_^)...他隨機從中抽出了5張牌,想測測自己的手氣,看看能不能抽到順子,如果抽到的話,他決定去買體育彩票,嘿嘿!!“紅心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是順子.....LL不高興了,他想了想,決定大\小 王可以看成任何數字,並且A看作1,J爲11,Q爲12,K爲13。上面的5張牌就可以變成“1,2,3,4,5”(大小王分別看作2和4),“So Lucky!”。LL決定去買體育彩票啦。 現在,要求你使用這幅牌模擬上面的過程,然後告訴我們LL的運氣如何, 如果牌能組成順子就輸出true,否則就輸出false。爲了方便起見,你可以認爲大小王是0。

先置換特殊字符AJQK爲數字,排序,然後求出大小王即0的個數,然後求出除去0之外的,數組間的數字間隔(求間隔的時候記得減去1,比如4和5的間隔爲5-4-1,表示4和5是連續的數字),同時求間隔的時候需要鑑別是否出現對。最後比較0的個數和間隔的大小即可。

# 如果出現相同的數,則必定不是順子

# -*- coding:utf-8 -*-
class Solution:
    def IsContinuous(self, numbers):
        # write code here
        if not numbers:
            return 
        my_dict = {'A':1, 'J':11, 'Q':12, 'K':13}
        # 將數組的A,等轉換 成 數值, 並計算0 的個數
        zero_num = 0
        for i in range(len(numbers)):
            if numbers[i] in my_dict.keys():
                numbers[i] = my_dict[numbers[i]]
            if numbers[i] == 0:
                zero_num += 1
        # 對轉換後的數組進行排序
        numbers.sort()
        small = zero_num 
        big = small + 1
        gap_sum = 0
        while big < len(numbers):
            if numbers[small] == numbers[big]: #如果有相同的數的話,肯定不是順子
                return False 
            gap_sum += numbers[big] - numbers[small] -1
            small += 1
            big += 1
        if  gap_sum > zero_num:
            return False
        else:
            return True 

劍指offer(2/3)第一大部分

劍指offer(3/3)第三大部分

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