国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【leetcode刷題之路】劍指Offer(4)——分治+排序算法+動態(tài)規(guī)劃

這篇具有很好參考價值的文章主要介紹了【leetcode刷題之路】劍指Offer(4)——分治+排序算法+動態(tài)規(guī)劃。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

8 分治算法

8.1 【遞歸】劍指 Offer 07 - 重建二叉樹

https://leetcode.cn/problems/zhong-jian-er-cha-shu-lcof/

??前序遍歷是根左右,中序遍歷是左根右,這也就意味著前序遍歷的第一個節(jié)點是整棵樹的根節(jié)點,順著這個節(jié)點找到它在中序遍歷中的位置,即為in_root,那么in_root左邊的都在左子樹,右邊的都在右子樹,這樣就可以知道左子樹一共有多少個節(jié)點,然后去前序遍歷中找到左右子樹的分界點,分成左右兩部分,分別重復上述過程,找到各自部分的第一個根節(jié)點,然后再依次往下進行,直到最后左右子樹的邊界發(fā)生重合,此時二叉樹重建完畢。

/**
 * 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 {
private:
    unordered_map<int,int> hash_table;
public:
    TreeNode* subTree(vector<int>& preorder, vector<int>& inorder, int pre_l, int pre_r, int in_l, int in_r)
    {
        if(pre_l > pre_r) return nullptr;
        //找到根節(jié)點,計算左子樹的節(jié)點數(shù)
        int pre_root = pre_l;
        int in_root = hash_table[preorder[pre_l]];
        int sub_l = in_root - in_l;
        //開始生成root
        TreeNode* root = new TreeNode(preorder[pre_l]);
        //對于左子樹而言,前序遍歷的[左邊界+1,左邊界+左子樹節(jié)點數(shù)]即為中序遍歷的[左邊界,根節(jié)點-1]
        root->left = subTree(preorder, inorder, pre_l+1, pre_l+sub_l, in_l, in_root-1);
        //對于右子樹而言,前序遍歷的[左邊界+左子樹節(jié)點數(shù)+1,右邊界]即為中序遍歷的[根節(jié)點+1,右邊界]
        root->right = subTree(preorder, inorder, pre_l+sub_l+1, pre_r, in_root+1, in_r);
        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) 
    {
        int n = inorder.size();
        for(int i=0;i<n;i++)
        {
            hash_table[inorder[i]] = i;
        }
        return subTree(preorder, inorder, 0, n-1, 0, n-1);
    }
};
8.2 【遞歸】【快速冪】劍指 Offer 16 - 數(shù)值的整數(shù)次方

https://leetcode.cn/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/description/

??這道題可以用快速冪來解決,具體思路就是把冪次按照二進制拆開,分別計算,下面舉個例子: 假設我要計算 x 10 x^{10} x10,因為10的二進制表示為1010,那么 x 10 = x 2 0 ? 0 ? x 2 1 ? 1 ? x 2 2 ? 0 ? x 2 3 ? 1 x^{10}=x^{2^0*0}*x^{2^1*1}*x^{2^2*0}*x^{2^3*1} x10=x20?0?x21?1?x22?0?x23?1,可以看作按照x的2次方依次遞增,只需要看這一個次方對應的二進制是否為1。

class Solution {
public:
    double myPow(double x, int n) {
        if(x==1 || n==0) return 1;
        if(x==0) return 0;

        double ans = 1;
        long num = n;
        if(n<0)
        {
            num = -num;
            x = 1/x;
        }
        while(num)
        {
            if(num & 1) ans *= x;
            x *= x;
            num >>= 1;
        }
        return ans;
    }
};
8.3 【遞歸】劍指 Offer 33 - 二叉搜索樹的后序遍歷序列

https://leetcode.cn/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof

??后序遍歷的特點就是先訪問左子樹再訪問右子樹最后訪問根節(jié)點,所以最后一個元素一定根節(jié)點,而二叉搜索樹的特點就是左子樹<根節(jié)點<右子樹,所以我們可以根據(jù)根節(jié)點的值把數(shù)組分為兩部分,然后分別判斷是否符合二叉搜索樹的特點,重復上述過程直到所有情況都判斷完為止。

class Solution {
public:
    bool dfs(vector<int>& postorder, int left, int right)
    {
        if(left >= right) return true;

        int i = left;
        while(postorder[i]<postorder[right])
        {
            i++;
        }
        int mid = i;
        while(postorder[i]>postorder[right])
        {
            i++;
        }
        return i==right & dfs(postorder,left,mid-1) & dfs(postorder,mid,right-1);
    }
    bool verifyPostorder(vector<int>& postorder)
    {
        return dfs(postorder,0,postorder.size()-1);
    }
};
8.4 【遞歸】【分治】劍指 Offer 17 - 打印從1到最大的n位數(shù)

https://leetcode.cn/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof

??這道題目由于指定了數(shù)組是int型,所以不用考慮大整數(shù)問題,直接暴力就可以解決,但如果是大整數(shù)的話,需要采用分治遞歸的思想,主要如下:

(1)第一層遍歷,為n,分別考慮到生成的數(shù)字是1位數(shù)、2位數(shù)、3位數(shù)…n位數(shù);

(2)第二層遍歷,分別遍歷每一位數(shù)是幾,除了第一位是1-9之外,其余都是0-9。

class Solution {
public:
    vector<int> printNumbers(int n) {
        int cnt = pow(10,n);
        vector<int> ans;
        for(int i=1;i<cnt;i++)
        {
            ans.push_back(i);
        }
        return ans;
    }
};
8.5 【歸并排序】【分治】劍指 Offer 51 - 數(shù)組中的逆序對

https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof

??這位大佬寫的很好?。╤ttps://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solutions/622496/jian-zhi-offer-51-shu-zu-zhong-de-ni-xu-pvn2h)

class Solution {
public:
    int merge_sort(int left, int right, vector<int>& nums, vector<int>& tmp)
    {
        if(left >= right) return 0;
        int mid = (left + right) / 2;
        int res = merge_sort(left, mid, nums, tmp) + merge_sort(mid+1, right, nums, tmp);
        
        int i = left, j = mid + 1;
        for(int k=left;k<=right;k++)
        {
            tmp[k] = nums[k];
        }
        for(int k=left;k<=right;k++)
        { 
            if(i == mid+1) nums[k] = tmp[j++];
            else if(j == right+1 || tmp[i] <= tmp[j]) nums[k] = tmp[i++];
            else
            {
                nums[k] = tmp[j++];
                res += mid - i + 1;
            }
        }
        return res;
    }

    int reversePairs(vector<int>& nums)
    {
        vector<int> tmp(nums.size());
        return merge_sort(0, nums.size()-1, nums, tmp);
    }
};

9 排序

9.1 【冒泡排序】劍指 Offer 45 - 把數(shù)組排成最小的數(shù)

https://leetcode.cn/problems/ba-shu-zu-pai-cheng-zui-xiao-de-shu-lcof

??這題可以看作是冒泡排序,只不過針對的對象是字符串,我們需要找到里面表示最大的字符串,然后把它放到字符串的最后即可,例如“32”和“3”,因為“323”<“332”,所以“32”<“3”,所以應該把“3”放在“32”后面,借助這種排序思路來解題。

class Solution {
public:
    string minNumber(vector<int>& nums) 
    {
        for(int i=nums.size()-1;i>0;i--)
        {
            for(int j=0;j<i;j++)
            {
                if(string_sort(nums[j],nums[j+1]))
                {
                    swap(nums[j],nums[j+1]);
                }
            }
        }
        string ans = "";
        for(int i=0;i<nums.size();i++)
        {
            ans += to_string(nums[i]);
        }
        return ans;
    }
    
    bool string_sort(int num1, int num2)
    {
        string s1 = to_string(num1) + to_string(num2);
        string s2 = to_string(num2) + to_string(num1);
        if(s1>s2) return true;
        else return false;
    }
};
9.2 【排序】劍指 Offer 61 - 撲克牌中的順子

https://leetcode.cn/problems/bu-ke-pai-zhong-de-shun-zi-lcof

??這道題不難,想清楚順子的判斷條件即可,主要為以下幾個方面:

(1)五個數(shù)中除了0以外不能有其他的重復數(shù)字,否則肯定不是順子;

(2)找出五個數(shù)中的最大值和最小值,看看這兩個數(shù)之間缺的數(shù)的個數(shù)是多少,記為gap,然后計算五個數(shù)中0的個數(shù),記為zero_num,如果gap<=zero_num,說明0的個數(shù)可以補全缺的數(shù),否則就肯定不是順子。

class Solution {
public:
    bool isStraight(vector<int>& nums) {
        int arr[14] = {0};
        int max_num = 0, min_num = 14, zero_num = 0;
        for(int i=0;i<5;i++)
        {
            if(nums[i]==0) zero_num++;
            else
            {
                if(arr[nums[i]]) return false;
                else
                {
                    arr[nums[i]] = 1;
                    min_num = min(min_num, nums[i]);
                    max_num = max(max_num, nums[i]);
                }
            }
        }
        int gap = 0;
        for(int i=min_num;i<max_num;i++)
        {
            if(arr[i]!=1) gap++;
        }
        if(gap <= zero_num) return true;
        else return false;
    }
};
9.3 【堆排序】劍指 Offer 40 - 最小的k個數(shù)

https://leetcode.cn/problems/zui-xiao-de-kge-shu-lcof

??方法一:直接用sort排序,然后選前k個數(shù)就好了。

??方法二:用堆排序,堆排序,采用大根堆,首先把前k個數(shù)字存入大根堆中,之后的數(shù)字依次和大根堆的top比較,如果比top小就更新大根堆,最后把大根堆中的數(shù)字存入vector中作為答案返回。

//方法一:直接排序
class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        sort(arr.begin(),arr.end());
        vector<int> ans;
        for(int i=0;i<k;i++)
        {
            ans.push_back(arr[i]);
        }
        return ans;
    }
};
//方法二:堆排序
class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        vector<int> ans;
        if(k == 0) return ans;

        priority_queue<int, vector<int>, less<int>> max_stack;
        //把前k個數(shù)字存入大根堆
        for(int i=0;i<k;i++)
        {
            max_stack.push(arr[i]);
        }
        //依次和大根堆的top比較,如果比top小就更新大根堆
        for(int j=k;j<arr.size();j++)
        {
            if(arr[j] < max_stack.top())
            {
                max_stack.pop();
                max_stack.push(arr[j]);
            }
        }
        //把大根堆中的數(shù)字存入vector中作為答案返回
        while(k--)
        {
            ans.push_back(max_stack.top());
            max_stack.pop();
        }
        return ans;
    }
};
9.4 【堆排序】【優(yōu)先隊列】劍指 Offer 41 - 數(shù)據(jù)流中的中位數(shù)

https://leetcode.cn/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof

??這道題如果直接對所有數(shù)進行排序的話,最后會runtime,其實只要能找出每一次執(zhí)行findMedian函數(shù)時整個數(shù)據(jù)流中間的兩個數(shù)或者一個數(shù)就行了,這是我們可以考慮用堆排序,同時維持一個大根堆和一個小根堆,把數(shù)據(jù)流中的數(shù)分為兩部分,同時也要保證大根堆中的top要比小根堆中的top小,這樣最后的中位數(shù)要么是小根堆top,要么就是大根堆和小根堆的top取平均,構造方法如下:

  • 如果大根堆和小根堆的size一樣,那么此時add的num就加入大根堆,然后把大根堆的top插入到小根堆中,保證大根堆的size<=小根堆的size;
  • 如果大根堆和小根堆的size不一樣,那么此時add的num就加入小根堆,然后把小根堆的top插入到大根堆中,保證小根堆的size<=大根堆的size;
  • 執(zhí)行findMedian函數(shù)時,看看此時大根堆和小根堆的size是否相等,如果相等的話,中位數(shù)就是各自取top元素相加取平均,如果不相等,那么中位數(shù)就是小根堆的top。
class MedianFinder {
public:
    /** initialize your data structure here. */
    priority_queue<int, vector<int>, greater<int>> min_stack;
    priority_queue<int, vector<int>, less<int>> max_stack;

    MedianFinder() {

    }
    
    void addNum(int num) {
        //如果大根堆和小根堆的size一樣,那么此時add的num就加入大根堆,然后把大根堆的top插入到小根堆中,保證大根堆的size<=小根堆的size
        if(min_stack.size() == max_stack.size())
        {
            max_stack.push(num);
            min_stack.push(max_stack.top());
            max_stack.pop();
        }
        //如果大根堆和小根堆的size不一樣,那么此時add的num就加入小根堆,然后把小根堆的top插入到大根堆中,保證小根堆的size<=大根堆的size
        else
        {
            min_stack.push(num);
            max_stack.push(min_stack.top());
            min_stack.pop();
        }
    }
    //執(zhí)行findMedian函數(shù)時,看看此時大根堆和小根堆的size是否相等,如果相等的話,中位數(shù)就是各自取top元素相加取平均,如果不相等,那么中位數(shù)就是小根堆的top
    double findMedian() {
        if(min_stack.size() == max_stack.size())
        {
            return (max_stack.top() + min_stack.top()) / 2.0;
        }
        else return min_stack.top();
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

10 動態(tài)規(guī)劃

10.1 【動態(tài)規(guī)劃】【哈希表】【DFS】劍指 Offer 10- I - 斐波那契數(shù)列

https://leetcode.cn/problems/fei-bo-na-qi-shu-lie-lcof

??這道題如果直接用動態(tài)規(guī)劃會runtime,主要是因為在計算過程中會有一些數(shù)被反復計算,所以我們在這里采用哈希表來存放已經(jīng)被計算過的數(shù),這樣在之后再次被計算時直接用就好了。

class Solution {
public:
    unordered_map<int,int> hash_table;
    int dfs(int n)
    {
        if(n == 0) return 0;
        else if(n == 1) return 1;
        else
        {
            if(hash_table.count(n)) return hash_table[n];
            else
            {
                int num1 = dfs(n-1) % 1000000007;
                int num2 = dfs(n-2) % 1000000007;
                hash_table[n] = (num1 + num2) % 1000000007;
                return hash_table[n];
            }
        }
    }
    int fib(int n) 
    {
        return dfs(n);
    }
};
10.2 【動態(tài)規(guī)劃】【哈希表】【DFS】劍指 Offer 10- II - 青蛙跳臺階問題

https://leetcode.cn/problems/qing-wa-tiao-tai-jie-wen-ti-lcof

??這道題目就是變形的斐波那契數(shù)列,在這里采用哈希表來存放已經(jīng)被計算過的數(shù),這樣在之后再次被計算時直接用就好了。

class Solution {
public:
    unordered_map<int,int> hash_table;
    int dfs(int n)
    {
        if(n == 0) return 1;
        else if(n == 1) return 1;
        else
        {
            if(hash_table.count(n)) return hash_table[n];
            else
            {
                int num1 = dfs(n-1) % 1000000007;
                int num2 = dfs(n-2) % 1000000007;
                hash_table[n] = (num1 + num2) % 1000000007;
                return hash_table[n];
            }
        }
    }
    int numWays(int n) {
        return dfs(n);
    }
};
10.3 【動態(tài)規(guī)劃】劍指 Offer 63 - 股票的最大利潤

https://leetcode.cn/problems/gu-piao-de-zui-da-li-run-lcof

??動態(tài)規(guī)劃類題目的解題主要是找到狀態(tài)轉移方程就好了,對于這道題目的狀態(tài)轉移就在于某一天有無股票,我們以此為分界來定義狀態(tài)轉移方程dp[i][2]:

(1)前i天未持有股票

d p [ i ] [ 0 ] = m a x ( d p [ i ? 1 ] [ 0 ] , d p [ i ? 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i]) dp[i][0]=max(dp[i?1][0],dp[i?1][1]+prices[i])

(2)前i天持有股票

d p [ i ] [ 1 ] = m a x ( d p [ i ? 1 ] [ 1 ] , 0 ? p r i c e s [ i ] ) dp[i][1] = max(dp[i-1][1], 0 - prices[i]) dp[i][1]=max(dp[i?1][1],0?prices[i])

??同時還要預判一下prices為空的情況,此時返回0,因為dp的兩個元素在反復調用,所以在代碼中也是直接用兩個變量來進行代替了。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(!prices.size()) return 0;
        int dp_0 = 0, dp_1 = -prices[0];
        for(int i=1;i<prices.size();i++)
        {
            dp_0 = max(dp_0, dp_1 + prices[i]);
            dp_1 = max(dp_1, 0 - prices[i]);
        }
        return dp_0;
    }
};
10.4 【動態(tài)規(guī)劃】【分治】劍指 Offer 42 - 連續(xù)子數(shù)組的最大和

