把草稿箱裏文章拿出來分享一下。
目錄
第二十三題 (N)(舉例讓抽象具體化) 二叉搜索樹的後序遍歷序列
第二十六題 (N)(分解讓複雜問題簡單) 二叉搜索樹與雙向鏈表
第三十一題 (時間效率)整數中1出現的次數(從1到n整數中1出現的次數)
第四十六題 (N)(抽象建模能力)孩子們的遊戲(圓圈中最後剩下的數)
第一題 (數組)二維數組中的查找
題目描述
在一個二維數組中(每個一維數組的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。
方法一
最粗暴的做法,對數組中的數字一個個遍歷過去。最差情況下的時間複雜度是O(rows*cols)
# -*- coding:utf-8 -*-
class Solution:
# array 二維列表
def Find(self, target, array):
# write code here
rows = len(array) # 行數
cols = len(array[0]) # 列數
for i in range(rows):
for j in range(cols):
if(array[i][j] == target):
return True
return False
方法二
考慮題目本身的的特性,每行每列都是有序的數組
每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序
# -*- coding:utf-8 -*-
class Solution:
# array 二維列表
def Find(self, target, array):
# write code here
rows = len(array) # 行數
cols = len(array[0]) # 列數
i = 0
j = cols-1
while i<rows and j>=0:
if(array[i][j] == target):
return True
elif(array[i][j] > target):
j -= 1
elif(array[i][j] < target):
i += 1
return False
第二題 (字符串) 替換空格
題目描述
請實現一個函數,將一個字符串中的每個空格替換成“%20”。例如,當字符串爲We Are Happy.則經過替換之後的字符串爲We%20Are%20Happy。
看到題目的第一反應是找到空格直接替換就行了。簡單粗暴。
python的replece方法一句話
str.replace(old, new[, max])
返回字符串中的 old(舊字符串) 替換成 new(新字符串)後生成的新字符串,如果指定第三個參數max,則替換不超過 max 次。
方法一
# -*- coding:utf-8 -*-
class Solution:
# s 源字符串
def replaceSpace(self, s):
# write code here
s = s.replace(' ','%20')
return s
# s = Solution()
# print(s.replaceSpace('We Are Happy'))
方法二
# -*- coding:utf-8 -*-
class Solution:
# s 源字符串
def replaceSpace(self, s):
# write code here
temp = ""
for i in range(len(s)):
if s[i] == ' ':
temp += '%20'
else:
temp += s[i]
return temp
第三題 (鏈表) 從尾到頭打印鏈表
題目描述
輸入一個鏈表,按鏈表值從尾到頭的順序返回一個ArrayList。
借用一個列表來存放值
class Solution:
# 返回從尾部到頭部的列表值序列,例如[1,2,3]
def printListFromTailToHead(self, listNode):
# write code here
alist = []
if listNode is None:
return alist
p = listNode
while p.next:
alist.append(p.val)
p = p.next
alist.append(p.val)
alist.reverse()
return alist
第四題 (樹) 重建二叉樹
題目描述
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。
前序確定根節點,中序根據根節點確定左右節點
class Solution:
# 返回構造的TreeNode根節點
def reConstructBinaryTree(self, pre, tin):
# write code here
if not pre and not tin:
return None
root = TreeNode(pre[0])
rootid = tin.index(root.val)
root.left = self.reConstructBinaryTree(pre[1: rootid+1], tin[:rootid])
root.right = self.reConstructBinaryTree(pre[rootid+1:], tin[rootid+1:])
return root
第五題 (棧和隊列) 用兩個棧實現隊列
題目描述
用兩個棧來實現一個隊列,完成隊列的Push和Pop操作。 隊列中的元素爲int類型。
class Solution:
def __init__(self):
self.s1 = []
self.s2 = []
def push(self, node):
# write code here
self.s1.append(node)
def pop(self):
# return xx
if self.s2:
return self.s2.pop()
else:
while self.s1:
self.s2.append(self.s1.pop())
return self.s2.pop()
python自帶的append和pop可以模擬實現棧和隊列
# 對於一個列表stack
stack = [3, 4, 5]
# 進棧
stack.append(n)
# 出棧
stack.pop()
# 對於一個隊列queue
queue = [1, 2, 3]
# 進隊列
queue.append(n)
# 出隊列
queue.pop(0)
第六題 (查找和排序) 旋轉數組的最小數字
題目描述
把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。 輸入一個非減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。 NOTE:給出的所有元素都大於0,若數組大小爲0,請返回0。
就是求出數組裏面最小的那個
方法一
最簡單的使用python的內置函數,不過這樣這道題就失去意義了。
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
m = min(rotateArray)
return m
方法二
先排序再取第一個值
排序也有內置函數sort()可以使用。
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
if not rotateArray:
return 0
else:
rotateArray.sort()
return rotateArray[0]
自己寫一個快排提交出現了錯誤。
快排在已排序或者數值全部相等情況下最差,複雜度爲O(n^2),退化成冒泡排序。
方法三
根據數組本身具有的特性
class Solution:
def minNumberInRotateArray(self, rotateArray):
# write code here
length = len(rotateArray)
if length == 0:
return 0
elif length == 1:
return rotateArray[0]
else:
for i in range(length - 1):
if rotateArray[i] > rotateArray[i + 1]:
return rotateArray[i + 1]
return rotateArray[length - 1]
方法四
二分查找,參考牛客‘XD’的答案
根據題意說明是一個遞增數組的旋轉,所以如題所示【3,4,5】,【1,2】還是局部遞增的,在這種的數組中查找,一般選擇二分的方法;基本模型有了,下面試着分析:
1.先取出中間的數值,和最後一個比較5>2 說明mid之前的某些部分旋轉到了後面,所以下次尋找 low = mid+1 開始;
2.取出的中間值要是小於high,說明mid-high之間都應爲被旋轉的部分,所以最小應該在mid的前面,但是也有可能當前的mid 就是最小的值 所以下次需找的應該 從mid開始,也即high = mid 開始
3.當*mid == *high的時候,說明數組中存在着相等的數值,可能是這樣的形式 【2,2,2,2,1,2】所以應該選擇的high 應該遞減1 作爲下次尋找的上界。
class Solution:
def minNumberInRotateArray(self, rotateArray):
if len(rotateArray) == 0:
return 0
l = 0
r = len(rotateArray)-1
mid = -1
if rotateArray[l] == rotateArray[r]:
for i in range(len(rotateArray)-1):
if rotateArray[i] > rotateArray[i+1]:
return rotateArray[i+1]
while rotateArray[l] >= rotateArray[r]:
if l+1 == r:
mid = r
break
mid = (l+r)//2
if rotateArray[mid] <= rotateArray[r]:
r = mid
elif rotateArray[mid] >= rotateArray[l]:
l = mid
return rotateArray[mid]
第七題 (遞歸和循環) 斐波那契數列
題目描述
大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項(從0開始,第0項爲0)。
n<=39
採用遞歸做的時候運行超時了,所以採用非遞歸方式。
class Solution:
def Fibonacci(self, n):
# write code here
if n == 0 or n == 1:
return n
a = 0
b = 1
for i in range(2, n+1):
t = a + b
a = b
b = t
return t
第八題 (遞歸和循環) 跳臺階
題目描述
一隻青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法(先後次序不同算不同的結果)。
實際上也是斐波那契數列
class Solution:
def jumpFloor(self, number):
# write code here
a, b = 1, 1
for i in range(number):
a, b = b, a+b
return a
第九題 (遞歸和循環) 變態跳臺階
題目描述
一隻青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。
分析:
n級臺階,第一步有n種跳法:跳1級、跳2級、到跳n級
跳1級,剩下n-1級,則剩下跳法是f(n-1)
跳2級,剩下n-2級,則剩下跳法是f(n-2)
所以f(n)=f(n-1)+f(n-2)+...+f(1)
又因爲f(n-1)=f(n-2)+...+f(1)
所以f(n)=f(n-1)*2
class Solution:
def jumpFloorII(self, number):
# write code here
return 2**(number-1)
如果不是很理解,可以直接列舉幾個找規律
number=1 '1' 1種
number=2 '1+1;2' 2種
number=3 '1+1+1;1+2;2+1;3' 4種
number=4 '1+1+1+1;1+1+2;1+2+1;2+1+1;2+2;1+3;3+1;4' 8種
以此類推……
第十題 (遞歸和循環) 矩形覆蓋
題目描述
我們可以用2*1的小矩形橫着或者豎着去覆蓋更大的矩形。請問用n個2*1的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?
還是斐波那契數列,2*n的大矩形,和n個2*1的小矩形
class Solution:
def rectCover(self, number):
# write code here
if number <= 1:
return number
a, b = 1, 2
for i in range(2,number+1):
a, b = b, a+b
return a
可以直接列舉幾個找規律
n = 1 '|' 1
n=2 '||、=' 2
n=3 ’|||、|=、=|‘ 3
n=4 '||||、||=、|=|、=||、==’ 5
以此類推……
N第十一題 (位運算) 二進制中1的個數
題目描述
輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼錶示。
看題目第一眼想到取餘做,但是提交出錯了
整理一下思路,首先要判斷數是否爲正數,如果是負數要用補碼錶示,所以n & 0xffffffff,把負數變成正數。
方法一
class Solution:
def NumberOf1(self, n):
# write code here
if n<0:
n = n & 0xffffffff
return bin(n).count('1')
方法二
使用n & (n - 1)消去n最後一位的1,消了幾次就是n中有幾個1
class Solution:
def NumberOf1(self, n):
# write code here
count = 0
if n<0:
n = n & 0xffffffff
while n:
count += 1
n = (n-1)&n
return count
第十二題 (代碼的完整性) 數值的整數次方
題目描述
給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。
方法一
python只要一句話哎
class Solution:
def Power(self, base, exponent):
return base**exponent
方法二
class Solution:
def Power(self, base, exponent):
r = 1.0
if exponent == 0:
return 1
temp = abs(exponent)
for i in range(temp):
r = r * base
if exponent > 0:
return r
else:
return 1/r
第十三題 (代碼的完整性) 調整數組順序使奇數位於偶數前面
題目描述
輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有的奇數位於數組的前半部分,所有的偶數位於數組的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。
一開始想用快排的思想,但是快排是不穩定的,不能滿足交換位置後奇數偶數的相對位置不變。
方法一
首先想到把偶數挑出來,再接到奇數後面。這裏就產生了空間複雜度,如果要求原地就不滿足了。
class Solution:
def reOrderArray(self, array):
l = []
i = 0
while i < len(array):
if array[i] % 2 == 0:
l.append(array[i])
array.remove(array[i])
else:
i += 1
array.extend(l)
return array
方法二
考慮不額外開闢空間
class Solution:
def reOrderArray(self, array):
# write code here
i = 0 # 記錄偶數
while i < len(array):
while i < len(array) and array[i]%2 == 1:
i += 1
j = i + 1 # 記錄奇數
while j < len(array) and array[j]%2 == 0:
j += 1
if j < len(array):
array.insert(i, array.pop(j))
else:
break
return array
代碼的魯棒性:程序能夠判斷輸入是否合乎規範要求,並對不符合要求的輸入予以合理的處理。
所以各種情況要考慮完全
第十四題 (代碼的魯棒性) 鏈表中倒數第k個結點
題目描述
輸入一個鏈表,輸出該鏈表中倒數第k個結點。
方法一
head和k的條件判斷很重要,因爲這兩個條件提交了3次才成功。
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindKthToTail(self, head, k):
if head is None:
return head
l = []
while head.next:
l.append(head)
head = head.next
l.append(head)
if k > len(l) or k <= 0:
return None
return l[-k]
方法二
還是要注意邊界條件,很重要!因爲兩個判斷又提交了3次才成功!
class Solution:
def FindKthToTail(self, head, k):
if head is None or k <= 0:
return None
slow, quick = head, head
for i in range(k-1):
if quick is None or quick.next is None:
return None
quick = quick.next
while quick.next:
slow = slow.next
quick = quick.next
return slow
第十五題 (代碼的魯棒性) 反轉鏈表
題目描述
輸入一個鏈表,反轉鏈表後,輸出新鏈表的表頭。
不帶頭節點的單鏈表反轉問題
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
# write code here
if pHead is None or pHead.next is None:
return pHead
p = pHead
q = pHead.next
p.next = None
while q:
t = q.next
q.next = p
p = q
q = t
return p
第十六題 (代碼的魯棒性) 合併兩個排序的鏈表
題目描述
輸入兩個單調遞增的鏈表,輸出兩個鏈表合成後的鏈表,當然我們需要合成後的鏈表滿足單調不減規則。
方法一
開闢一個新的鏈表,不使用遞歸
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
# 返回合併後列表
def Merge(self, pHead1, pHead2):
# write code here
if not pHead1:
return pHead2
if not pHead2:
return pHead1
temp = ListNode(0)
t = temp
while pHead1 and pHead2:
if pHead1.val > pHead2.val:
t.next = pHead2
pHead2 = pHead2.next
else:
t.next = pHead1
pHead1 = pHead1.next
t = t.next # 加這句話很重要!?
if pHead1:
t.next = pHead1
if pHead2:
t.next = pHead2
return temp.next
方法二
使用遞歸
class Solution:
# 返回合併後列表
def Merge(self, pHead1, pHead2):
# write code here
if not pHead1:
return pHead2
if not pHead2:
return pHead1
if pHead1.val < pHead2.val:
t = pHead1
t.next = self.Merge(pHead1.next, pHead2)
else:
t = pHead2
t.next = self.Merge(pHead1, pHead2.next)
return t
(N)第十七題 (代碼的魯棒性) 樹的子結構
題目描述
輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)
沒做出來,參考一下別人的答案
思路:遞歸。增加輔助函數判斷樹A樹B是否相同。
1)首先在樹A中找到和樹B根節點值相同的結點R;
2)判斷樹A中以R爲根結點的子樹是不是包含和樹B一樣的結構。
# -*- coding:utf-8 -*-
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def HasSubtree(self, pRoot1, pRoot2):
# write code here
result = False
if pRoot1 and pRoot2:
if pRoot1.val == pRoot2.val:
result = self.similar(pRoot1, pRoot2)
if not result:
result = self.HasSubtree(pRoot1.left, pRoot2)
if not result:
result = self.HasSubtree(pRoot1.right, pRoot2)
return result
def similar(self, p, q):
if q is None: # 如果Tree2已經遍歷完了都能對應的上,返回true
return True
if p is None: # 如果Tree2還沒有遍歷完,Tree1卻遍歷完了。返回false
return False
if p.val != q.val:
return False
return self.similar(p.left, q.left) and self.similar(p.right, q.right)
第十八題 (面試思路) 二叉樹的鏡像
題目描述
操作給定的二叉樹,將其變換爲源二叉樹的鏡像。
輸入描述
二叉樹的鏡像定義:源二叉樹
8
/ \
6 10
/ \ / \
5 7 9 11
鏡像二叉樹
8
/ \
10 6
/ \ / \
11 9 7 5
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# 返回鏡像樹的根節點
def Mirror(self, root):
# write code here
if not root:
return None
root.right, root.left = root.left, root.right
self.Mirror(root.left)
self.Mirror(root.right)
return root
(N)第十九題 (畫圖讓抽象形象化) 順時針打印矩陣
題目描述
輸入一個矩陣,按照從外向裏以順時針的順序依次打印出每一個數字,例如,如果輸入如下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.
來自牛客討論的方法
對原矩陣
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
每次打印第一行1,2,3,4後,進行逆時針旋轉,變成
8 12 16
7 11 15
6 10 14
5 9 13
繼續打印第一行8,12,16後逆時針旋轉……
class Solution:
# matrix類型爲二維列表,需要返回列表
def printMatrix(self, matrix):
# write code here
n = len(matrix)
m = len(matrix[0])
out = []
while matrix:
out += matrix.pop(0)
if not matrix or not matrix[0]:
break
matrix = self.turn(matrix)
return out
def turn(self, matrix):
m = len(matrix) # 行數
n = len(matrix[0]) # 列數
new = []
for i in range(n-1, -1, -1):
newh = []
for j in range(m):
newh.append(matrix[j][i])
new.append(newh)
return new
第二十題 (舉例讓抽象具體化) 包含min函數的棧
題目描述
定義棧的數據結構,請在該類型中實現一個能夠得到棧中所含最小元素的min函數(時間複雜度應爲O(1))。
python一句話搞定
class Solution:
def __init__(self):
self.stack = []
def push(self, node):
# write code here
self.stack.append(node)
def pop(self):
# write code here
return self.stack.pop()
def top(self):
# write code here
return self.stack[-1]
def min(self):
# write code here
return min(self.stack)
不用python自帶的min實現
class Solution:
def __init__(self):
self.stack = []
self.mins = []
def push(self, node):
# write code here
if not self.mins:
self.mins.append(node)
else:
self.mins.append(min(self.mins[-1], node))
self.stack.append(node)
def pop(self):
# write code here
if not self.stack:
return None
self.mins.pop()
return self.stack.pop()
def top(self):
# write code here
if not self.stack:
return None
return self.stack[-1]
def min(self):
# write code here
if not self.mins:
return None
return self.mins[-1]
第二十一題(N) (舉例讓抽象具體化) 棧的壓入、彈出序列
題目描述
輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否可能爲該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注意:這兩個序列的長度是相等的)
來自牛客討論的思路
方法一
添加輔助的棧,遍歷pushV和popV,
遍歷壓棧順序,先講第一個放入棧中,這裏是1,然後判斷棧頂元素是不是出棧順序的第一個元素,這裏是4,很顯然1≠4,所以我們繼續壓棧,直到相等以後開始出棧,出棧一個元素,則將出棧順序向後移動一位,直到不相等,這樣循環等壓棧順序遍歷完成,如果輔助棧還不爲空,說明彈出序列不是該棧的彈出順序。
舉例:
入棧1,2,3,4,5
出棧4,5,3,2,1
首先1入輔助棧,此時棧頂1≠4,繼續入棧2
此時棧頂2≠4,繼續入棧3
此時棧頂3≠4,繼續入棧4
此時棧頂4=4,出棧4,彈出序列向後一位,此時爲5,,輔助棧裏面是1,2,3
此時棧頂3≠5,繼續入棧5
此時棧頂5=5,出棧5,彈出序列向後一位,此時爲3,,輔助棧裏面是1,2,3
class Solution:
def IsPopOrder(self, pushV, popV):
# write code here
if len(pushV)==0 or len(popV)==0 or len(pushV) != len(popV):
return False
stack = []
j = 0
for i in range(len(pushV)):
stack.append(pushV[i])
while stack and stack[-1] == popV[j]:
stack.pop()
j += 1
if not stack:
return True
else:
return False
方法二
輔助棧stack中存入pushV中取出的數據
如果pushV和popV第一個元素相等,直接都彈出,根本不用壓入stack
如果stack的最後一個元素與popV中第一個元素相等,將兩個元素都彈出
如果pushV中有數據,壓入stack
class Solution:
def IsPopOrder(self, pushV, popV):
# write code here
stack = []
while popV:
if pushV and pushV[0] == popV[0]:
pushV.pop(0)
popV.pop(0)
elif stack and stack[-1] == popV[0]:
stack.pop()
popV.pop(0)
elif pushV:
stack.append(pushV.pop(0))
else:
return False
return True
第二十二題 (舉例讓抽象具體化)從上往下打印二叉樹
題目描述
從上往下打印出二叉樹的每個節點,同層節點從左至右打印。
層次遍歷輸出
借用一個輔助隊列
class Solution:
# 返回從上到下每個節點值列表,例:[1,2,3]
def PrintFromTopToBottom(self, root):
# write code here
t = []
queue = []
if not root:
return t
queue.append(root)
t.append(root.val)
while queue:
fu = queue.pop(0)
if fu.left:
t.append(fu.left.val)
queue.append(fu.left)
if fu.right:
t.append(fu.right.val)
queue.append(fu.right)
return t
第二十三題 (N)(舉例讓抽象具體化) 二叉搜索樹的後序遍歷序列
題目描述
輸入一個整數數組,判斷該數組是不是某二叉搜索樹的後序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同。
每次碰到樹相關的問題都很困惑!
假設序列爲 sequence[0, 1, 2, ..., n-1]
(1) 序列的最後一個節點爲根節點root = sequence[n -1];
(2) 從序列第一個元素開始查找第一個值大於root的值的元素 sequence[i];
(3) 如果從sequence[i]到root前一個節點sequence[n-2]的值都大於root的值,則進行下一步,否則直接返回false;
(4) 如果i>0,則說明二叉查找樹含有左子樹,那麼重複步驟(1)(2)(3)判斷序列sequence[0, 1, ..., i-1]是否爲二叉查找樹的後序遍歷結果;
(5) 如果i<n-1,則說明二叉查找樹含有右子樹,那麼重複步驟(1)(2)(3)判斷序列sequence[i, i+1, ..., n-2]是否爲二叉查找樹的後序遍歷結果;
(6) 如果步驟(4)、(5)的結果都爲true,那麼這個序列就是二叉查找樹的後序遍歷結果;否則不是。
class Solution:
def VerifySquenceOfBST(self, sequence):
# write code here
if len(sequence) == 0:
return False
root = sequence[-1]
for i in range(len(sequence)):
if sequence[i] > root:
break
print(sequence[i])
for j in range(i, len(sequence)-1):
if sequence[j] < root:
return False
l = r = True
if i > 0:
l = self.VerifySquenceOfBST(sequence[:i])
if i < len(sequence)-1:
r = self.VerifySquenceOfBST(sequence[i:-1])
return l and r
第二十四題 (舉例讓抽象具體化) 二叉樹中和爲某一值的路徑
題目描述
輸入一顆二叉樹的跟節點和一個整數,打印出二叉樹中結點值的和爲輸入整數的所有路徑。路徑定義爲從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。(注意: 在返回值的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
out = []
s = []
if root:
self.DFS(root, expectNumber, out, s)
return out
def DFS(self, r, n, out, s):
s.append(r.val)
if not r.left and not r.right and sum(s) == n:
out.append(s[:]) # 注意一下這句話
if r.left:
self.DFS(r.left, n, out, s)
if r.right:
self.DFS(r.right, n, out, s)
s.pop()
思路本身還好不是很難,但運行的時候,原來out.append(s[:])寫的是out.append(s),輸出的總是空列表,但輸出中間值是有的。查了很多博客,提到Python的可變對象、不可變對象等機制。
參考博文:劍指offer:二叉樹中和爲某一值的路徑
第二十五題 (N)(分解讓複雜問題簡單) 複雜鏈的複製
題目描述
輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果爲複製後複雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,否則判題程序會直接返回空)
分析一下
鏈表的複雜在於有一個特殊指針指向任意一個結點,如果去掉這個特殊指針,那麼完成鏈表的複製就很簡單了。
第一步 複製原來的鏈表,順次連接形成新鏈表
第二步,利用原節點的random指向,來用複製的相應節點的random
最後一步,將複製好的鏈表拆分出來,或者說將 偶數位的節點重新拆分合成新的鏈表,得到的就是複製的鏈表
# -*- 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):
if not pHead:
return pHead
# 第一步 複製原來的鏈表,順次連接形成新鏈表
cloNode = pHead # 指向被複制的點
while cloNode:
node = RandomListNode(cloNode.label) # 新建複製的點
node.next = cloNode.next
cloNode.next = node
cloNode = node.next
# 第二步,利用原節點的random指向,來用複製的相應節點的random
cloNode = pHead # 指向被複制的點,即原鏈表的結點A
while cloNode:
node = cloNode.next # 新建複製的點,即複製原鏈表結點的新節點B
if cloNode.random:
node.random = cloNode.random.next
cloNode = node.next
# 最後一步,將複製好的鏈表拆分出來
cloNode = pHead
pHead = pHead.next
while cloNode.next:
node = cloNode.next
cloNode.next = node.next
cloNode = node
return pHead
第二十六題 (N)(分解讓複雜問題簡單) 二叉搜索樹與雙向鏈表
題目描述
輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。
老實說連題目都沒讀懂,
看解題思路的時候看到這張圖,才大概明白了題目的意思
這樣子的話就是中序遍歷的思想,剩下的還是不怎麼會,吐血
看了3遍終於看明白了。
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
self.pHead = None
# pHead指向鏈表頭節點,這裏就是指向中序遍歷的第一個數字,也即根節點左孩子的最左節點
self.pTail = None
# pTail剛開始指向當前操作節點的前一個節點,完成指針指向變化後,指向當前操作節點
def Convert(self, pRootOfTree):
if not pRootOfTree:
return None
self.Convert(pRootOfTree.left)
if not self.pHead: # 初始化pHead節點和Tail節點
self.pHead = pRootOfTree
self.pTail = pRootOfTree
else: # pRootOfTree爲當前操作節點,變換它的左邊的鏈接
self.pTail.right = pRootOfTree
pRootOfTree.left = self.pTail
self.pTail = pRootOfTree
self.Convert(pRootOfTree.right)
return self.pHead
附:前/中/後序/層次遍歷(遞歸/非遞歸)
class TreeNode:
def __init__(self,x):
self.val = x
self.left = None
self.right = None
class Solution:
def initTree(self): # 構造樹
m = TreeNode(0)
m1 = TreeNode(1)
m2 = TreeNode(2)
m.left = m1
m.right = m2
m3 = TreeNode(3)
m4 = TreeNode(4)
m1.left = m3
m1.right = m4
m5 = TreeNode(5)
m6 = TreeNode(6)
m2.left = m5
m2.right = m6
m7 = TreeNode(7)
m8 = TreeNode(8)
m3.left = m7
m3.right = m8
m9 = TreeNode(9)
m4.left = m9
return m
def layer(self, root): # 層次遍歷
if not root:
return None
queue = [root]
while queue:
t = queue.pop(0)
print(t.val, end=" ")
if t.left:
queue.append(t.left)
if t.right:
queue.append(t.right)
def preOrder(self, root): # 先序遍歷-遞歸
if not root:
return None
print(root.val, end=" ")
self.preOrder(root.left)
self.preOrder(root.right)
def preOederNo(self, root): # 先序遍歷-非遞歸
if not root:
return None
# out = []
stack = []
while root or stack:
while root:
# out.append(root.val)
print(root.val, end=" ")
stack.append(root)
root = root.left
if stack:
t = stack.pop()
root = t.right
# return out
def midOrder(self, root): # 中序遍歷-遞歸
if not root:
return None
self.midOrder(root.left)
print(root.val, end=" ")
self.midOrder(root.right)
def midOederNo(self, root): # 中序遍歷-非遞歸
if not root:
return None
# out = []
stack = []
while root or stack:
while root:
stack.append(root)
root = root.left
if stack:
t = stack.pop()
# out.append(root.val)
print(t.val, end=" ")
root = t.right
# return out
def postOrder(self, root): # 後序遍歷-遞歸
if not root:
return None
self.postOrder(root.left)
self.postOrder(root.right)
print(root.val, end=" ")
def postOederNo(self, root): # 後序遍歷-非遞歸
if not root:
return None
out = []
stack = []
while root or stack:
while root:
out.append(root.val)
stack.append(root)
root = root.right
if stack:
t = stack.pop()
root = t.left
for i in out[::-1]:
print(i, end=" ")
if __name__ == '__main__':
t = Solution()
# 構造樹
m = t.initTree()
print("層次遍歷:", end=" ")
t.layer(m)
print("")
print("先序遍歷(遞歸):", end=" ")
t.preOrder(m)
print("")
print("先序遍歷(非遞歸):", end=" ")
t.preOederNo(m)
print("")
print("中序遍歷(遞歸):", end=" ")
t.midOrder(m)
print("")
print("中序遍歷(非遞歸):", end=" ")
t.midOederNo(m)
print("")
print("後序遍歷(遞歸):", end=" ")
t.postOrder(m)
print("")
print("後序遍歷(非遞歸):", end=" ")
t.postOederNo(m)
運行結果
第二十七題(N) (分解讓複雜問題簡單) 字符串的排列
題目描述
輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。
輸入描述:
輸入一個字符串,長度不超過9(可能有字符重複),字符只包括大小寫字母。
python有自帶的庫可以求全排列
import itertools
class Solution:
def Permutation(self, ss):
res = []
if not ss:
return res
else:
for i in itertools.permutations(ss):
res.append(''.join(i))
return sorted(list(set(res)))
不考慮自帶庫,涉及到全排列
一是使用遞歸
class Solution:
def Permutation(self, ss):
if not ss:
return []
if len(ss) == 1:
return [ss]
res = []
# 固定第一個元素,然後遞歸求解
for i in range(len(ss)):
for j in self.Permutation(ss[:i]+ss[i+1:]):
res.append(ss[i]+j)
return sorted(list(set(res)))
二是使用深度優先算法
其他語言有看到使用字典排序算法
mark一下
前面3題:25、26、27對我來說有點難,沒有思路,空着好久,參考了一些的博文和牛客的討論。
第二十八題 (時間效率)數組中出現次數超過一半的數字
題目描述
數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度爲9的數組{1,2,3,2,2,2,5,4,2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。
方法一
使用字典
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
# write code here
dic = {}
for i in numbers:
if i in dic:
dic[i] += 1
else:
dic[i] = 1
for i in dic:
if dic[i]>len(numbers)//2:
return i
return 0
方法二
使用python自帶的庫
import collections
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
# write code here
l = collections.Counter(numbers)
for x in l.most_common(1):
if x[1] > len(numbers)//2:
return x[0]
return 0
簡化一下
import collections
class Solution:
def MoreThanHalfNum_Solution(self, numbers):
# write code here
l = collections.Counter(numbers)
x = l.most_common(1) # 得到的x是[( )]形式
if x[0][1] > len(numbers)//2:
return x[0][0]
return 0
第二十九題 (時間效率)最小的K個數
題目描述
輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。
用python的話方法很多
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# write code here
tinput.sort()
if k > len(tinput):
return []
return tinput[:k]
import heapq
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# write code here
if k > len(tinput):
return []
return heapq.nsmallest(k,tinput)
寫一個冒泡排序
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
self.sort(tinput)
return tinput[:k]
def sort(self, n):
for i in range(len(n)):
for j in range(1,len(n)-i):
if n[j] < n[j-1]:
n[j], n[j-1] = n[j-1], n[j]
return n
第三十題 (時間效率)連續子數組的最大和
題目描述
HZ偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會後,他又發話了:在古老的一維模式識別中,常常需要計算連續子向量的最大和,當向量全爲正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,並期望旁邊的正數會彌補它呢?例如:{6,-3,-2,7,-15,1,2,2},連續子向量的最大和爲8(從第0個開始,到第3個爲止)。給一個數組,返回它的最大連續子序列的和,你會不會被他忽悠住?(子向量的長度至少是1)
方法一
比較笨的方法,就是把所有可能算一下,邊算邊選最大的
class Solution:
def FindGreatestSumOfSubArray(self, array):
# write code here
maxt = array[0]
for i in range(len(array)):
temp = 0
for j in range(i, len(array)):
temp += array[j]
if temp > maxt:
maxt = temp
return maxt
方法二
最大序列和的第一個數和最後一個數一定是正數
對當前的數i:
如果前面的累加值爲負數或者等於0,那對累加值清0重新累加,把當前的第i個數的值賦給累加值。
如果前面的累加值爲整數,那麼繼續累加,即之前的累加值加上當前第i個數的值作爲新的累加值。
class Solution:
def FindGreatestSumOfSubArray(self, array):
maxt = array[0]
temp = 0
for i in array:
if temp < 0:
temp = i
else:
temp += i
if temp > maxt:
maxt = temp
return maxt
第三十一題 (時間效率)整數中1出現的次數(從1到n整數中1出現的次數)
題目描述
求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?爲此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數(從1 到 n 中1出現的次數)。
方法一
直接用python將每個數轉化爲字符串看裏面有幾個1比較簡單
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
# write code here
count = 0
for i in range(1, n+1):
for i in str(i):
if i == '1':
count += 1
return count
方法二
遍歷每一個數字,然後一個個算,時間複雜度有點高
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
count = 0
for i in range(1, n+1):
temp = i
while temp:
if temp % 10 == 1:
count += 1
temp //= 10
return count
方法三
參考牛客‘咩咩jiang’的解析
主要思路:設定整數點(如1、10、100等等)作爲位置點i(對應n的各位、十位、百位等等),分別對每個數位上有多少包含1的點進行分析
根據設定的整數位置,對n進行分割,分爲兩部分,高位n/i,低位n%i
1、當i表示百位,且百位對應的數>=2:
如n=31456,i=100,則a=314,b=56,此時百位爲1的次數有a/10+1=32(最高兩位0~31),每一次都包含100個連續的點,即共有(a/10+1)*100個點的百位爲1
2、當i表示百位,且百位對應的數爲1:
如n=31156,i=100,則a=311,b=56,此時百位對應的就是1,則共有a/10(最高兩位0-30)次是包含100個連續點,當最高兩位爲31(即a=311),本次只對應局部點00~56,共b+1次,所有點加起來共有(a/10*100)+(b+1),這些點百位對應爲1
3、當i表示百位,且百位對應的數爲0:
如n=31056,i=100,則a=310,b=56,此時百位爲1的次數有a/10=31(最高兩位0~30)
綜合以上三種情況,當百位對應0或>=2時,有(a+8)/10次包含所有100個點,還有當百位爲1(a%10==1),需要增加局部點b+1
之所以補8,是因爲當百位爲0,則a/10==(a+8)/10,當百位>=2,補8會產生進位位,效果等同於(a/10+1)
換句話說,補8是爲了合併第一種和第三種的情況。
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
count = 0
i = 1
while i <= n:
a = n // i
b = n % i
count += (a+8)//10*i+(a%10==1)*(b+1)
i *= 10
return count
第三十二題 (時間效率)把數組排成最小的數
題目描述
輸入一個正整數數組,把數組裏所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。例如輸入數組{3,32,321},則打印出這三個數字能排成的最小數字爲321323。
轉化爲str進行比較
雖然牛客上是使用python2.7,但自己平時使用的都是3.5、3.6版本,所以python3裏沒有了cmp參數,只有key參數
比較函數(comparison function)轉化爲了關鍵字函數(key function)
functools.cmp_to_key(func)函數主要用來將程序轉成 Python3 格式的,因爲 Python3 中不支持比較函數
from functools import cmp_to_key
class Solution:
def PrintMinNumber(self, numbers):
if not numbers:
return ""
n = list(map(str, numbers))
n.sort(key=cmp_to_key(self.cmp))
return "".join(n)
def cmp(self, x, y):
if x+y > y+x:
return 1
if x+y < y+x:
return -1
else:
return 0
如果對python2則有
class Solution:
def PrintMinNumber(self, numbers):
if not numbers:
return ""
n = list(map(str, numbers))
n.sort(cmp=lambda x, y: int(x + y) - int(y + x))
return "".join(n)
第三十三題 (時間空間效率的平衡)醜數
題目描述
把只包含質因子2、3和5的數稱作醜數(Ugly Number)。例如6、8都是醜數,但14不是,因爲它包含質因子7。 習慣上我們把1當做是第一個醜數。求按從小到大的順序的第N個醜數。
錯誤的嘗試:最開始想用蠻力法,一個數一個數看滿不滿足,直到得到第N個醜數
class Solution:
def GetUglyNumber_Solution(self, index):
l = [1]
t = 2
while len(l) < index:
if self.ischou(t):
l.append(t)
t += 1
return l[-1]
def ischou(self,i):
while i >1:
if i % 2 == 0:
i = i//2
elif i % 3 == 0:
i =i//3
elif i % 5 == 0:
i = i//5
else:
return False
return True
代碼沒問題,但是時間複雜度太高,超出時間限制
考慮其他方法
class Solution:
def GetUglyNumber_Solution(self, index):
if index < 7:
return index
out = [1]
t2=t3=t5 = 0
while len(out) < index:
m = min(out[t2]*2, out[t3]*3, out[t5]*5)
out.append(m)
print(out)
if out[t2] * 2 == m:
t2 += 1
if out[t3] * 3 == m:
t3 += 1
if out[t5] * 5 == m:
t5 += 1
return out[-1]
第三十四題 (時間空間效率的平衡)第一個只出現一次的字符
題目描述
在一個字符串(0<=字符串長度<=10000,全部由字母組成)中找到第一個只出現一次的字符,並返回它的位置, 如果沒有則返回 -1(需要區分大小寫).
方法一
想到的第一個方法就是掃描字符串,將每一個字符與整個字符串進行比較,找到第一個只出現一次的字符串。
時間複雜度爲O(n^2),算法複雜度過大
考慮用空間換時間
由於字典是無序的所以,使用字典很難找到第一個只出現一次的字符串,所以再遍歷一次
class Solution:
def FirstNotRepeatingChar(self, s):
if not s:
return -1
dic = {}
for i in s:
if i in dic:
dic[i] += 1
else:
dic[i] = 1
for i in s:
if dic[i]==1:
return s.index(i)
也可以分三個列表,分別存儲所有的字符、出現多次的字符;根據前兩個可以得到只出現一次的字符。
class Solution:
def FirstNotRepeatingChar(self, s):
if not s:
return -1
s1 = []
s2 = []
out = []
for ss in s:
if ss not in s1:
s1.append(ss)
else:
s2.append(ss)
for ii in s1:
if ii not in s2:
out.append(ii)
c = out[0]
return s.index(c)
方法二
體現了python的簡便之處
class Solution:
def FirstNotRepeatingChar(self, s):
for i in s:
if s.count(i) == 1:
return s.index(i)
return -1
方法三
使用collections下的Counter,字典子類,爲可以進行哈希的對象計數
同時還涉及到enumerate,將一個可遍歷的數據對象(如列表、元組或字符串)組合爲一個索引序列,同時列出數據和數據下標
from collections import Counter
class Solution:
def FirstNotRepeatingChar(self, s):
if not s:
return -1
count = Counter(s)
for i,c in enumerate(s):
if count[c] == 1:
return i
第三十五題(N) (時間空間效率的平衡)數組中的逆序對
題目描述
在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數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
一上來還是想到兩個循環,算法複雜度過大
看討論區說用Python解決的代碼很難在1秒內完成,所以答案過不了
參考Python:數組中的逆序對,提供兩種思路
一個如下
計算數組中的最小值在數組中出現的位置,統計數組中最小值前面的個數(即其index),之後在原始數組中去掉最小值。重複上述步驟。
不過這個做法還是時間複雜度超了
class Solution:
def InversePairs(self, data):
if not data:
return 0
c = 0
while len(data) > 1:
c += data.index(min(data))
data.pop(data.index(min(data)))
return c%1000000007
作爲只會c和python的渣渣,這個題真的很爲難我。先空着吧。
一個是歸併排序
參考牛客‘rs勿忘初心’的討論
(a) 把長度爲4的數組分解成兩個長度爲2的子數組;
(b) 把長度爲2的數組分解成兩個成都爲1的子數組;
在上圖(a)和(b)中,我們先把數組分解成兩個長度爲2的子數組,再把這兩個子數組分別拆成兩個長度爲1的子數組。
(c) 把長度爲1的子數組 合併、排序並統計逆序對 ;
一邊合併相鄰的子數組,一邊統計逆序對的數目。
在第一對長度爲1的子數組{7}、{5}中7大於5,因此(7,5)組成一個逆序對。同樣在第二對長度爲1的子數組{6}、{4}中也有逆序對(6,4)。
由於我們已經統計了這兩對子數組內部的逆序對,因此需要把這兩對子數組排序如上圖(c)所示, 以免在以後的統計過程中再重複統計。
(d) 把長度爲2的子數組合並、排序,並統計逆序對;
接下來我們統計兩個長度爲2的子數組子數組之間的逆序對。合併子數組並統計逆序對的過程如下圖如下圖所示。
先用兩個指針分別指向兩個子數組的末尾,並每次比較兩個指針指向的數字。
如果第一個子數組中的數字大於第二個數組中的數字,則構成逆序對,並且逆序對的數目等於第二個子數組中剩餘數字的個數,如下圖(a)和(c)所示。
如果第一個數組的數字小於或等於第二個數組中的數字,則不構成逆序對,如圖b所示。
每一次比較的時候,我們都把較大的數字從後面往前複製到一個輔助數組中,確保 輔助數組(記爲copy) 中的數字是遞增排序的。在把較大的數字複製到輔助數組之後,把對應的指針向前移動一位,接下來進行下一輪比較。
思路搞明白就好了,編程語言大同小異。
C++版:
class Solution {
public:
int InversePairs(vector<int> data) {
if(data.size()==0)
return 0;
vector<int> copy(data); // 輔助數組,每次遞歸後有序
return InversePairsCore(data, copy, 0, data.size()-1);
}
int InversePairsCore(vector<int>& data, vector<int>& copy, int begin, int end) {
if(begin == end)
return 0;
int mid = begin + (end-begin)/2;
int left = InversePairsCore(copy, data, begin, mid);
int right = InversePairsCore(copy, data, mid+1, end);
int last_in_left = mid; // 比較從尾端開始
int last_in_right = end; // 比較從尾端開始
int index_copy = end; // 比較結果存入輔助數組尾端
long res = 0;
// 歸併排序:相當於兩個有序數組合成一個有序表(從尾端開始是爲了計數)
while(last_in_left>=begin && last_in_right>=mid+1) {
if(data[last_in_left] > data[last_in_right]) {
copy[index_copy--] = data[last_in_left--];
res += last_in_right - mid;
}
else
copy[index_copy--] = data[last_in_right--];
}
while(last_in_left >= begin)
copy[index_copy--] = data[last_in_left--];
while(last_in_right >= mid+1)
copy[index_copy--] = data[last_in_right--];
return (left+right+res) % 1000000007;
}
};
Java版
public class Solution {
public int InversePairs(int [] array) {
if(array==null||array.length==0)
{
return 0;
}
int[] copy = new int[array.length];
for(int i=0;i<array.length;i++)
{
copy[i] = array[i];
}
int count = InversePairsCore(array,copy,0,array.length-1);//數值過大求餘
return count;
}
private int InversePairsCore(int[] array,int[] copy,int low,int high)
{
if(low==high)
{
return 0;
}
int mid = (low+high)>>1;
int leftCount = InversePairsCore(array,copy,low,mid)%1000000007;
int rightCount = InversePairsCore(array,copy,mid+1,high)%1000000007;
int count = 0;
int i=mid;
int j=high;
int locCopy = high;
while(i>=low&&j>mid)
{
if(array[i]>array[j])
{
count += j-mid;
copy[locCopy--] = array[i--];
if(count>=1000000007)//數值過大求餘
{
count%=1000000007;
}
}
else
{
copy[locCopy--] = array[j--];
}
}
for(;i>=low;i--)
{
copy[locCopy--]=array[i];
}
for(;j>mid;j--)
{
copy[locCopy--]=array[j];
}
for(int s=low;s<=high;s++)
{
array[s] = copy[s];
}
return (leftCount+rightCount+count)%1000000007;
}
}
第三十六題 (時間空間效率的平衡)兩個鏈表的第一個公共結點
題目描述
輸入兩個鏈表,找出它們的第一個公共結點。
每次拿到這種題目第一反應就是雙重循環,推翻再來。
方法一
參考牛客‘Yannyezixin’的答案
兩條相交的鏈表呈Y型。可以從兩條鏈表尾部同時出發,最後一個相同的結點就是鏈表的第一個相同的結點。可以利用棧來實現。時間複雜度有O(m + n), 空間複雜度爲O(m + n)
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
if not pHead1 or not pHead2:
return None
p = []
q = []
while pHead1:
p.append(pHead1)
pHead1 = pHead1.next
while pHead2:
q.append(pHead2)
pHead2 = pHead2.next
out = None
while p and q:
out1 = p.pop()
out2 = q.pop()
if out1 is out2:
out = out1
else:
break
return out
方法二
之所以不能用兩個指針分貝對兩個鏈表進行遍歷找相同節點,是因爲兩者的長度不一樣。那麼將兩個鏈表合併,pHead1+pHead2和pHead2+pHead1,得到的兩個長度就相同了,再依次遍歷就能找到相同節點了。
比如
1 2 3 5 6和4 7 5 6
分別合併得到
1 2 3 5 6 4 7 5 6以及
4 7 5 6 1 2 3 5 6
class Solution:
def FindFirstCommonNode(self, pHead1, pHead2):
p1,p2=pHead1,pHead2
while p1!=p2:
if p1:
p1 = p1.next
else:
p1 = pHead2
if p2:
p2 = p2.next
else:
p2 = pHead1
return p1
第三十七題 (知識遷移能力)數字在排序數組中出現的次數
題目描述
統計一個數字在排序數組中出現的次數。
方法一
用python自帶的count,很簡單一句話
面試的時候大概是不能這麼寫的,汗顏
class Solution:
def GetNumberOfK(self, data, k):
return data.count(k)
方法二
考慮不使用自帶函數
class Solution:
def GetNumberOfK(self, data, k):
if k not in data:
return 0
c = 0
for i in data:
if i == k:
c += 1
return c
但是這個方法沒考慮到有序這個條件
方法三
使用二分查找算法找到第一個k的位置和最後一個k的位置
class Solution:
def GetNumberOfK(self, data, k):
if k not in data:
return 0
l = self.getleft(data, k, 0, len(data)-1)
r = self.getright(data, k, 0, len(data)-1)
return r-l+1
def getleft(self, data, k, l, r):
while l <= r: # !
mid = (l+r)//2
if data[mid] < k: # !
l = mid+1
else:
r = mid-1
return l # !
def getright(self, data, k, l, r):
while l <= r: # !
mid = (l+r)//2
if data[mid] <= k: # !
l = mid+1
else:
r = mid-1
return r # !
備註感嘆號的都是我覺得需要注意的地方
第三十八題 (知識遷移能力)二叉樹的深度
題目描述
輸入一棵二叉樹,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度爲樹的深度。
# 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
return max(self.TreeDepth(pRoot.left),self.TreeDepth(pRoot.right))+1
# 考慮層次遍歷非遞歸
class Solution:
def TreeDepth(self, pRoot):
if not pRoot:
return 0
t = [pRoot]
h = 0
while t:
for i in range(len(t)):
temp = t.pop(0)
if temp.left:
t.append(temp.left)
if temp.right:
t.append(temp.right)
h += 1
return h
第三十九題 (知識遷移能力)平衡二叉樹
題目描述
輸入一棵二叉樹,判斷該二叉樹是否是平衡二叉樹。
之前碰到過一次,沒做出來。第二次做了還是有點小錯誤
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def IsBalanced_Solution(self, pRoot):
if not pRoot:
return True
while pRoot:
if abs(self.depth(pRoot.left) - self.depth(pRoot.right)) > 1:
return False
else:
return self.IsBalanced_Solution(pRoot.left) and self.IsBalanced_Solution(pRoot.right)
def depth(self, r):
if not r:
return 0
return max(self.depth(r.left), self.depth(r.right))+1
第四十題 (知識遷移能力)數組中只出現一次的數字
題目描述
一個整型數組裏除了兩個數字之外,其他的數字都出現了偶數次。請寫程序找出這兩個只出現一次的數字。
這道題方法很多
方法一
比較簡單能想到的是字典的方法,還有python自帶的Counter方法
class Solution:
# 返回[a,b] 其中ab是出現一次的兩個數字
def FindNumsAppearOnce(self, array):
dic = {}
for i in array:
if i in dic:
dic[i] += 1
else:
dic[i] = 1
t = []
for v in dic:
if dic[v] == 1:
t.append(v)
return t
方法二
難一點的就是使用異或,如果是隻有一個數唯一用異或還是很簡單的
參考了一下牛客討論區的答案
class Solution:
# 返回[a,b] 其中ab是出現一次的兩個數字
def FindNumsAppearOnce(self, array):
t = 0
# 兩個數相異爲1相同爲0
for i in array:
t ^= i
# 得到的t是兩個不同數的異或
id = 1
a = b = 0
# 找到從右起第一個1的位置
while t & id == 0:
id <<= 1
for i in array:
if i & id == 0: # 找到標誌位爲0的那些數
a ^= i # 標誌位爲0的只有一個數是單獨的,找到那個數
else: # 找到標誌位爲1的那些數
b ^= i # 標誌位爲1的只有一個數是單獨的,找到那個數
return [a, b]
第四十一題 (知識遷移能力)和爲S的連續正數序列
題目描述
小明很喜歡數學,有一天他在做數學作業時,要求計算出9~16的和,他馬上就寫出了正確答案是100。但是他並不滿足於此,他在想究竟有多少種連續的正數序列的和爲100(至少包括兩個數)。沒多久,他就得到另一組連續正數和爲100的序列:18,19,20,21,22。現在把問題交給你,你能不能也很快的找出所有和爲S的連續正數序列? Good Luck!
輸出描述:
輸出所有和爲S的連續正數序列。序列內按照從小至大的順序,序列間按照開始數字從小到大的順序
方法一
想了一個比較笨的方法
class Solution:
def FindContinuousSequence(self, tsum):
i = 1
out = []
while i <= tsum//2+1:
sum = 0
t = []
j = i
while sum < tsum:
sum += j
t.append(j)
j += 1
if sum == tsum and len(t)>1:
out.append(t)
i += 1
return out
原以爲會因爲複雜度高提交失敗,沒想到過了。
在牛客的討論區發現兩個比較好的方法
方法二
首先是雙指針法
class Solution:
def FindContinuousSequence(self, tsum):
out = []
i = 1
j = 2
while i < j:
t = []
s = (i+j)*(j-i+1)//2
if s == tsum:
for k in range(i, j+1):
t.append(k)
out.append(t)
if s < tsum:
j += 1
else:
i += 1
return out
方法三
參考‘丁滿歷險記’解析
1)由於我們要找的是和爲S的連續正數序列,因此這個序列是個公差爲1的等差數列,而這個序列的中間值代表了平均值的大小。假設序列長度爲n,那麼這個序列的中間值可以通過(S / n)得到,知道序列的中間值和長度,也就不難求出這段序列了。
2)滿足條件的n分兩種情況:
n爲奇數時,序列中間的數正好是序列的平均值,所以條件爲:(n & 1) == 1 && sum % n == 0;
n爲偶數時,序列中間兩個數的平均值是序列的平均值,而這個平均值的小數部分爲0.5,所以條件爲:(sum % n) * 2 == n.
3)由題可知n >= 2,那麼n的最大值是多少呢?我們完全可以將n從2到S全部遍歷一次,但是大部分遍歷是不必要的。爲了讓n儘可能大,我們讓序列從1開始,
根據等差數列的求和公式:S = (1 + n) * n / 2,得到.
class Solution:
def FindContinuousSequence(self, tsum):
out = []
n = int((2*tsum)**0.5)
for i in range(n, 1,-1):
if (i & 1 == 1 and tsum % i == 0) or ((tsum % i) * 2 == i):
# 求出滿足條件的i
temp = []
k = int((tsum / i) - (i - 1) / 2)
for j in range(i):
temp.append(k)
k += 1
out.append(temp)
return out
這個方法還是要好好理解一下的
第四十二題 (知識遷移能力)和爲S的兩個數字
題目描述
輸入一個遞增排序的數組和一個數字S,在數組中查找兩個數,使得他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。
輸出描述:
對應每個測試案例,輸出兩個數,小的先輸出。
雙指針法
class Solution:
def FindNumbersWithSum(self, array, tsum):
out =[]
if not array:
return out
i = 0
j = len(array)-1
mins = array[j]*array[j]
while i < j:
if array[i]+array[j] == tsum:
temp = array[i]*array[j]
if temp < mins:
mins = temp
out.append(array[i])
out.append(array[j])
i += 1
j -= 1
elif array[i]+array[j] < tsum:
i += 1
elif array[i]+array[j] > tsum:
j -= 1
return out
根據‘馬客(Mark)’說的:找到的第一組(相差最大的)就是乘積最小的。
證明:考慮x+y=C(C是常數),x*y的大小。不妨設y>=x,y-x=d>=0,即y=x+d, 2x+d=C, x=(C-d)/2, x*y=x(x+d)=(C-d)(C+d)/4=(C^2-d^2)/4,也就是x*y是一個關於變量d的二次函數,對稱軸是y軸,開口向下。d是>=0的,d越大, x*y也就越小。
所以其實代碼中設置的mins並沒有什麼用,去掉也不會影響最後的結果。
第四十三題 (知識遷移能力)左旋轉字符串
題目描述
彙編語言中有一種移位指令叫做循環左移(ROL),現在有個簡單的任務,就是用字符串模擬這個指令的運算結果。對於一個給定的字符序列S,請你把其循環左移K位後的序列輸出。例如,字符序列S=”abcXYZdef”,要求輸出循環左移3位後的結果,即“XYZdefabc”。是不是很簡單?OK,搞定它!
使用切片
class Solution:
def LeftRotateString(self, s, n):
if not s:
return ""
n = n %(len(s))
return s[n:]+s[:n]
也可以使用三次反轉
class Solution:
def LeftRotateString(self, s, n):
if not s: return s
s = list(s)
self.reverse(s, 0, n - 1)
self.reverse(s, n, len(s) - 1)
self.reverse(s, 0, len(s) - 1)
return ''.join(s)
def reverse(self, s, start, end):
while start < end:
s[start], s[end] = s[end], s[start]
start += 1
end -= 1
第四十四題 (知識遷移能力)翻轉單詞順序列
題目描述
牛客最近來了一個新員工Fish,每天早晨總是會拿着一本英文雜誌,寫些句子在本子上。同事Cat對Fish寫的內容頗感興趣,有一天他向Fish借來翻看,但卻讀不懂它的意思。例如,“student. a am I”。後來才意識到,這傢伙原來把句子單詞的順序翻轉了,正確的句子應該是“I am a student.”。Cat對一一的翻轉這些單詞順序可不在行,你能幫助他麼?
class Solution:
def ReverseSentence(self, s):
s = list(map(str,s.split(" ")))
s.reverse()
return " ".join(s)
附:推薦工程師死絕的世界
插一句:2019.1.22
今天參加了牛客網的寒假算法訓練營(一),做到自閉,我怕是對自己有什麼誤解。
另外呢,日本IT求職學習服務平臺PAIZA推出了一個編程遊戲《工程師死絕的世界》
玩了幾局感覺可以,推薦一下,看懂全靠Google翻譯。
第四十五題 (抽象建模能力)撲克牌順子
題目描述
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。
思路:數字0可以代表任意數
有以下幾個條件必須滿足
1、除0以外,不能有其他重複數字,注意0最多隻有4個。
2、滿足順子的話,排序後前一個數比後一個數小1。同時需要考慮有0的情況,如果後面一個數比前面一個數大於1以上,那麼就用0來補。
class Solution:
def IsContinuous(self, numbers):
if not numbers:
return None
zero = numbers.count(0)
if zero == 5:
return False
numbers.sort()
for i in range(0, len(numbers)-1):
if numbers[i] != 0:
if numbers[i] == numbers[i+1]:
return False
zero -= numbers[i+1] - numbers[i] - 1
if zero < 0:
return False
return True
第四十六題 (N)(抽象建模能力)孩子們的遊戲(圓圈中最後剩下的數)
題目描述
每年六一兒童節,牛客都會準備一些小禮物去看望孤兒院的小朋友,今年亦是如此。HF作爲牛客的資深元老,自然也準備了一些小遊戲。其中,有個遊戲是這樣的:首先,讓小朋友們圍成一個大圈。然後,他隨機指定一個數m,讓編號爲0的小朋友開始報數。每次喊到m-1的那個小朋友要出列唱首歌,然後可以在禮品箱中任意的挑選禮物,並且不再回到圈中,從他的下一個小朋友開始,繼續0...m-1報數....這樣下去....直到剩下最後一個小朋友,可以不用表演,並且拿到牛客名貴的“名偵探柯南”典藏版(名額有限哦!!^_^)。請你試着想下,哪個小朋友會得到這份禮品呢?(注:小朋友的編號是從0到n-1)
看着挺簡單的,但是不知道怎麼用算法表示出來
class Solution:
def LastRemaining_Solution(self, n, m):
if n < 1:
return -1
if n == 1:
return 0
l = []
for i in range(n):
l.append(i)
i = -1
j = 0
while l:
k = (j+m-1)%n
i = l.pop(k)
n -= 1
j = k
return i
參考:約瑟夫環問題
時間複雜度爲O(n),空間複雜度爲O(1)
在這n個數字中,第一個被刪除的數字是(m-1)%n,爲簡單起見記爲k。那麼刪除k之後的剩下n-1的數字爲0,1,…,k-1,k+1,…,n-1,並且下一個開始計數的數字是k+1。相當於在剩下的序列中,k+1排到最前面,從而形成序列k+1,…,n-1,0,…k-1。該序列最後剩下的數字也應該是關於n和m的函數。由於這個序列的規律和前面最初的序列不一樣(最初的序列是從0開始的連續序列),因此該函數不同於前面函數,記爲f’(n-1,m)。最初序列最後剩下的數字f(n,m)一定是剩下序列的最後剩下數字f’(n-1,m),所以f(n,m)=f’(n-1,m)。
接下來我們把剩下的的這n-1個數字的序列k+1,…,n-1,0,…k-1作一個映射,映射的結果是形成一個從0到n-2的序列:
k+1 -> 0
k+2 -> 1
…
n-1 -> n-k-2
0 -> n-k-1
…
k-1 -> n-2
把映射定義爲p,則p(x)= (x-k-1)%n,即如果映射前的數字是x,則映射後的數字是(x-k-1)%n。對應的逆映射是p-1(x)=(x+k+1)%n。
由於映射之後的序列和最初的序列有同樣的形式,都是從0開始的連續序列,因此仍然可以用函數f來表示,記爲f(n-1,m)。根據我們的映射規則,映射之前的序列最後剩下的數字f’(n-1,m)= p-1 [f(n-1,m)]=[f(n-1,m)+k+1]%n。把k=(m-1)%n代入得到f(n,m)=f’(n-1,m)=[f(n-1,m)+m]%n。
經過上面複雜的分析,我們終於找到一個遞歸的公式:f(n,m)= [f(n-1,m)+m]%n
19.1.23 說實話沒怎麼看明白,先這樣,等明天腦子清醒了再說。
第四十七題 (發散思維能力)求1+2+3+...+n
題目描述
求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。
class Solution:
def Sum_Solution(self, n):
return (1+n)*n/2
第四十八題(N) (發散思維能力)不用加減乘除做加法
題目描述
寫一個函數,求兩個整數之和,要求在函數體內不得使用+、-、*、/四則運算符號。
參考牛客‘咩咩jiang’的討論
兩個數異或:相當於每一位相加,而不考慮進位;
兩個數相與,並左移一位:相當於求得進位;
python版
因爲python沒有無符號又移操作,所以需要越界檢查一波
class Solution:
def Add(self, num1, num2):
while num2:
n = num1 ^ num2 # 異或查看兩個數哪些二進制位只有一個爲1,這些是非進位位
num2 = (num1 & num2)<<1 # 找出進位項
num1 = n & 0xFFFFFFFF
return num1 if num1 <= 0x7FFFFFFF else ~(num1 ^ 0xFFFFFFFF)
C++版
class Solution {
public:
int Add(int num1, int num2)
{
while(num2)
{
int temp = num1 ^ num2;
num2 = (num1 & num2)<<1;
num1 = temp;
}
return num1;
}
}
C++的另一個版本
1:按位與是查看兩個數哪些二進制位都爲1,這些都是進位位,結果需左移一位,表示進位後的結果
2:異或是查看兩個數哪些二進制位只有一個爲1,這些是非進位位,可以直接加、減,結果表示非進位位進行加操作後的結果
3:n1&n2是查看有沒有進位位了,如果有,需要重複step1、step2;如果沒有,保留n1、n2上二進制爲1的部分,
兩數相或即爲最後結果
class Solution {
public:
int Add(int num1, int num2)
{
int n1,n2;
n1 = (num1 & num2)<<1;
n2 = num1 ^ num2;
while(n1 & n2)
{
num1 = n1;
num2 = n2;
n1 = (num1 & num2)<<1;
n2 = num1 ^ num2;
}
return n1 | n2;
}
}
兩個版本其實是一個意思。
吐槽一句,用慣了python回頭用c/c++寫程序的時候,一直忘記定義變量以及每一句後面加‘;’
插一句:2019.1.24
今天參加了牛客網的寒假算法訓練營(二),還是兩道題告終,做完回家過年了!
第四十九題(N) (綜合)把字符串轉換成整數
題目描述
將一個字符串轉換成一個整數(實現Integer.valueOf(string)的功能,但是string不符合數字要求時返回0),要求不能使用字符串轉換整數的庫函數。 數值爲0或者字符串不是一個合法的數值則返回0。
輸入描述: 輸入一個字符串,包括數字字母符號,可以爲空
輸出描述: 如果是合法的數值表達則返回該數字,否則返回0
示例1
輸入
+2147483647
1a33
輸出
2147483647
0
又一次沒看懂題目,憂傷啊我的理解能力,再把題目多讀兩遍
class Solution:
def StrToInt(self, s):
numlist = ['0','1','2','3','4','5','6','7','8','9','+','-']
if not s:
return 0
flag = 0
i = 0
t = 0
if s[0] == '-':
flag = -1
i = 1
elif s[0] == '+':
flag = 0
i = 1
while i < len(s):
print(i,t)
if s[i] not in numlist:
return 0
t = t*10+numlist.index(s[i])
i += 1
if flag == 0:
return t
else:
return -t
感覺寫的有點複雜了,但是思路都是差不多的。
總結一下數字字符之間的轉化
str(x ) | 將對象 x 轉換爲字符串 |
int(x [,base ]) |
將x轉換爲一個整數 base=2將二進制變成十進制;base=8將八進制變成十進制;base=16將十六進制變成十進制;不加就是自動變成十進制 |
ord(x ) |
將單個字符轉換爲它的整數值 'a'對應97 |
oct(x ) |
將一個整數轉換爲一個八進制字符串 比如將一個整數10變成0o12,所以只取後面幾位oct(x)[2:] |
hex(x ) |
將一個整數轉換爲一個十六進制字符串 比如將一個整數10變成0xa,所以只取後面幾位hex(x)[2:] |
第五十題(N) (數組)數組中重複的數字
題目描述
在一個長度爲n的數組裏的所有數字都在0到n-1的範圍內。 數組中某些數字是重複的,但不知道有幾個數字是重複的。也不知道每個數字重複幾次。請找出數組中任意一個重複的數字。 例如,如果輸入長度爲7的數組{2,3,1,0,2,5,3},那麼對應的輸出是第一個重複的數字2。
class Solution:
# 這裏要特別注意~找到任意重複的一個值並賦值到duplication[0]
# 函數返回True/False
def duplicate(self, numbers, duplication):
# write code here
dic = {}
for i in numbers:
if i not in dic:
dic[i] = 1
else:
duplication[0] = i
return True
return False
第五十一題(N) (數組)構建乘積數組
題目描述
給定一個數組A[0,1,...,n-1],請構建一個數組B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。
解析:盜用一下牛客討論裏面的圖,看了圖,基本上思路就順了
可以用兩個列表分別存儲
一個存儲從1開始,依次存儲A0、A0*A1、A0*A1*A2、……、A0*A1*A2*……*An-2
一個存儲從1開始,依次存儲An-1、An-1*An-2、……、An-1*An-2*……*A2*A1
對應想乘
我這裏只用一個列表存儲,所以注意最後取的是B[:len(A)-2:-1]
不太理解可以舉特殊的例子看一會下就知道了。
class Solution:
def multiply(self, A):
B = [1]
if len(A) == 0:
return None
for i in range(1,len(A)):
B.append(B[i-1]*A[i-1])
temp = 1
for j in range(len(A)-2, -1, -1):
temp *= A[j+1]
B.append(B[j] * temp)
return B[:len(A)-2:-1]
插一句:2019.1.26
今天參加了牛客網的寒假算法訓練營(三),還是兩道題告終。慘兮兮,懷疑人生……
第五十二題(N) (字符串)正則表達式匹配
題目描述
請實現一個函數用來匹配包括'.'和'*'的正則表達式。模式中的字符'.'表示任意一個字符,而'*'表示它前面的字符可以出現任意次(包含0次)。 在本題中,匹配是指字符串的所有字符匹配整個模式。例如,字符串"aaa"與模式"a.a"和"ab*ac*a"匹配,但是與"aa.a"和"ab*a"均不匹配
首先對s和patter本身存在與否進行討論:
s和pattern都不存在,返回True
s存在但是pattern不存在,返回False
s不存在,pattern存在,只有任意字符+*的組合情況才成立,否則都返回False
其次主要是判斷有'.'和'*'的不同情況
僅'.'的情況,比較簡單,直接當作相同字符,s和pattern的指針都往下指一個,接着往下就好了;
'*'的情況就比較複雜了:
如果*之前的字符與s中指針指向的字符不匹配,那麼*之前的字符出現0次,pattern往後進兩個字符,跳到*的後一個字符
如果匹配,就要考慮*之前的字符出現幾次,所以用or連接
class Solution:
# s, pattern都是字符串
def match(self, s, pattern):
# write code here
if not s and not pattern:
return True
if s and not pattern:
return False
if not s and pattern:
if len(pattern) > 1 and pattern[1] == '*':
return self.match(s, pattern[2:])
else:
return False
if len(pattern) > 1 and pattern[1] == '*':
if s[0] != pattern[0] and pattern[0]!='.':
return self.match(s, pattern[2:])
else:
return self.match(s[1:],pattern) or self.match(s[1:],pattern[2:]) or self.match(s,pattern[2:])
else:
if s[0] == pattern[0] or pattern[0] == '.':
return self.match(s[1:], pattern[1:])
else:
return False
第五十三題(N)(字符串)表示數值的字符串
題目描述
請實現一個函數用來判斷字符串是否表示數值(包括整數和小數)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示數值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
主要是對‘+’、‘-’、小數點、’e'或者‘E’的討論
‘+’、‘-’只能出現在字符串的第一位,或者緊跟e/E。如果沒有e/E,只能最多出現一次;如果有可以出現最多兩次
小數點只能出現最多一次,且不能出現在e/E的後面
e/E只能最多出現一次,且不能不出現在最後一位
除此以外,還要判斷一下其他位必須是0-9的字符
class Solution:
# s字符串
def isNumeric(self, s):
# write code here
if not s:
return False
a = False # '+'\'-'
p = False # '.'
e = False # 'e'\'E'
for i in range(len(s)):
if s[i] == 'e' or s[i] == 'E':
if e:
return False
else:
e = True
if i == len(s)-1:
return False
elif s[i] == '.':
if p:
return False
else:
p = True
if e:
return False
elif s[i] == '+' or s[i] == '-':
if a:
if s[i-1] != 'e' and s[i-1] != 'E':
return False
else:
a = True
if i > 0 and s[i-1] != 'e' and s[i-1] != 'E':
return False
else:
if s[i] < '0' or s[i] > '9':
return False
return True
第五十四題(字符串)字符流中第一個不重複的字符
題目描述
請實現一個函數用來找出字符流中第一個只出現一次的字符。例如,當從字符流中只讀出前兩個字符"go"時,第一個只出現一次的字符是"g"。當從該字符流中讀出前六個字符“google"時,第一個只出現一次的字符是"l"。
輸出描述:
如果當前字符流沒有存在出現一次的字符,返回#字符。
沒有看更多的方法
class Solution:
# 返回對應char
def __init__(self):
self.s = []
self.t = []
def FirstAppearingOnce(self):
if not self.t:
return '#'
else:
return self.t[0]
def Insert(self, char):
self.s.append(char)
if char not in self.t:
self.t.append(char)
else:
self.t.remove(char)
第五十五題(鏈表)鏈表中環的入口結點
題目描述
給一個鏈表,若其中包含環,請找出該鏈表的環的入口結點,否則,輸出null。
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def EntryNodeOfLoop(self, pHead):
t = []
while pHead:
if pHead not in t:
t.append(pHead)
else:
return pHead
pHead = pHead.next
return None
第五十六題(鏈表)刪除鏈表中重複的結點
題目描述
在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留,返回鏈表頭指針。 例如,鏈表1->2->3->3->4->4->5 處理後爲 1->2->5
非遞歸:
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def deleteDuplication(self, pHead):
if not pHead or not pHead.next:
return pHead
new = ListNode(-1)
new.next = pHead
p = pHead
t = new
while p and p.next:
if p.val != p.next.val:
t = t.next
p = p.next
else:
val = p.val
while p and p.val == val:
p = p.next
t.next = p
return new.next
if __name__ == '__main__':
t = Solution()
s = ListNode(1)
s2 = ListNode(2)
s.next = s2
s3 = ListNode(3)
s2.next = s3
s4 = ListNode(3)
s3.next = s4
s5 = ListNode(4)
s4.next = s5
s6 = ListNode(4)
s5.next = s6
s7 = ListNode(5)
s6.next = s7
q = s
# while q:
# print(q.val)
# q = q.next
p = t.deleteDuplication(s)
while p:
print(p.val)
p = p.next
討論區看到的另一種做法,打問號那一句我一直以爲寫的是t.next = p,有點疑惑
class Solution:
def deleteDuplication(self, pHead):
if not pHead or not pHead.next:
return pHead
new = ListNode(-1)
new.next = pHead
p = pHead
t = new
q = None
while p and p.next:
q = p.next
if p.val == q.val:
while q and p.val == q.val:
q = q.next
p = q
t.next = p
else:
t = p # ?
p = p.next
return new.next
遞歸法:
class Solution:
def deleteDuplication(self, pHead):
if not pHead or not pHead.next:
return pHead
if pHead.val == pHead.next.val:
p = pHead.next
while p and p.val == pHead.val:
p = p.next
return self.deleteDuplication(p)
else:
pHead.next = self.deleteDuplication(pHead.next)
return pHead
第五十七題(樹)二叉樹的下一個結點
題目描述
給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點並且返回。注意,樹中的結點不僅包含左右子結點,同時包含指向父結點的指針。
以下情況:
1.二叉樹爲空,則返回空;
2.如果該節點的右孩子存在,則找到該節點右孩子的最左孩子,如果其右孩子沒有左孩子就是右孩子本身;
3.節點不是根節點。
如果該節點是其父節點的左孩子,則返回父節點;
否則繼續向上遍歷其父節點的父節點,重複之前的判斷,返回結果。
# class TreeLinkNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
# self.next = None
class Solution:
def GetNext(self, pNode):
if not pNode:
return None
if pNode.right:
p = pNode.right
while p.left:
p = p.left
return p
else: # !
if pNode.next:
p = pNode.next
while p.val < pNode.val and p.next:
p = p.next
if p.val < pNode.val:
return None
else:
return p
else:
return None
!下面的if判斷完全是幾遍提交,提醒我錯誤用例之後改出來的。要注意。
之前有一道二叉排序樹和雙向鏈表之前的轉換,如果先將樹變成中序下的鏈表,再找結點也不是不可以。
第五十八題(樹)對稱的二叉樹
題目描述
請實現一個函數,用來判斷一顆二叉樹是不是對稱的。注意,如果一個二叉樹同此二叉樹的鏡像是同樣的,定義其爲對稱的。
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def isSymmetrical(self, pRoot):
if not pRoot:
return True
else:
return self.panduan(pRoot.left, pRoot.right)
def panduan(self, left, right):
if not left and not right:
return True
elif left and right and left.val == right.val:
l = self.panduan(left.left, right.right)
r = self.panduan(right.left, left.right)
return l and r
else:
return False
第五十九題(樹)按之字形順序打印二叉樹
題目描述
請實現一個函數按照之字形打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右至左的順序打印,第三行按照從左到右的順序打印,其他行以此類推。
借用了層次遍歷的思想
用t1和t2分別一層隔一層的存儲每層的結點,幫助實現層次遍歷
然後用一個temp來存儲節點的值,一層正一層反地添加到最終輸出的out裏
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def Print(self, pRoot):
if not pRoot:
return []
out = []
t1 = [pRoot]
t2 = []
while t1 or t2:
if t1:
temp = []
while t1:
p = t1.pop(0)
temp.append(p.val)
if p.left:
t2.append(p.left)
if p.right:
t2.append(p.right)
out.append(temp)
if t2:
temp = []
while t2:
p = t2.pop(0)
temp.append(p.val)
if p.left:
t1.append(p.left)
if p.right:
t1.append(p.right)
temp.reverse()
out.append(temp)
return out
第六十題(樹)把二叉樹打印成多行
題目描述
從上到下按層打印二叉樹,同一層結點從左至右輸出。每一層輸出一行。
還是層次遍歷的思想,借用隊列先進先出的來實現
class Solution:
# 返回二維列表[[1,2],[4,5]]
def Print(self, pRoot):
if not pRoot:
return []
out = []
queue = [pRoot]
while queue:
temp = []
for i in range(len(queue)):
p = queue.pop(0)
temp.append(p.val)
if p.left:
queue.append(p.left)
if p.right:
queue.append(p.right)
out.append(temp)
return out
第六十一題(樹)序列化二叉樹
題目描述
請實現兩個函數,分別用來序列化和反序列化二叉樹
特地查了一下定義
二叉樹的序列化是指:把一棵二叉樹按照某種遍歷方式的結果以某種格式保存爲字符串,從而使得內存中建立起來的二叉樹可以持久保存。序列化可以基於 先序、中序、後序、按層 的二叉樹遍歷方式來進行修改。
二叉樹的反序列化是指:根據某種遍歷順序得到的序列化字符串結果str,重構二叉樹。
class Solution:
def Serialize(self, root):
out = []
def pre(root):
if not root:
out.append('#')
else:
out.append(str(root.val))
pre(root.left)
pre(root.right)
pre(root)
return ' '.join(out)
def Deserialize(self, s):
tr = s.split(' ')
def build():
if tr:
t = tr.pop(0)
if t == '#':
return None
root = TreeNode(int(t))
root.left = build()
root.right = build()
return root
return build()
第六十二題(樹)二叉搜索樹的第k個結點
題目描述
給定一棵二叉搜索樹,請找出其中的第k小的結點。例如, (5,3,7,2,4,6,8) 中,按結點數值大小順序第三小結點的值爲4。
二叉排序樹中序遍歷後可以得到順序排列的數組,所以直接中序遍歷
記得討論特殊情況 k <=0 或者k > len(t)
另外注意題乾的輸出"結點",不用val
class Solution:
# 返回對應節點TreeNode
def build(self, pRoot, t):
if not pRoot:
return None
self.build(pRoot.left, t)
t.append(pRoot)
self.build(pRoot.right, t)
def KthNode(self, pRoot, k):
t = []
self.build(pRoot, t)
if k <=0 or k > len(t):
return None
return t[k-1]
第六十三題(樹)數據流中的中位數
題目描述
如何得到一個數據流中的中位數?如果從數據流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從數據流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。我們使用Insert()方法讀取數據流,使用GetMedian()方法獲取當前讀取數據的中位數。
思路很簡單,但是python2和python3有區別,練習模式下只能用python2。用3的代碼提交了好多遍不通過,吐血
# python2
class Solution:
def __init__(self):
self.out = []
self.count = 0
def Insert(self, num):
self.out.append(num)
self.out.sort()
self.count += 1
def GetMedian(self, out):
if self.count % 2 == 1:
l = len(self.out)//2
return self.out[l]
elif self.count % 2 == 0:
l = len(self.out)//2
return (self.out[l]+self.out[l-1])/2.0
# python3
class Solution:
def __init__(self):
self.out = []
self.count = 0
def Insert(self, num):
self.out.append(num)
self.out.sort()
self.count += 1
def GetMedian(self):
if self.count % 2 == 1:
l = len(self.out)//2
return self.out[l]
elif self.count % 2 == 0:
l = len(self.out)//2
return (self.out[l]+self.out[l-1])/2
if __name__ == '__main__':
t = Solution()
t.Insert(5)
print(t.GetMedian())
t.Insert(2)
print(t.GetMedian())
t.Insert(3)
print(t.GetMedian())
t.Insert(4)
print(t.GetMedian())
第六十四題(棧和隊列)滑動窗口的最大值
題目描述
給定一個數組和滑動窗口的大小,找出所有滑動窗口裏數值的最大值。例如,如果輸入數組{2,3,4,2,6,2,5,1}及滑動窗口的大小3,那麼一共存在6個滑動窗口,他們的最大值分別爲{4,4,6,6,6,5}; 針對數組{2,3,4,2,6,2,5,1}的滑動窗口有以下6個: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
還是要注意邊界情況!很重要。
class Solution:
def maxInWindows(self, num, size):
out = []
if not num or size <= 0 or size > len(num):
return out
elif size == 1:
return num
else:
for i in range(len(num)-size+1):
temp = []
temp = num[i:i+size]
out.append(max(temp))
return out
2019.1.31連同之前剩下的兩道還有4道題,都是對我而言比較難的題。要好好看一下了。
第六十五題(N)(回溯法)矩陣中的路徑
題目描述
請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。如果一條路徑經過了矩陣中的某一個格子,則之後不能再次進入這個格子。 例如 a b c e s f c s a d e e 這樣的3 X 4 矩陣中包含一條字符串"bcced"的路徑,但是矩陣中不包含"abcb"路徑,因爲字符串的第一個字符b佔據了矩陣中的第一行第二個格子之後,路徑不能再次進入該格子。
m = [[0 for i in range(cols)]for j in range(rows)]
for i in range(rows):
for j in range(cols):
m[i][j] = matrix[i*cols+j]
原思路是轉換成雙層list形式做,下面的思路沒問題。
可是一直不通過,很疑惑!
class Solution:
def hasPath(self, matrix, rows, cols, path):
for i in range(rows):
for j in range(cols):
if matrix[i*cols + j] == path[0]:
if self.find_path(list(matrix), rows, cols, path[1:], i, j):
return True
def find_path(self, matrix, rows, cols, path, i, j):
if not path:
return True
matrix[i*cols + j] = 0
if j+1 < cols and matrix[i*cols+j+1] == path[0]:
return self.find_path(matrix, rows, cols, path[1:], i, j+1)
elif j > 0 and matrix[i*cols+j-1] == path[0]:
return self.find_path(matrix, rows, cols, path[1:], i, j-1)
elif i+1 < rows and matrix[(i+1)*cols+j] == path[0]:
return self.find_path(matrix, rows, cols, path[1:], i+1, j)
elif i >= 0 and matrix[(i-1)*cols+j] == path[0]:
return self.find_path(matrix, rows, cols, path[1:], i-1, j)
else:
return False
第六十六題(N)(回溯法)機器人的運動範圍
題目描述
地上有一個m行和n列的方格。一個機器人從座標0,0的格子開始移動,每一次只能向左,右,上,下四個方向移動一格,但是不能進入行座標和列座標的數位之和大於k的格子。 例如,當k爲18時,機器人能夠進入方格(35,37),因爲3+5+3+7 = 18。但是,它不能進入方格(35,38),因爲3+5+3+8 = 19。請問該機器人能夠達到多少個格子?
# -*- coding:utf-8 -*-
class Solution:
def movingCount(self, threshold, rows, cols):
matrix = [[0 for i in range(cols)]for j in range(rows)]
count = self.find(threshold, rows, cols, 0, 0, matrix)
return count
def find(self, threshold, rows, cols, i, j, m):
count = 0
if i<rows and j<cols and i>=0 and j>=0 and m[i][j] == 0 and self.judge(i, j, threshold):
m[i][j] = 1
count += 1+self.find(threshold,rows,cols,i+1,j,m)+self.find(threshold,rows,cols,i-1,j,m)+self.find(threshold,rows,cols,i,j+1,m)+self.find(threshold,rows,cols,i,j-1,m)
return count
def judge(self, i, j, k):
# temp = sum(map(int, str(i)+str(j)))
temp = 0
while i:
t = i%10
i = i//10
temp += t
while j:
t = j%10
j = j//10
temp += t
if temp <= k:
return True
else:
return False
希望能到心儀的城市找到心儀的工作,從提高自己的渣渣編程能力開始!
給自己立個flag:
不求多,但求每題都能認真去做!