劍指offer在線編程(08-10)【6】

Date: 2019-08-10

1.  兩個鏈表的第一個公共結點 (考察知識點:鏈表)

題目描述

輸入兩個鏈表,找出它們的第一個公共結點。

分析思路:  剛開始的想法是暴力解決,每一個節點和另一個鏈表的每一個節點進行比較,這樣算法的時間複雜度是O(length1*length2)。看到牛客上別人的解釋,發現:第一個公共節點(節點的val和next相同)之後的所有節點應該是一樣的,即是有相同的尾部。則因此我們可以讓長度比較長的鏈表先走(next)他們的長度差,然後基於相同長度的鏈表進行一對一的比較 !

代碼如下:(寫代碼一定要記得分析題意,舉例兩個鏈表進行分析)

# -*- 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
        def Getlength(head):  # 先定義計算鏈表的長度的子函數
            length = 0
            while head != None:
                length += 1
                head = head.next
            return length 
        def Gostep(node, k):   # 定義讓長的鏈表先走k步的子函數
            while k != 0:
                node = node.next
                k -= 1
            return node
        
        length1 = Getlength(pHead1)  # 計算兩個鏈表的長度、計算兩者的長度差
        length2 = Getlength(pHead2)
        lengthdiff = abs(length1 - length2)
        if length1 >= length2:  # 如果鏈表1更長,則鏈表1先走
            Headlong = Gostep(pHead1, lengthdiff)
            Headshort = pHead2
        else:                  # 如果鏈表2更長,則鏈表2先走
            Headlong = Gostep(pHead2, lengthdiff)
            Headshort = pHead1
        while Headlong != None and Headshort != None and Headlong != Headshort:  # 一對一比較,不相等時則繼續向next進行遍歷比較,直到相等時停止。
            Headlong = Headlong.next
            Headshort = Headshort.next
        Commonnode = Headlong
        return Commonnode

2.  數字在排序數組中出現的次數 (考察知識: 數組和二分查找)

題目描述

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

分析: 剛開始看到題目到的初始想法就是直接data.count(k),代碼如下(通過了的)。但是轉念一想,這不是本題的考察角度,應該考察的是利用二分查找來找到k的第一個index和最後一個index,然後相減+1便是我們要求的次數。

# -*- coding:utf-8 -*-
class Solution:
    def GetNumberOfK(self, data, k):
        # write code here
        return data.count(k)
# -*- coding:utf-8 -*-(通過的代碼)
class Solution:
    def calfirstindex(self, data, k):
        low = 0
        high = len(data)-1
        while low <= high:
            mid = (low + high)//2  # 如果low < high ,則進行求中間的index
            if data[mid] > k:  # 中間index的元素大於k,則左移,減1
                high = mid -1
            elif data[mid] <k:  # 中間index的元素小於,則右移,加1
                low = mid + 1
            else: # 否則如果兩者相等
                if mid == low or data[mid-1] != k:  # 且mid等於low或者mid-1的元素並不等於k,則就是第一個元素爲k的index 
                    return mid
                else:  # 否則high進行mid-1的移動
                    high = mid -1
        return -1
    def callastindex(self, data, k):
        low = 0
        high = len(data)-1
        while low <= high:
            mid = (low + high)//2  # 如果low < high ,則進行求中間的index
            if data[mid] > k:  # 中間index的元素大於k,則左移,減1
                high = mid -1
            elif data[mid] <k:  # 中間index的元素小於,則右移,加1
                low = mid + 1
            else: # 否則如果兩者相等(mid的元素和k相等)
                if mid == high or data[mid+1] != k:  # 且mid等於high或者mid+1的元素並不等於k,則就是最後一個元素爲k的index 
                    return mid
                else:  # 否則low進行mid+1的移動
                    low = mid +1
        return -1
    
    def GetNumberOfK(self, data, k):
        # write code here
        if not data:
            return 0
        if self.callastindex(data,k) == -1 and self.calfirstindex(data,k) == -1:
            return 0
        return self.callastindex(data,k) - self.calfirstindex(data,k) +1

