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