一周刷完劍指offer(7)

day7

61.撲克牌中的順子

思路:先將牌排序,然后將牌分為兩部分:0和非0。遍歷非0部分,遇到非順子的情況,消耗掉一個(gè)0,并自加1。最后看看非0部分能否遍歷完畢。

class Solution {
public:
    bool isStraight(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int zero=-1;//指向最后一個(gè)零元素,不存在的話為-1
        int k,n=nums.size();
        for(k=0;nums[k]==0&&k<nums.size();k++){//nums[k]==0一定要寫在前面
        }
        if(k) zero+=k;
        
        int i=0,j=zero+1;
        while(j<nums.size()-1){
            if(nums[j+1]-nums[j]==1){
                j++;
            }
            else{
                if(i<=zero){
                    I++;
                    //nums[j+1]--;不可以這樣寫,這樣會(huì)影響到j(luò)+2 對(duì) j+1的判斷
                    nums[j]++;
                }
                else{
                    break;
                }
            }
        }
        if(j==n-1 || j==n || j==nums.size()-1) return true;
        else return false;
    }
};

課本的思路更加清晰:
首先把數(shù)組排序;其次統(tǒng)計(jì)數(shù)組中0的個(gè)數(shù);最后統(tǒng)計(jì)排序之后的數(shù)組中相鄰數(shù)字之間的空缺總數(shù)。如果空缺的總數(shù)小于或者等于0的個(gè)數(shù),那么這個(gè)數(shù)組就是連續(xù)的;反之,則不連續(xù)。但需要注意:如果數(shù)組中的非0數(shù)字重復(fù)出現(xiàn),則該數(shù)組不是連續(xù)的。也就是說(shuō),如果牌中有對(duì)子,則不可能是順子。

課本說(shuō),sort函數(shù)為o(nlogn),還不夠快。由于撲克牌的值出現(xiàn)在0~13之間,我們可以定義一個(gè)長(zhǎng)度為14的哈希表,這樣在O(n)時(shí)間就能完成排序。通常我們認(rèn)為不同級(jí)別的時(shí)間復(fù)雜度只有當(dāng)n足夠大時(shí)才有意義。由于本題中數(shù)組的長(zhǎng)度是固定的,只有5張牌,那么O(n)和O(nlogn)不會(huì)有多少區(qū)別,我們可以選用簡(jiǎn)潔易懂的方法來(lái)實(shí)現(xiàn)算法。

HASH排序原理:
將Value值作為下標(biāo),存放在一個(gè)conut數(shù)組里面。以count數(shù)組對(duì)應(yīng)下標(biāo)的值為計(jì)重復(fù)次數(shù)。遍歷count數(shù)組。對(duì)有值的進(jìn)行打印下標(biāo)。完成排序。整體的時(shí)間復(fù)雜度取決于數(shù)組最大數(shù)字

測(cè)試了好多次代碼才通過(guò)

class Solution {
public:
    bool isStraight(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int zero=0,blank=0;//0的數(shù)目和空缺數(shù)
        for(int i=0;i<nums.size()-1;i++){
            if(nums[i]==0){
                zero++;
            } 
            else if(nums[i]==nums[i+1]){
                return false;
            }
            else{
                blank+=nums[i+1]-nums[i]-1;//+=!
            }
        }
        if(blank<=zero||blank==0) return true;//不是blank==zero
        else return false;
    }
};

課本的寫法

class Solution {
public:
    bool isStraight(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int zero=0,gap=0;
        for(int i=0;nums[i]==0&&i<nums.size();i++){
            zero++;
        }
        for(int i=zero;i<nums.size()-1;i++){
            if(nums[i]==nums[i+1]){
                return false;
            }
            gap+=nums[i+1]-nums[i]-1;
        }
        if(gap<=zero) return true;//不是blank==zero
        else return false;
    }
};

思路2:關(guān)鍵點(diǎn)在于max-min<5


