劍指offer 第12-22題


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不會出現在其中;
  • 如果對於某個 ini≥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]匹配的方案的集合。
屬性:集合是否為空
狀態轉移:

  1. 如果p[j]不是通配符'*'

    \[f[i][j] = (s[i] == p[j]\ or\ p[j] == '.')\ \&\&\ f[i-1][j-1]== 1 \]

  2. 如果p[j+1]是通配符'*'*表示字母個數來划分集合
  • *表示的字母個數為0個時:

\[f[i][j] = f[i][j-2] \]

  • *表示的字母個數為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]$$

......

類似完全背包的優化方式

\[f[i][j] = (f[i][j-2])|| (f[i-1][j-2] \&\& s[i] ==p[j-1])|| (f[i-2][j-2] \&\& s[i] ==p[j-1]\&\&s[i-1]==p[j-1]) \]

\[f[i-1][j] = (f[i-1][j-2])|| (f[i-2][j-2] \&\& s[i-1] ==p[j-1])|| (f[i-3][j-2] \&\& s[i-1] ==p[j-1\&\&s[i-2]==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"都不是。

注意:

  1. 小數可以沒有整數部分,例如.123等於0.123;
  2. 小數點后面可以沒有數字,例如233.等於233.0;
  3. 小數點前面和后面可以有數字,例如233.666;
  4. 當e或E前面沒有數字時,整個字符串不能表示數字,例如.e1、e1;
  5. 當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;
    }
};


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM