劍指Offer 第34-44題


AcWing 46. 二叉搜索樹的后序遍歷序列

輸入一個整數數組,判斷該數組是不是某二叉搜索樹的后序遍歷的結果。

如果是則返回true,否則返回false。

假設輸入的數組的任意兩個數字都互不相同。

樣例
輸入:[4, 8, 6, 12, 16, 14, 10]

輸出:true

題解:

  1. 中序(二叉搜索樹的中序是從小到大的結果)+后序建樹
  2. 后序遍歷最后一個數為根,二叉搜索樹的性質,我們直接從頭開始找到比根小的值,這便是左子樹;右邊部分便是右子樹,如果不符合便是錯誤。
class Solution {
public:
    vector<int>  seq;
    bool verifySequenceOfBST(vector<int> sequence) {
        seq = sequence;
        if(seq.empty()) return true;
        return dfs(1, seq.size() - 1);
    }
    bool dfs(int l, int r){
        if(l >= r) return true;
        int root = seq[r];
        int  k = 0;
        while(k < r && seq[k] < root) k++;
        for(int i = k; i < r; i++)
        {A
            if(seq[i] < root) return false;
        }
        return dfs(l, k - 1) && dfs(k, r - 1);
    }
};

AcWing 47. 二叉樹中和為某一值的路徑

輸入一棵二叉樹和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。

從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。

樣例
給出二叉樹如下所示,並給出num=22。
在這里插入圖片描述
dfs

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> findPath(TreeNode* root, int sum) {
        if(root == NULL) return res;
        vector<int> path;
        dfs(root, sum, path);
        return res;
    }
    void dfs(TreeNode* root, int sum, vector<int>&path){
        if(root->left == NULL && root->right == NULL)
        {
            if(sum - root->val == 0)
            {
                path.push_back(root->val);
                res.push_back(path);
                path.pop_back();
            }
            return;
        }
        path.push_back(root->val);
        if(root->left != NULL) dfs(root->left, sum - root->val, path);
        if(root->right != NULL) dfs(root->right, sum - root->val, path);
        path.pop_back();
    }
};

AcWing 48. 復雜鏈表的復刻

請實現一個函數可以復制一個復雜鏈表。

在復雜鏈表中,每個結點除了有一個指針指向下一個結點外,還有一個額外的指針指向鏈表中的任意結點或者null。

注意:

函數結束后原鏈表要與輸入時保持一致。

  1. hash表做映射 時間O(n) 空間O(n)
  2. 在這里插入圖片描述