https://leetcode-cn.com/problems/bu-ke-pai-zhong-de-shun-zi-lcof/solution/mian-shi-ti-61-bu-ke-pai-zhong-de-shun-zi-ji-he-se/

https://leetcode-cn.com/problems/bu-ke-pai-zhong-de-shun-zi-lcof/solution/bu-ke-pai-zhong-de-shun-zi-pai-xu-fang-shi-he-bu-p/

未實(shí)現(xiàn)

62.圓圈中最后剩下的數(shù)字(重點(diǎn))

思路1:環(huán)形鏈表模擬圓圈
題目中有一個(gè)數(shù)字圓圈,很自然的想法就是用個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)模擬這個(gè)圓圈。
在常用的數(shù)據(jù)結(jié)構(gòu)中,很容易的想到環(huán)形鏈表??梢詣?chuàng)建一個(gè)共有n個(gè)結(jié)點(diǎn)的環(huán)形鏈表,然后每次都從這個(gè)鏈表中刪除第m個(gè)結(jié)點(diǎn)。
一開始個(gè)人的思路是用數(shù)組,但數(shù)組不適合頻繁的刪除操作啊。

如果面試官要求不可以使用標(biāo)準(zhǔn)模版庫(kù)里的數(shù)據(jù)容器來(lái)模擬環(huán)形鏈表,那么我們可以自己實(shí)現(xiàn)一個(gè)鏈表。若沒有要求可以使用模版庫(kù)中的std::list(雙向鏈表)來(lái)模擬一個(gè)環(huán)形鏈表。由于list并不是一個(gè)環(huán)形結(jié)構(gòu),因此每當(dāng)?shù)鲯呙璧芥湵砟┪矔r(shí),要記得把迭代器移到鏈表的頭部。這樣就相當(dāng)于按照順序在一個(gè)圓圈里遍歷了。

雖然超時(shí)了,但是這個(gè)思路要會(huì)。

class Solution {
public:
    int lastRemaining(int n, int m) {
        list<int> cir;
        for(int i=0;i<n;i++){
            cir.push_back(i);
        }
        list<int>::iterator cur=cir.begin();
        while(cir.size()>1){
            for(int i=1;i<m;i++){
                cur++;
                if(cur==cir.end()) cur=cir.begin();
            }
            list<int>::iterator next=++cur;
            if(next==cir.end()) next=cir.begin();
            --cur;
            cir.erase(cur);
            cur=next;
        }
        return cir.back();
    }
};

自己寫的代碼,原本想利用求余來(lái)節(jié)約時(shí)間,可是忽略了余數(shù)的結(jié)果范圍是0~cir.size()-1;當(dāng)余數(shù)為0時(shí),應(yīng)該令其等于cir.size();
否則你后續(xù)的while(--temp_m) 根本無(wú)法退出循環(huán),即使修改成while(--temp_m>0)也是不對(duì)的,雖然可以退出循環(huán),但是也是不正確的。

class Solution {
public:
    int lastRemaining(int n, int m) {
        list<int> cir;
        for(int i=0;i<n;i++){
            cir.push_back(i);
        }
        list<int>::iterator cur=cir.begin();
        while(cir.size()>1){
            int temp_m=m%cir.size();
            if(temp_m==0) temp_m=cir.size();//!
            while(--temp_m){//0如果減1 變成-1 負(fù)數(shù)一直剪也不會(huì)變成0呀.。
            //while(--temp_m>0){
                cur++;
                if(cur==cir.end()) cur=cir.begin();
            }
            list<int>::iterator next=++cur;
            if(next==cir.end()) next=cir.begin();
            --cur;
            cir.erase(cur);
            cur=next;
        }
        return cir.back();
    }
};

ps:list的迭代器erase問題
https://www.cnblogs.com/litifeng/p/12960622.html


一開始的思路是數(shù)組,但是寫不出來(lái)代碼,寫得很復(fù)雜,還用什么迭代器,都暈了,看看別人的,好好領(lǐng)悟一下

class Solution{
public:
    //n個(gè)數(shù)字,每次刪除第m個(gè)數(shù)字
    int lastRemaining(int n, int m){
        if (n < 1 || m < 1) return -1;
        //使用vector來(lái)模擬一個(gè)環(huán)形鏈表
        vector<int> num(n);
        for(int i = 0; i < n; i++) num[i] = i;

        int temp = 0;
        while(num.size() > 1){
            int count = temp + m - 1;//從數(shù)組開頭刪除下一個(gè)節(jié)點(diǎn)需要的總步數(shù)
            int size = num.size();//數(shù)組大小
            int mv = count % size;//實(shí)際從數(shù)組開頭需走的步數(shù)
            temp = mv;
            num.erase(num.begin() + mv); //刪除元素
        }
        return num[0];
    }
};

思路2:
課本p302-303
沒有實(shí)現(xiàn) 沒有仔細(xì)去想
下面應(yīng)該都是思路2
https://blog.csdn.net/u011500062/article/details/72855826
https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/huan-ge-jiao-du-ju-li-jie-jue-yue-se-fu-huan-by-as/
https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/
https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-by-lee/
https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/javajie-jue-yue-se-fu-huan-wen-ti-gao-su-ni-wei-sh/
https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/jian-zhi-offer-62-yuan-quan-zhong-zui-ho-dcow/

63.股票的最大利潤(rùn)

思路:
買入價(jià)越低越好,賣出價(jià)越高越好。

自己的思路:遍歷兩次。先從后往前遍歷,建立max_n數(shù)組,表示i之后的最大賣出價(jià)。
第二次從頭遍歷,求每一個(gè)買入價(jià)和賣出價(jià)的差,找出最大值。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()<1) return 0;
        vector<int> max_p(prices.size());
        int max=-1;//價(jià)格不會(huì)為負(fù)數(shù)
        for(int i=prices.size()-1;i>=0;i--){
            if(prices[i]>max){
                max=prices[i];
            }
            max_p[i]=max;
        }
        int ans=0;
        int min=prices[0];
        for(int i=1;i<prices.size();i++){
            if(prices[i]<min) min=prices[i];
            if(max_p[i]-min>ans) ans=max_p[i]-min;
        }
        return ans;
    }
};

優(yōu)化:實(shí)際上并不需要遍歷兩次,也不需建立數(shù)組。
定義函數(shù)diff(i)為當(dāng)賣出價(jià)為數(shù)組中第i個(gè)數(shù)字時(shí)可能獲得的最大利潤(rùn)。
顯然,當(dāng)賣出價(jià)固定時(shí),買入價(jià)越低獲得的利潤(rùn)越大。也就是說(shuō),如果在掃描到數(shù)組中的第i個(gè)數(shù)字時(shí),只要我們能記住之前的i-1個(gè)數(shù)字中的最小值,就能算出在當(dāng)前價(jià)位賣出時(shí)可能得到的最大利潤(rùn)!!!
代碼中變量min保存了數(shù)組前i-1個(gè)數(shù)字的最小值,也就是之前股票的最低價(jià)。只遍歷一次,時(shí)間復(fù)雜度o(n)

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()<=1) return 0;
        int min_price=prices[0],max_profit=0;
        for(int i=1;i<prices.size();i++){
            if(prices[i]<min_price){
                min_price=prices[i];
            }
            int cur_profit=prices[i]-min_price;
            if(cur_profit>max_profit){
                max_profit=cur_profit;
            }
        }
        return max_profit;
    }
};

64.求1+2+...+n(重點(diǎn))

不會(huì)寫
通常求1+2+…+n除了用公式n(n+1)/2之外,無(wú)外乎循環(huán)和遞歸兩種思路。由于已經(jīng)明確限制for和while的使用,循環(huán)已經(jīng)不能再用了。同樣,遞歸函數(shù)也需要用if語(yǔ)句或者條件判斷語(yǔ)句來(lái)判斷是繼續(xù)遞歸下去還是終止遞歸,但現(xiàn)在題目已經(jīng)不允許使用這兩種語(yǔ)句了
因此我們手里能用的工具很少,列舉出來(lái)發(fā)現(xiàn)只有加減法、賦值、位運(yùn)算符以及邏輯運(yùn)算符。

遞歸(沒有想到):f(n)=n+f(n-1)

class Solution {
public:
    int sumNums(int n) {
        if(n==1) return 1;
        return n+sumNums(n-1);
    }
};

但是這道題:不能用for循環(huán);不能用if運(yùn)算.
如何解決:
for用遞歸實(shí)現(xiàn),這很好理解
if用邏輯運(yùn)算符的計(jì)算特性來(lái)解決。即&&的短路特性。

A && function() 如果A是True, 返回的是function 如果A是false,function都不會(huì)被執(zhí)行到就下一句了。
因此我們把遞歸入口放在function處,那么A表達(dá)式就可以起到if的作用,function遞歸起到for的作用
為了讓n能及時(shí)停止(數(shù)量夠的時(shí)候,要輸出false),我們只能把終點(diǎn)設(shè)置成0,因此我們遞歸中要倒數(shù)。

class Solution {
public:
    int sumNums(int n) {
        n && (n += sumNums(n-1));
        return n;
    }
};

時(shí)間復(fù)雜度:O(n)遞歸函數(shù)遞歸n 次,每次遞歸中計(jì)算時(shí)間復(fù)雜度為 O(1),因此總時(shí)間復(fù)雜度為 O(n)。
空間復(fù)雜度:O(n)。遞歸函數(shù)的空間復(fù)雜度取決于遞歸調(diào)用棧的深度,這里遞歸函數(shù)調(diào)用棧深度為 O(n),因此空間復(fù)雜度為 O(n)。

思路2:巧用sizeof做乘法,>>做除法
定義一個(gè)二維數(shù)組再獲取數(shù)組的size,達(dá)到n*(n+1)的目的,然后位運(yùn)算就是n(n+1)/2

class Solution {
public:
    int sumNums(int n) {
        bool arr[n][n+1];
        return sizeof(arr)>>1;
    }
};

思路3 .4.5.6:利用構(gòu)造函數(shù)/虛函數(shù)/函數(shù)指針/模版類型求解
課本p307-310
由于不熟悉這些知識(shí),不太看得明白

67.把字符串轉(zhuǎn)變?yōu)檎麛?shù)

(重點(diǎn)res=res*10+str[i]-'0',不必按照思維從低位算起)

原始思路:常規(guī)的思路,逐步解決,具體如步驟1、2、3。
但是22% 5%..

class Solution {
public:
    int strToInt(string str) {
        if(str.empty()) return 0;//空字符串
        //1 去除開頭的空格
        int i=0;
        while(i<str.size()&&str[i]==' '){
            i++;
        }
        if(i==str.size()) return 0;//全為空格
        if(str[i]!='+' && str[i]!='-' && str[i]<'0' && str[i]>'9'){
            return 0;
        }
        //2 判斷正負(fù)
        bool positive=true;
        if(str[i]=='+') i++;
        else if(str[i]=='-'){
            i++;
            positive=false;
        }
        //3 處理數(shù)字并返回結(jié)果
        if(i==str.size()) return 0;//只有正負(fù)號(hào)
        //去除開頭的無(wú)意義0
        while(i<str.size()&&str[i]=='0'){
            i++;
        }
        if(i==str.size()) return 0;//全是0

        stack<int> num;
        while(i<str.size()&&str[i]>='0'&&str[i]<='9'){
            num.push(str[i]-'0');
            i++;
        }
        long n=0,ten=1;
        while(!num.empty()){
            n+=num.top()*ten;
            num.pop();
            ten*=10;
            //如果不去除開頭的無(wú)意義0,雖然n沒有超過(guò)INT_MAX,不會(huì)break,但是ten*=10這里會(huì)不斷累乘,導(dǎo)致溢出 程序崩潰
            //"  0000000000012345678"
            if(n>INT_MAX) break;
            if(ten>10000000000) break;
            //"20000000000000000000"
            //n一直沒有超過(guò)INT_MAX,不會(huì)break,但是ten*=10這里不斷累乘,導(dǎo)致溢出 程序崩潰。而且開頭沒有無(wú)意義的0
        }
        if(!positive) n=-n;

        if(n>INT_MAX || (!num.empty()&&positive)){
            return INT_MAX;
        }
        else if(n<INT_MIN || (!num.empty()&&!positive)){
            return INT_MIN;
        }
        else{
            return (int)n;
        }
    }
};