3.  二叉樹的深度   (考察知識點:樹+遞歸)

題目描述

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

分析:該題算是中等偏下難度的題目,主要是考察利用遞歸的思想對樹進行處理。第一種方法是:建立子函數計算深度:首先判斷根節點的左右子樹是否存在,

如果不存在,則只返回1;

如果左子樹不存在,則只有右子樹,因此往右遍歷計算深度;

如果右子樹不存在,則只有左子樹,因此往左遍歷計算深度,

否則遞歸計算左右子樹的深度,然後求max +1.

# -*- 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

第二種方法看上去更爲簡潔:

直接遞歸計算左右子樹的深度,然後比較即可

# -*- 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
        else:
            lefts = self.TreeDepth(pRoot.left)+1
            rights = self.TreeDepth(pRoot.right)+1
            return  max(lefts, rights)

4.  平衡二叉樹  (考察知識點: 樹+遞歸調用)

題目描述

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

分析:自己初始寫了一個版本,雖然通過了,但是感覺有問題。

首先平衡二叉樹的判斷條件: 

平衡二叉樹(Balanced Binary Tree),具有以下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。並沒有要求左子樹小於根節點, 右子樹大於根節點。

自己版本的思路:首先建立一個子函數計算深度,如果樹爲空,則爲平衡樹,否則計算左右子樹的深度,判斷是否滿足平衡二叉樹的條件!

問題在於:1)沒有遞歸判斷左右子樹是否也是平衡二叉樹,只是計算了根節點的左右子樹深度是否滿足條件(但是也通過);

                  2) 即是第二版本的答案考慮全面了,但是其是從上到下的一種解法,有很多重複計算的過程!

                  3)第三個版本採用自下而上的思想,避免重複,如果葉節點往上的左/右子樹不平衡,則整棵樹就不平衡了。注意的是: 子樹不平衡時,返回-1.

# 版本一的解法,雖然通過了,但是自我感覺沒有考慮全面。
# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def caldepth(self, root):
        if not root:
            return 0
        else:
            lefts = self.caldepth(root.left)+1
            rights = self.caldepth(root.right)+1
            return max(lefts, rights)
        
    def IsBalanced_Solution(self, pRoot):
        # write code here
        if not pRoot:
            return  True
        else:
            leftdepth = self.caldepth(pRoot.left)
            rightdepth = self.caldepth(pRoot.right)
            diff = abs(leftdepth-rightdepth)
            if diff <=1:
                return True
            else:
                return False

方法一:自頂向下,對於每個節點,都計算一下左子樹以及右子樹的差的絕對值,即每個節點都判斷一下。算法複雜度爲O(N*2)

# -*- coding:utf-8 -*- 自上而下的全面版本,但是有較多重複計算
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def caldepth(self, root):
        if not root:
            return 0
        else:
            lefts = self.caldepth(root.left)+1
            rights = self.caldepth(root.right)+1
            return max(lefts, rights)
        
    def IsBalanced_Solution(self, pRoot):
        # write code here
        if not pRoot:
            return  True
        else:
            leftdepth = self.caldepth(pRoot.left)
            rightdepth = self.caldepth(pRoot.right)
            diff = abs(leftdepth-rightdepth)
            return diff <=1 and self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)

方法二:自下往上  算法複雜度O(N) 注意:子樹不平衡時返回-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, p):
        return self.dfs(p) != -1
    def dfs(self, p):
        if p is None:
            return 0
        left = self.dfs(p.left)
        if left == -1:
            return -1
        right = self.dfs(p.right)
        if right == -1:
            return -1
        if abs(left - right) > 1:
            return -1
        return max(left, right) + 1

5.  數組中只出現一次的數字  (考察知識點:數組+邏輯)

題目描述

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