/**
 * Definition for singly-linked list with a random pointer.
 * struct ListNode {
 *     int val;
 *     ListNode *next, *random;
 *     ListNode(int x) : val(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *copyRandomList(ListNode *head) {
        if(!head) return head;
        unordered_map<ListNode*, ListNode*> pos;
        for(auto p = head; p; p = p -> next)
        {
            pos[p] = new ListNode(p->val);
        }
        
        pos[NULL] = NULL;
        for(auto p = head; p; p = p -> next)
        {
            pos[p]->next = pos[p->next];
            pos[p]->random = pos[p->random];
        }
        return pos[head];
    }
};
/**
 * Definition for singly-linked list with a random pointer.
 * struct ListNode {
 *     int val;
 *     ListNode *next, *random;
 *     ListNode(int x) : val(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *copyRandomList(ListNode *head) {
        // 每一個節點后面加復制
        for(auto p = head; p; p = p->next)
        {
            auto np = new ListNode(p->val);
            auto next = p->next;
            p->next = np;
            np->next = next;
            p = np;
        }
        
        // 復制random指針
        for(auto p = head; p; p = p->next->next)
        {
            if(p->random)
                p->next->random = p->random->next;
        }
        
        // 分離兩個鏈表
        auto dummy = new ListNode(-1);
        auto cur = dummy;
        for(auto p = head; p; p = p->next)
        {
            cur->next = p->next;
            cur = cur->next;
            p->next = p->next->next;
        }
        return dummy->next;
    }
};

AcWing 49. 二叉搜索樹與雙向鏈表

輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。

要求不能創建任何新的結點,只能調整樹中結點指針的指向。

注意:

需要返回雙向鏈表最左側的節點。
例如,輸入下圖中左邊的二叉搜索樹,則輸出右邊的排序雙向鏈表。
在這里插入圖片描述
解題思路
實現返回每一個子樹的兩端節點即可

AcWing 50. 序列化二叉樹

AcWing 51. 數字排列(good)

輸入一組數字(可能包含重復數字),輸出其所有的排列方式。

樣例
輸入:[1,2,3]

輸出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

題解

根以前的枚舉不一樣,需要用占位的思想去枚舉,預先開出n個坑位,將nums每一個數往里面放,一個數只能放在前一個相同數字的后面。

class Solution {
public:
    int n = 0;
    vector<vector<int> > ans;
    vector<int> path;//位置
    //u表示枚舉到nums第幾個數了,start表示從空位的第幾位開始可以放
    void dfs(int u, int start, int state, vector<int>& nums)
    {
        if(u == n)
        {
            ans.push_back(path);
            return;
        }
        
        // 頭一個數或者與上一個數不同
        if(u == 0 || nums[u] != nums[u-1]) start = 0;
        for(int i = start; i < n; i++)
        {
            if(!(state>>i&1))
            {
                path[i] = nums[u];
                dfs(u + 1, i + 1, state+(1<<i), nums);
            }
        }
    }
    vector<vector<int>> permutation(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        n = nums.size();
        path.resize(n);
        dfs(0, 0, 0, nums);
    
        return ans;
    }
};

庫函數

class Solution {
public:
    vector<vector<int>> permutation(vector<int>& nums) {
        sort(nums.begin(), nums.end());

        vector<vector<int>> res;
        do
        {
            res.push_back(nums);
        }while (next_permutation(nums.begin(),nums.end()));
        // do res.push_back(nums); while (next_permutation(nums.begin(), nums.end()));

        return res;
    }
};

AcWing 52. 數組中出現次數超過一半的數字

數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。

假設數組非空,並且一定存在滿足條件的數字。

思考題:

假設要求只能使用 O(n) 的時間和額外 O(1) 的空間,該怎么做呢?
樣例
輸入:[1,2,1,1,3]

輸出:1

題解

抵消(配對)的思想。
循環數組

  1. count == 0: value=a[i], count=1
  2. count != 0:
    • value != a[i] : count -= 1
    • value == a[i] : count += 1

n個村民
好人數量 > n/2 (回答的是正確)
壞蛋數量 < n/2 (回答隨機)
o(n)次詢問找出壞蛋。

count: 當前人被信任的次數,0
val: 人是誰,-1

從頭循環,

  1. count == 0,首先問自己是否被信任,
    回答是好人,count += 1,val = id
    回答壞蛋的一定是壞蛋,好人只會回答自己是好人。
  2. count != 0, 問val十分是好人,若val是好人,則count += 1。
    若是壞人,則count -= 1。count減到0,則替換
    好人的信任值。

好人的信用量一定大於壞人的信用量,消耗一個好人肯定需要消耗一個壞人。

class Solution {
public:
    int moreThanHalfNum_Solution(vector<int>& nums) {
        int cnt = 0, val = -1;
        for(auto it: nums)
        {
            if(cnt == 0)
            {
                val = it;
                cnt ++;
            }
            else
            {
                if(val == it) cnt ++;
                else cnt --;
            }
        }
        return val;
    }
};

AcWing 53. 最小的k個數

輸入n個整數,找出其中最小的k個數。

注意:

數據保證k一定小於等於輸入數組的長度;
輸出數組內元素請按從小到大順序排序;
樣例
輸入:[1,2,3,4,5,6,7,8] , k=4

輸出:[1,2,3,4]

用大根堆維護一下即可,(優先隊列)

class Solution {
public:

    vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
        priority_queue<int> heap;
        for(auto it : input)
        {
            
            if(heap.size() >= k)
            {
               
                if(it < heap.top())
                {
                    
                    heap.pop();
                    heap.push(it);
                }
            }
            else
                heap.push(it);
           
        }
        vector<int> res;
        for(int i = 0; i < k; i++)
        {
            res.push_back(heap.top());
            heap.pop();
        }
        reverse(res.begin(), res.end());
        return res;
    }
};
class Solution {
public:
    vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
        priority_queue<int> heap;
        for (auto x : input)
        {
            if (heap.size() < k || heap.top() > x) heap.push(x);
            if (heap.size() > k) heap.pop();
        }
        vector<int> res;
        while (heap.size()) res.push_back(heap.top()), heap.pop();
        reverse(res.begin(), res.end());
        return res;
    }
};


AcWing 54. 數據流中的中位數

如何得到一個數據流中的中位數?

如果從數據流中讀出奇數個數值,那么中位數就是所有數值排序之后位於中間的數值。

如果從數據流中讀出偶數個數值,那么中位數就是所有數值排序之后中間兩個數的平均值。

樣例
輸入:1, 2, 3, 4

輸出:1,1.5,2,2.5

解釋:每當數據流讀入一個數據,就進行一次判斷並輸出當前的中位數。

題解
維護兩個堆,大根堆維護較小的那部分,小根堆維護較大那部分,保證大跟堆的數量最多只比小根堆的數量多1個即可,代碼中實現的是比較簡捷的做法。

class Solution {
public:
    priority_queue<int, vector<int>, greater<int> > heap_u;
    priority_queue<int> heap_d;
    int n = 0;
    void insert(int num){
        n++; 
        heap_d.push(num);
      
        while(heap_u.size() != 0 && heap_d.top() > heap_u.top())
        {
            int maxx = heap_d.top(), minn = heap_u.top();
            heap_d.pop(), heap_u.pop();
            heap_d.push(minn), heap_u.push(maxx);
        }
       
        
        if(heap_d.size() - heap_u.size() > 1)
        {
            heap_u.push(heap_d.top());
            heap_d.pop();
        }
    }

    double getMedian(){
        if(n & 1)
            return heap_d.top();
        else
            return (heap_d.top() + heap_u.top()) / 2.0;
    }
};

AcWing 55. 連續子數組的最大和

輸入一個 非空 整型數組,數組里的數可能為正,也可能為負。

數組中一個或連續的多個整數組成一個子數組。

求所有子數組的和的最大值。

要求時間復雜度為O(n)。

樣例
輸入:[1, -2, 3, 10, -4, 7, 2, -5]

輸出:18

題解
狀態表示:
集合划分:以a[k] 為結尾的連續子數組

  1. 只有a[k]一個數
  2. 至少兩個數,一定選a[k],所以可以去掉a[k]
    在這里插入圖片描述
    相當於f(k-1) + a[k]

f(k) = max{a[k], f(k-1) +a[k]}
f(k) = a[k] + max(0, f(k-1))
S = a[k] + max(0, S)

AcWing 56. 從1到n整數中1出現的次數


免責聲明!

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



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