自己的代碼因?yàn)闆]有使用正確的思想將string轉(zhuǎn)為int導(dǎo)致復(fù)雜。
改成:res=res*10+str[i]-'0';
幾乎雙百

class Solution {
public:
    int strToInt(string str) {
        if(str.empty()) return 0;//空字符串
        //1 去除開頭的空格
        int i=0;
        while(i<str.size()&&str[i]==' '){
            i++;
        }
        if(i==str.size()) return 0;//全為空格
        if(str[i]!='+' && str[i]!='-' && str[i]<'0' && str[i]>'9'){
            return 0;
        }
        //2 判斷正負(fù)
        bool positive=true;
        if(str[i]=='+') i++;
        else if(str[i]=='-'){
            i++;
            positive=false;
        }
        //3 處理數(shù)字并返回結(jié)果
        if(i==str.size()) return 0;//只有正負(fù)號(hào)
        long res=0;
        while(i<str.size()&&str[i]>='0'&&str[i]<='9'){
            res=res*10+str[i]-'0';
            i++;
            if(res>INT_MAX) break;
        }
        if(!positive) res=-res;
        if(res>INT_MAX){
            return INT_MAX;
        }
        else if(res<INT_MIN){
            return INT_MIN;
        }
        else{
            return (int)res;
        }
    }
};

但是好像不能夠使用long..因?yàn)轭}目說(shuō)環(huán)境是32位的.
下次再看吧.
https://leetcode-cn.com/problems/ba-zi-fu-chuan-zhuan-huan-cheng-zheng-shu-lcof/solution/mian-shi-ti-67-ba-zi-fu-chuan-zhuan-huan-cheng-z-4/

68- I. 二叉搜索樹的最近公共祖先

思路1:注意是BST,所以分別保存兩個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)到數(shù)組,最后從數(shù)組尾部開始遍歷,尋找第一個(gè)相同節(jié)點(diǎn)。

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        vector<TreeNode*> phead,qhead;
        TreeNode* temp=root;
        //保存p的父親節(jié)點(diǎn)
        while(temp!=p){
            if(temp->val>p->val){
                temp=temp->left;
            }
            else if(temp->val<p->val){
                temp=temp->right;
            }
            else{
                temp=p;
            }
            phead.push_back(temp);
        }
        //保存q的父親節(jié)點(diǎn)
        temp=root;
        while(temp!=q){
            if(temp->val>q->val){
                temp=temp->left;
            }
            else if(temp->val<q->val){
                temp=temp->right;
            }
            else{
                temp=q;
            }
            qhead.push_back(temp);
        }
        //從尾部開始找第一個(gè)相同的元素
        for(int i=phead.size()-1;i>=0;i--){
            for(int j=qhead.size()-1;j>=0;j--){
                if(phead[i]==qhead[j]){
                    return phead[i];
                }
            }
        }
        return root;
    }
};