分析:該題應該有很多方法,首先看到該題想到的是利用count內置函數進行頻次計算。遍歷set(array),計算array中每個互異元素的頻次,如果頻次等於1,則放入res列表中,並且因爲題目中談到了只有兩個僅僅出現了一次,所以如果len(res)==2時,可以直接返回。  但是:沒有充分利用其他數字都出現了兩次這一條件,所以下一版本不用count函數,且利用前面這個條件。

# -*- coding:utf-8 -*- 版本1:時間複雜度低一點,但是利用了內置函數count()
class Solution:
    # 返回[a,b] 其中ab是出現一次的兩個數字
    def FindNumsAppearOnce(self, array):
        # write code here
        if not array:
            return False
        res = []
        for k in set(array):
            if array.count(k) == 1:
                res.append(k)
                if len(res) == 2:
                    return res

充分考慮條件:遍歷array中的每一個元素,第一次遇到k元素時,將其放入res中,第二次遇到時,將其從res中移除,而只出現一次的元素會一直留在res中。(只出現一次的數字有兩個,而其他數字都只出現2次)

# -*- coding:utf-8 -*-
class Solution:
    # 返回[a,b] 其中ab是出現一次的兩個數字
    def FindNumsAppearOnce(self, array):
        # write code here
        if not array:
            return False
        res = []
        for k in array:
            if k in res:
                res.remove(k)
            else:
                res.append(k)
        return res

看到牛客上還有人說利用異或的思想,即一個相同的元素出現兩次,則第一個k的異或和k會相互抵消,但對於只出現一次的元素而言,無法進行抵消!

6.  和爲S的連續正數序列  (考察:數組+窮舉+數學)

題目描述

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

輸出描述:

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

方法一(雙指針):

用small和big分別表示序列的左值和右值,首先將small初始化爲1,big初始化爲2;


若[small, big]之和 > S,從序列中去掉第一個值(增大small);
若和 < S,增大big,和中加入新的big;
若等於S,將[small, big] 納入到結果集中;

# -*- coding:utf-8 -*-  雙指針的思想時間複雜度更低一些
class Solution:
    def FindContinuousSequence(self, tsum):
        if tsum <3:
            return []
        small = 1  # 初始指針small爲1
        big = 2    #初始指針big爲1
        Cursum = small + big  #累計子序列的和
        middle = (tsum + 1) // 2  # 計算S的二等分值,作爲while依據
        output = []
        while small < middle:
            if Cursum == tsum:  # 如果子序列和等於s,則直接返回子序列,注意big+1,但是隻取到了big
                output.append(range(small, big+1))
                big += 1
                Cursum += big
            elif Cursum > tsum:  # 當前子序列和大於S,則捨棄到最小值
                Cursum -= small
                small += 1
            elif Cursum < tsum:  # 當前子序列小於S,增加最大值big
                big += 1
                Cursum += big
        return output

方法二:採用等差數列的思想:

根據所學知識,有:

(a + b)(b - a + 1) = 2 * sum;此爲等差數列公式。

令i = b - a + 1(項數), j = a + b(首末項之和);現討論取值範圍。i >= 2(序列中至少有2項), j >= 3(序列之和至少爲3);隱藏的關係是: j > i同時還有 i * j = 2 * sum,進行放縮之後就有 i * i < 2 * sum,即 i < 根號(2 * sum)。對i進行遍歷,找出i,j∈正整數且j - i + 1爲偶的取值。
 

# -*- coding:utf-8 -*- 等差數列知識
class Solution:
    def FindContinuousSequence(self, tsum):
        # write code here
        res=[]
        for i in range(1,tsum/2+1):  # I是首項,j是尾項;首尾項之和*((j-i+1)/2) 爲當前子序列的和
            for j in range(i,tsum/2+2):
                tmp=(j+i)*(j-i+1)/2
                if tmp>tsum:
                    break
                elif tmp==tsum:
                    res.append(range(i,j+1))
        return res

 

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