AcWing 24. 機器人的運動范圍
地上有一個 m 行和 n 列的方格,橫縱坐標范圍分別是 0∼m−1 和 0∼n−1。
一個機器人從坐標0,0的格子開始移動,每一次只能向左,右,上,下四個方向移動一格。
但是不能進入行坐標和列坐標的數位之和大於 k 的格子。
請問該機器人能夠達到多少個格子?
樣例1
輸入:k=7, m=4, n=5
輸出:20
樣例2
輸入:k=18, m=40, n=40
輸出:1484
解釋:當k為18時,機器人能夠進入方格(35,37),因為3+5+3+7 = 18。
但是,它不能進入方格(35,38),因為3+5+3+8 = 19。
注意:
0<=m<=50
0<=n<=50
0<=k<=100
時間復雜度:\(o(nm)\)
寬搜與深搜(容易爆棧)
class Solution(object):
def bfs(self, x, y, k):
def f(x, y):
res = 0
for i in str(x):
res += int(i)
for j in str(y):
res += int(j)
return res
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
hh = 0
tt = -1
q = [[] for i in range(self.cols * self.rows + 100)]
tt += 1
q[tt] = [0, 0]
res = 0
while hh <= tt:
t = q[hh]
hh += 1
if self.st[t[0]][t[1]] == 1:
continue
self.st[t[0]][t[1]] = 1
res += 1
for i in range(4):
x1 = t[0] + dx[i]
y1 = t[1] + dy[i]
if x1 < self.rows and x1 >= 0 and y1 < self.cols and y1 >=0 and f(x1, y1) <= k and self.st[x1][y1] == 0:
tt += 1
q[tt] = [x1, y1]
return res
def movingCount(self, threshold, rows, cols):
"""
:type threshold: int
:type rows: int
:type cols: int
:rtype: int
"""
self.w = 0
self.st = [[0 for j in range(cols)] for i in range(rows)]
if rows == 0 or cols ==0 :
return 0
self.rows = rows
self.cols = cols
return self.bfs(0, 0, threshold)
深搜
class Solution(object):
def dfs(self, x, y, k):
self.w += 1
dx = [0, 0, 1, -1]
dy = [1, -1, 0, 0]
res = 1
for t in range(4):
x1 = dx[t] + x
y1 = dy[t] + y
#
#if self.w == 100:
# return 0;
def f(x, y):
res = 0
for i in str(x):
res += int(i)
for j in str(y):
res += int(j)
return res
if x1 < self.rows and x1 >= 0 and y1 < self.cols and y1 >=0 and f(x1, y1) <= k and self.st[x1][y1] == 0:
# print(x1, y1)
self.st[x1][y1] = 1
res += self.dfs(x1, y1, k)
# self.st[x1][y1] = 0
return res
def movingCount(self, threshold, rows, cols):
"""
:type threshold: int
:type rows: int
:type cols: int
:rtype: int
"""
self.w = 0
self.st = [[0 for j in range(cols)] for i in range(rows)]
if rows == 0 or cols ==0 :
return 0
self.rows = rows
self.cols = cols
self.st[0][0] = 1
return self.dfs(0, 0, threshold)
AcWing 25. 剪繩子
給你一根長度為 n 繩子,請把繩子剪成 m 段(m、n 都是整數,2≤n≤58 並且 m≥2)。
每段的繩子的長度記為k[0]、k[1]、……、k[m]。k[0]k[1] … k[m] 可能的最大乘積是多少?
例如當繩子的長度是8時,我們把它剪成長度分別為2、3、3的三段,此時得到最大的乘積18。
樣例
輸入:8
輸出:18
算法
(數學) O(n)
這道題目是數學中一個很經典的問題。
下面我們給出證明:
首先把一個正整數 N拆分成若干正整數只有有限種拆法,所以存在最大乘積。
假設 N=n1+n2+…+nk
,並且 n1×n2×…×nk
是最大乘積。
- 顯然1不會出現在其中;
- 如果對於某個
i
有ni≥5
,那么把 ni 拆分成3 + (ni−3)
,我們有3(ni−3) = 3ni−9 > ni
;推出不能有大於5
的數。 - 如果
ni=4
,拆成2+2
乘積不變,所以不妨假設沒有4
; - 如果有三個以上的2,那么
3×3>2×2×2
,所以替換成3乘積更大;
綜上,選用盡量多的3,直到剩下2或者4時,用2。
時間復雜度分析:當 n比較大時,n 會被拆分成 ⌈n/3⌉
個數,我們需要計算這么多次減法和乘法,所以時間復雜度是 O(n)
。
class Solution(object):
def maxProductAfterCutting(self,length):
"""
:type length: int
:rtype: int
"""
res = 1
if length % 3 == 0:
while length > 0:
length -= 3
res *= 3
elif length % 3 == 1:
length -= 4
while length > 0:
length -= 3
res *= 3
res *= 4
elif length % 3 == 2:
length -= 2
if length == 0:
return 1
while length > 0:
length -= 3
res *= 3
res *= 2
return res
AcWing 26. 二進制中1的個數
正數補碼是本身,
負數補碼 :符號位不變,逐位求反(從頭開始), 加1。
思路1:
lowbit : n&(n-1)的結果:n最右邊的1變成0,比如n為6
110&101-》100
循環直到n為0為止
上述思想C++可以直接過。
為什么python中負數需要和 0xFFFFFFFF 做與操作?
在計算機中,所有的數字都是使用補碼存儲起來的。由於Python沒有位數這個概念,所以得到二進制表示需要多一點操作,即將位數限制在32位,通過和一個32位的全1數字按位與運算即可。但對於負數來說,直接bin(-1)是不能得到其補碼的,而是得到了1的原碼前面加上了負號,即-0b1。則通過和一個32位的全1數字按位與運算可得到其補碼二進制表示對應的十進制數(按位與運算把符號位的1視為了數字)。Python要使用 n & 0xffffffff 得到一個數的補碼
class Solution(object):
def NumberOf1(self,n):
"""
:type n: int
:rtype: int
"""
res = 0
if n < 0:
n = n & 0xFFFFFFFF
while n != 0 :
n -= n & (-n)
res += 1
return res
思路2
(位運算) O(logn)
迭代進行如下兩步,直到 n 變成0為止:
如果 n在二進制表示下末尾是1,則在答案中加1;
將 n右移一位,也就是將 nn 在二進制表示下的最后一位刪掉;
這里有個難點是如何處理負數。
在C++中如果我們右移一個負整數,系統會自動在最高位補1,這樣會導致 n 永遠不為0,就死循環了。
解決辦法是把 n 強制轉化成無符號整型,這樣 n 的二進制表示不會發生改變,但在右移時系統會自動在最高位補0。
時間復雜度
每次會將 n除以2,最多會除 logn 次,所以時間復雜度是 O(logn)。
class Solution {
public:
int NumberOf1(int n) {
int res = 0;
unsigned int un = n;
while (un) res += un & 1, un >>= 1;
return res;
}
};
AcWing 27. 數值的整數次方
實現函數double Power(double base, int exponent),求base的 exponent次方。
不得使用庫函數,同時不需要考慮大數問題。
注意:
不會出現底數和指數同為0的情況
當底數為0時,指數一定為正
樣例1 輸入:10 ,2
輸出:100樣例2
輸入:10 ,-2 >
輸出:0.01
注意一下負數即可
https://pxlsdz.blog.csdn.net/article/details/109124180
class Solution(object):
def Power(self, base, exponent):
"""
:type base: float
:type exponent: int
:rtype: float
"""
b = exponent
exponent = abs(exponent)
res = 1
while exponent > 0:
if exponent & 1 == 1:
res *= base
base = base * base
exponent >>= 1
if b < 0:
res = 1 / res
return res
AcWing 28. 在O(1)時間刪除鏈表結點
算法
(鏈表) O(1)
給定單向鏈表的一個節點指針,定義一個函數在O(1)時間刪除該結點。
假設鏈表一定存在,並且該節點一定不是尾節點。
樣例 輸入:鏈表 1->4->6->8
刪掉節點:第2個節點即6(頭節點為第0個節點)
輸出:新鏈表 1->4->8
由於是單鏈表,我們不能找到前驅節點,所以我們不能按常規方法將該節點刪除。
我們可以換一種思路,將下一個節點的值復制到當前節點,然后將下一個節點刪除即可。
時間復雜度
只有常數次操作,所以時間復雜度是 O(1)。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void
"""
node.val = node.next.val
node.next = node.next.next
AcWing 29. 刪除鏈表中重復的節點
算法
(線性掃描) O(n)
為了方便處理邊界情況,我們定義一個虛擬元素 dummy 指向鏈表頭節點。
然后從前往后掃描整個鏈表,每次掃描元素相同的一段,如果這段中的元素個數多於1個,則將整段元素直接刪除。
時間復雜度
整個鏈表只掃描一遍,所以時間復雜度是 O(n)。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def deleteDuplication(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
dumpy = ListNode(None)
dumpy.next = head
p = dumpy
while p != None:
q = p.next
while p.next != None and q.next != None and p.next.val == q.next.val:
q = q.next
if p.next != q:
p.next = q.next
else: p = p.next
return dumpy.next
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplication(ListNode* head) {
auto dummy = new ListNode(-1);
dummy->next = head;
auto p = dummy;
while (p->next) {
auto q = p->next;
while (q && p->next->val == q->val) q = q->next;
if (p->next->next == q) p = p->next;
else p->next = q;
}
return dummy->next;
}
};
AcWing 30. 正則表達式匹配
題目描述
請實現一個函數用來匹配包括’.’和’*’的正則表達式。
模式中的字符’.’表示任意一個字符,而’*’表示它前面的字符可以出現任意次(含0次)。
在本題中,匹配是指字符串的所有字符匹配整個模式。
例如,字符串”aaa”與模式”a.a”和”abaca”匹配,但是與”aa.a”和”ab*a”均不匹配。
樣例 輸入:
s="aa" p="a*"
輸出:true
算法
(動態規划) O(nm)
狀態表示:f[i][j]為所有s[1...i]與p[1....j]匹配的方案的集合。
屬性:集合是否為空
狀態轉移:
- 如果p[j]不是通配符'*'
\[f[i][j] = (s[i] == p[j]\ or\ p[j] == '.')\ \&\&\ f[i-1][j-1]== 1 \]
- 如果p[j+1]是通配符'
*'
,以*
表示字母個數來划分集合。
- 當
*
表示的字母個數為0個時:
- 當
*
表示的字母個數為1個時:\[f[i][j] = f[i-1][j-2] \&\& s[i] ==p[j-1] \] - 當
*
表示的字母個數為2個時:
$$f[i][j] = f[i-2][j-2] \&\& s[i] ==p[j-1\&\&s[i-1]==p[j-1]$$
......
類似完全背包的優化方式
后半部都多了\(s[i-1] == p[j-1]\),結合律(A&b|A&c=A&(b|c))即可推出$$f[i][j] =( f[i][j-2])\ ||\ (f[i-1][j] & s[i]== p[j-1])$$
時間復雜度分析:
n表示s的長度,m 表示p的長度,總共 nm個狀態,狀態轉移復雜度 O(1),所以總時間復雜度是 O(nm).
class Solution(object):
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
n = len(s)
m = len(p)
s = " " + s
p = " " + p
dp = [[False for j in range(m + 1)] for i in range(n + 1)]
dp[0][0] = True
for i in range(0, n + 1):
for j in range(1, m + 1):
if j + 1 < m and p[j+1] == "*":
continue
if i and p[j] != "*":
dp[i][j] = dp[i - 1][j - 1] and (s[i] == p[j] or p[j] == '.')
elif p[j] == '*':
dp[i][j] = dp[i][j - 2] or (i and dp[i - 1][j] and (s[i] == p[j - 1] or p[j - 1] == '.'))
return dp[n][m]
class Solution(object):
def isMatch(self, s, p):
"""
:type s: str
:type p: str
:rtype: bool
"""
n = len(s)
m = len(p)
f = [[False] * (m+1) for i in range(n+1)]
s = ' ' + s
p = ' ' + p
f[0][0] = True
for i in range(n+1):
for j in range(1, m+1):
if j + 1 < m and p[j+1] == '*':
continue
if i and p[j] != '*':
if s[i] == p[j] or p[j] == '.':
f[i][j] = f[i-1][j-1]
elif p[j] == '*':
f[i][j] = f[i][j-2] or (f[i-1][j] and (s[i] == p[j-1] or p[j-1] == '.'))
return f[n][m]
AcWing 31. 表示數值的字符串
請實現一個函數用來判斷字符串是否表示數值(包括整數和小數)。
例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示數值。
但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
注意:
- 小數可以沒有整數部分,例如.123等於0.123;
- 小數點后面可以沒有數字,例如233.等於233.0;
- 小數點前面和后面可以有數字,例如233.666;
- 當e或E前面沒有數字時,整個字符串不能表示數字,例如.e1、e1;
- 當e或E后面沒有整數時,整個字符串不能表示數字,例如12e、12e+5.4;
樣例: 輸入: "0"
輸出: true
class Solution(object):
def isNumber(self, s):
"""
:type s: str
:rtype: bool
"""
try :
float(s)
return True
except:
return False
AcWing 32. 調整數組順序使奇數位於偶數前面
算法
(雙指針掃描) O(n)
用兩個指針分別從首尾開始,往中間掃描。掃描時保證第一個指針前面的數都是奇數,第二個指針后面的數都是偶數。
每次迭代時需要進行的操作:
- 第一個指針一直往后走,直到遇到第一個偶數為止;
- 第二個指針一直往前走,直到遇到第一個奇數為止;
- 交換兩個指針指向的位置上的數,再進入下一層迭代,直到兩個指針相遇為止;
時間復雜度
當兩個指針相遇時,走過的總路程長度是 n,所以時間復雜度是 O(n)。
class Solution(object):
def reOrderArray(self, array):
"""
:type array: List[int]
:rtype: void
"""
i = 0
j = len(array) - 1
while i < j:
while i< j and array[i] & 1 == 1:
i += 1
while j > i and array[j] & 1 != 1:
j -= 1
if i <j : array[i], array[j] = array[j], array[i]
AcWing 33. 鏈表中倒數第k個節點
輸入一個鏈表,輸出該鏈表中倒數第k個結點。
注意:
- k >= 0; 如果k大於鏈表長度,則返回 NULL;
樣例
輸入:鏈表:1->2->3->4->5 ,k=2
輸出:4
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def findKthToTail(self, pListHead, k):
"""
:type pListHead: ListNode
:type k: int
:rtype: ListNode
"""
p = pListHead
n = 1
while p != None:
n += 1
p = p.next
if k > n:
return None
p = pListHead
i = 1
while p != None:
if i == n-k:
return p
p = p.next
i += 1
AcWing 34. 鏈表中環的入口結點
給定一個鏈表,若其中包含環,則輸出環的入口節點。
若其中不包含環,則輸出null。
樣例
給定如上所示的鏈表:
[1, 2, 3, 4, 5, 6]
2
注意,這里的2表示編號是2的節點,節點編號從0開始。所以編號是2的節點就是val等於3的節點。則輸出環的入口節點3.
算法
(鏈表,快慢指針掃描) O(n)
用兩個指針 first,second 分別從起點開始走,first 每次走一步,second 每次走兩步。
如果過程中 second走到null,則說明不存在環。否則當 first 和 second相遇后,讓 first返回起點,second 待在原地不動,然后兩個指針每次分別走一步,當相遇時,相遇點就是環的入口。
證明:如上圖所示,a 是起點,b 是環的入口,c 是兩個指針的第一次相遇點,ab 之間的距離是 x,bc 之間的距離是 y, z表示從 cc點順時針走到 b 的距離。
則第一次相遇時 second所走的距離是 \(x+(y+z)∗n+y\), n表示圈數,同時 second 走過的距離是 first 的兩倍,也就是 \(2(x+y)\),所以我們有\(x+(y+z)∗n+y=2(x+y)\),所以 $$x=(n−1)*(y+z)+z$$
y+z是一圈的距離,z = x %(y+z),那么我們讓 second從 c 點開始走,走 x步,會恰好走到 b 點;讓 first 從 a 點開始走,走 x 步,也會走到 b 點。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def entryNodeOfLoop(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
first = head
second = head
while first and second:
if first == None or second == None:
return None
first = first.next
second = second.next
if second :
second = second.next
if first == second:
first = head
while first and second:
if first == second:
return first
first = first.next
second = second.next
return None
map o(nlogn) 空間o(n)
class Solution {
public:
ListNode *entryNodeOfLoop(ListNode *head) {
map<ListNode*,int> mp;
while(head)
{
if(mp[head])
return head;
mp[head]=1;
head=head->next;
}
return NULL;
}
};