標(biāo)準(zhǔn)思路:其實(shí)并不需要這么復(fù)雜。利用bst的性質(zhì):位于左子樹的節(jié)點(diǎn)都比父節(jié)點(diǎn)小,而位于右子樹的節(jié)點(diǎn)都比父節(jié)點(diǎn)大。我們只需從樹的根結(jié)點(diǎn)開始和輸入的兩個(gè)結(jié)點(diǎn)進(jìn)行比較。如果當(dāng)前結(jié)點(diǎn)的值比兩個(gè)結(jié)點(diǎn)的值都大,那么最低的共同父結(jié)點(diǎn)一定是在當(dāng)前結(jié)點(diǎn)的左子樹中,于是下一步遍歷當(dāng)前結(jié)點(diǎn)的左子結(jié)點(diǎn);如果當(dāng)前結(jié)點(diǎn)的值比兩個(gè)結(jié)點(diǎn)的值都小,那么最低的共同父結(jié)點(diǎn)一定是在當(dāng)前結(jié)點(diǎn)的右子樹中,于是下一步遍歷當(dāng)前結(jié)點(diǎn)的右子結(jié)點(diǎn)。這樣在樹中從上到下找到第一個(gè)在輸入結(jié)點(diǎn)的值之間的結(jié)點(diǎn),就是最低的公共祖先。

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==NULL) return NULL;
        if (root->val > p->val && root->val > q->val)
            return lowestCommonAncestor(root->left, p, q);
        if (root->val < p->val && root->val < q->val)
            return lowestCommonAncestor(root->right, p, q);
        return root;//不滿足兩個(gè)if則找到了,直接返回
    }
};

時(shí)間復(fù)雜度 O(N) : 其中 N 為二叉樹節(jié)點(diǎn)數(shù);每循環(huán)一輪排除一層,二叉搜索樹的層數(shù)最小為 logN (滿二叉樹),最大為 N (退化為鏈表)。
空間復(fù)雜度 O(N) : 最差情況下,即樹退化為鏈表時(shí),遞歸深度達(dá)到樹的層數(shù) N 。

思路2:迭代解法
將上述思想轉(zhuǎn)為迭代
迭代

1.循環(huán)搜索: 當(dāng)節(jié)點(diǎn) root 為空時(shí)跳出;
當(dāng) p,q 都在 root 的 右子樹 中,則遍歷至 root.right ;
否則,當(dāng) p,q 都在 root 的 左子樹 中,則遍歷至 root.left ;
否則,說(shuō)明找到了 最近公共祖先 ,跳出。

  1. 返回值: 最近公共祖先 root 。

復(fù)雜度分析:
時(shí)間復(fù)雜度 O(N) : 其中 N 為二叉樹節(jié)點(diǎn)數(shù);每循環(huán)一輪排除一層,二叉搜索樹的層數(shù)最小為 logN (滿二叉樹),最大為 N (退化為鏈表)。
空間復(fù)雜度 O(1) : 使用常數(shù)大小的額外空間。

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        while(root!=NULL){
            if(root->val < p->val && root->val < q->val) // p,q 都在 root 的右子樹中
                root = root->right; // 遍歷至右子節(jié)點(diǎn)
            else if(root->val > p->val && root->val > q->val) // p,q 都在 root 的左子樹中
                root = root->left; // 遍歷至左子節(jié)點(diǎn)
            else break;
        }
        return root;
    }
};

68 - II. 二叉樹的最近公共祖先