https://leetcode.cn/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof

??這道題用到一點點分治的思想,假設現(xiàn)在數(shù)組長度為 n n n,連續(xù)子數(shù)組的最大和為 f ( n ) f(n) f(n),那么 f ( n ) = m a x ( f ( n ? 1 ) + n u m s [ i ] , n u m [ i ] ) f(n)=max(f(n-1)+nums[i],num[i]) f(n)=max(f(n?1)+nums[i],num[i]),也就意味著最大和要么是前 n ? 1 n-1 n?1個數(shù)的最大和加上第 i i i個數(shù),要么就是第 i i i個數(shù)本身,如果是第 i i i個數(shù)本身的話,就要從這里開始重新找到連續(xù)子數(shù)組了,在這個過程中記錄下最大值即可。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int pre = 0, max_seqsum = nums[0];
        for(int i=0;i<nums.size();i++)
        {
            pre = max(pre + nums[i], nums[i]);
            max_seqsum = max(max_seqsum, pre);
        }
        return max_seqsum;
    }
};
10.5 【動態(tài)規(guī)劃】劍指 Offer 47 - 禮物的最大價值

https://leetcode.cn/problems/li-wu-de-zui-da-jie-zhi-lcof

??其實每個地方的最大值只與兩個狀態(tài)有關,假設目前要求的是 v a l u e [ i ] [ j ] value[i][j] value[i][j]的最大值,那么 v a l u e [ i ] [ j ] = m a x ( v a l u e [ i ] [ j ? 1 ] , v a l u e [ i ? 1 ] [ j ] ) + g r i d [ i ] [ j ] value[i][j] = max(value[i][j-1],value[i-1][j]) + grid[i][j] value[i][j]=max(value[i][j?1],value[i?1][j])+grid[i][j],這里為了方便初始化,把初始數(shù)組的大小設置為 ( m + 1 ) ? ( n + 1 ) (m+1)*(n+1) (m+1)?(n+1)文章來源地址http://www.zghlxwxcb.cn/news/detail-681874.html

class Solution {
public:
    int maxValue(vector<vector<int>>& grid) 
    {
        int m = grid.size(), n = grid[0].size();
        int value[m+1][n+1];
        memset(value,0,sizeof(value));
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                value[i+1][j+1] = max(value[i+1][j],value[i][j+1]) + grid[i][j];
            }
        }
        return value[m][n];
    }
};

到了這里,關于【leetcode刷題之路】劍指Offer(4)——分治+排序算法+動態(tài)規(guī)劃的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包