程式碼隨想錄演算法訓練營day37 | leetcode 738. 單調遞增的數字、968. 監控二叉樹

Humphreyr發表於2024-04-03

目錄
  • 題目連結:738. 單調遞增的數字-中等
  • 題目連結:968. 監控二叉樹-困難

題目連結:738. 單調遞增的數字-中等

題目描述:

當且僅當每個相鄰位數上的數字 xy 滿足 x <= y 時,我們稱這個整數是單調遞增的。

給定一個整數 n ,返回 小於或等於 n 的最大數字,且數字呈 單調遞增

示例 1:

輸入: n = 10
輸出: 9

示例 2:

輸入: n = 1234
輸出: 1234

示例 3:

輸入: n = 332
輸出: 299

提示:

  • 0 <= n <= 10^9

此題需要從後往前遍歷,當遇到前一位比該位大的時候,將前一位數字減一,向前繼續遍歷,直到最大位(即第1位),記錄下需要改的最大位(即迴圈裡後面遍歷到的),從這一位開始後面的都修改為9,得到的就是結果。

程式碼如下:

// 時間複雜度:O(n),n 為數字長度
// 空間複雜度:O(n),需要一個字串,轉化為字串操作更方便
class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        string str = to_string(n);
        int flag = str.size();
        for (int i = str.size() - 1; i > 0; --i) {
            if (str[i - 1] > str[i]) {
                --str[i - 1];
                flag = i;
            }
        }
        for (int i = flag; i < str.size(); ++i) {
            str[i] = '9';
        }
        return stoi(str);
    }
};

題目連結:968. 監控二叉樹-困難

題目描述:

給定一個二叉樹,我們在樹的節點上安裝攝像頭。

節點上的每個攝影頭都可以監視其父物件、自身及其直接子物件。

計算監控樹的所有節點所需的最小攝像頭數量。

示例 1:

img

輸入:[0,0,null,0,0]
輸出:1
解釋:如圖所示,一臺攝像頭足以監控所有節點。

示例 2:

img

輸入:[0,0,null,0,null,0,null,null,0]
輸出:2
解釋:需要至少兩個攝像頭來監視樹的所有節點。 上圖顯示了攝像頭放置的有效位置之一。

提示:

  1. 給定樹的節點數的範圍是 [1, 1000]
  2. 每個節點的值都是 0。

主要有如下四類情況:

  • 情況1:左右節點都有覆蓋

左孩子有覆蓋,右孩子有覆蓋,那麼此時中間節點應該就是無覆蓋的狀態了。

  • 情況2:左右節點至少有一個無覆蓋的情況

如果是以下情況,則中間節點(父節點)應該放攝像頭

  • 情況3:左右節點至少有一個有攝像頭

如果是以下情況,其實就是 左右孩子節點有一個有攝像頭了,那麼其父節點就應該是2(覆蓋的狀態)

  • 情況4:頭結點沒有覆蓋

以上都處理完了,遞迴結束之後,可能頭結點 還有一個無覆蓋的情況

程式碼如下:

// 時間複雜度: O(n),需要遍歷二叉樹上的每個節點
// 空間複雜度: O(n)
class Solution {
private:
    int result;
    int traversal(TreeNode* cur) {

        // 空節點,該節點有覆蓋
        if (cur == NULL) return 2;

        int left = traversal(cur->left);    // 左
        int right = traversal(cur->right);  // 右

        // 情況1
        // 左右節點都有覆蓋
        if (left == 2 && right == 2) return 0;

        // 情況2
        // left == 0 && right == 0 左右節點無覆蓋
        // left == 1 && right == 0 左節點有攝像頭,右節點無覆蓋
        // left == 0 && right == 1 左節點有無覆蓋,右節點攝像頭
        // left == 0 && right == 2 左節點無覆蓋,右節點覆蓋
        // left == 2 && right == 0 左節點覆蓋,右節點無覆蓋
        if (left == 0 || right == 0) {
            result++;
            return 1;
        }

        // 情況3
        // left == 1 && right == 2 左節點有攝像頭,右節點有覆蓋
        // left == 2 && right == 1 左節點有覆蓋,右節點有攝像頭
        // left == 1 && right == 1 左右節點都有攝像頭
        // 其他情況前段程式碼均已覆蓋
        if (left == 1 || right == 1) return 2;

        // 以上程式碼我沒有使用else,主要是為了把各個分支條件展現出來,這樣程式碼有助於讀者理解
        // 這個 return -1 邏輯不會走到這裡。
        return -1;
    }

public:
    int minCameraCover(TreeNode* root) {
        result = 0;
        // 情況4
        if (traversal(root) == 0) { // root 無覆蓋
            result++;
        }
        return result;
    }
};

精簡如下:

class Solution {
private:
    int result;
    int traversal(TreeNode* cur) {
        if (cur == NULL) return 2;
        int left = traversal(cur->left);    // 左
        int right = traversal(cur->right);  // 右
        if (left == 2 && right == 2) return 0;
        else if (left == 0 || right == 0) {
            result++;
            return 1;
        } else return 2;
    }
public:
    int minCameraCover(TreeNode* root) {
        result = 0;
        if (traversal(root) == 0) { // root 無覆蓋
            result++;
        }
        return result;
    }
};

相關文章