思路:dfs遍歷每一條路徑并臨時(shí)保存,如果發(fā)現(xiàn)該路徑中有所求節(jié)點(diǎn)p or q,就保存下來(lái),找到兩條路徑之后便退出。
48% 10%

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        vector<TreeNode*> path,phead,qhead;
        dfs(root,p,q,path,phead,qhead);
        //從尾部開始找第一個(gè)相同的元素
        for(int i=phead.size()-1;i>=0;i--){
            for(int j=qhead.size()-1;j>=0;j--){
                if(phead[i]==qhead[j]){
                    return phead[i];
                }
            }
        }
        return root;
    }
    void dfs(TreeNode* root, TreeNode* p, TreeNode* q,vector<TreeNode*> &path,vector<TreeNode*> &phead,vector<TreeNode*> &qhead){
        if(root==NULL|| (!phead.empty()&&!qhead.empty()) ){//如果兩條路徑都找到便不需要再遍歷了
            return;
        }
        path.push_back(root);
        if(root->val==q->val){
            qhead=path;
        }
        else if(root->val==p->val){
            phead=path;
        }
        dfs(root->left,p,q,path,phead,qhead);
        dfs(root->right,p,q,path,phead,qhead);
        path.pop_back();
    }
};

其實(shí)獲得兩條路徑之后,并不需要從尾部開始找,從頭也可以,這樣都不用寫兩層循環(huán)了呀,更快。
75% 10%

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        vector<TreeNode*> path,phead,qhead;
        dfs(root,p,q,path,phead,qhead);
        
        //從頭開始找
        TreeNode* res=root;
        for(int i=0,j=0;i<phead.size()&&j<qhead.size();i++,j++){
            if(phead[i]==qhead[j]){
                res=phead[i];
            }
        }
        return res;
    }
    void dfs(TreeNode* root, TreeNode* p, TreeNode* q,vector<TreeNode*> &path,vector<TreeNode*> &phead,vector<TreeNode*> &qhead){
        if(root==NULL|| (!phead.empty()&&!qhead.empty()) ){//如果兩條路徑都找到便不需要再遍歷了
            return;
        }
        path.push_back(root);
        if(root->val==q->val){
            qhead=path;
        }
        else if(root->val==p->val){
            phead=path;
        }
        dfs(root->left,p,q,path,phead,qhead);
        dfs(root->right,p,q,path,phead,qhead);
        path.pop_back();
    }
};

另一種思路,遞歸,不用存路徑。
沒太看明白。。
https://leetcode-cn.com/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/solution/mian-shi-ti-68-ii-er-cha-shu-de-zui-jin-gong-gon-7/
這個(gè)也是這種做法

//如果是一般的二叉樹
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == NULL)
            return NULL;
        //如果根節(jié)點(diǎn)就是p或者q,返回根節(jié)點(diǎn)
        if(root == p || root == q)
            return root;
        //分別去左右子樹里面找
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);

        if(left && right)//p,q各在一邊,說(shuō)明當(dāng)前的根就是最近共同祖先 
            return root;
        else if(left)//說(shuō)明p,q都在左子樹
            return left;
        else if(right)//說(shuō)明p,q都在右子樹
            return right;
        else
            return NULL;
        
    }
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 建模的第一步是選擇合理的數(shù)據(jù)結(jié)構(gòu)來(lái)表達(dá)問題。實(shí)際生活中的問題千變?nèi)f化,但是數(shù)據(jù)結(jié)構(gòu)就只有固定的幾種。選擇合理的數(shù)據(jù)...
    小逗比兒閱讀 734評(píng)論 0 0
  • leetcode 鏈接[https://leetcode-cn.com/problemset/lcof/] 3、數(shù)...
    漫徹思特閱讀 368評(píng)論 0 0
  • 劍指 Offer 03. 數(shù)組中重復(fù)的數(shù)字[https://leetcode-cn.com/problems/sh...
    君子何為閱讀 709評(píng)論 0 0
  • to-do:看一下別人寫的題解 https://github.com/981377660LMT/algorithm...
    winter_sweetie閱讀 895評(píng)論 1 0
  • fan qie qi shi chuan zhe wa zi shui jido di jiu zhang 第九...
    鐘小颯閱讀 703評(píng)論 0 0

友情鏈接更多精彩內(nèi)容