劍指offer刷題 3

用兩個棧實現隊列 3/4

參考:https://www.cnblogs.com/silentteller/p/11827215.html

思路:

隊列先進先出,棧先進後出

棧1:模擬入隊;棧2:模擬出隊;res:保存出隊結果

實現入隊:將元素壓入棧1

實現出隊:如果棧2非空,將棧2元素彈出,存到res

                  如果棧2空,棧1非空,將棧1彈出,存到棧2,將棧2棧頂元素彈出,存到res

【隊列】【棧】

class Solution
{
public:
    void push(int node) {//有元素入隊 直接壓入stack1
        stack1.push(node);
    }

    int pop() {
        int res;
        if(stack2.empty()){//如果棧2空,則壓入棧1棧頂元素,否則直接輸出棧2棧頂元素
            while(!stack1.empty()){//當棧1非空,將棧1棧頂元素壓入棧2;否則兩個棧都空,那啥也不做,直接輸出空的res
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        res = stack2.top();//將棧2棧頂元素存入res
        stack2.pop();
        return res;//輸出int型res
    }

private:
    stack<int> stack1;//模擬入隊
    stack<int> stack2;//模擬出隊
};

包含 min函數的棧

“每入棧一次,就與輔助棧頂比較大小,如果小就入棧,如果大就入棧當前的輔助棧頂

當出棧時,輔助棧也要出棧

這種做法可以保證輔助棧頂一定都當前棧的最小值

”參考:https://www.nowcoder.com/questionTerminal/4c776177d2c04c2494f2555c9fcc1e49?f=discussion

【輔助棧】

題目要求O(1),必須使用輔助棧min,使得min棧頂元素保存棧s的最小值

【易錯】:我沒想到min棧也要pop

【易錯】:在最小元素彈出後還能得到次小元素, 次小的彈出後, 還要能得到次次小的. 所以不能用成員變量

class Solution {
public:
    //每入棧一次,就與輔助棧頂比較大小,如果小就入棧,如果大就入棧當前的輔助棧頂
    void push(int value) {//入棧
        s.push(value);
        if(s_min.size() == 0)//如果min爲空,將s棧頂存入min
            s_min.push(s.top());
        else{//如果min非空,將s棧頂與min中元素比較
            if(s_min.top() > s.top())
                s_min.push(s.top());
            else
                s_min.push(s_min.top());
        }
    }
   
//當出棧時,輔助棧也要出棧
//這種做法可以保證輔助棧頂一定都當前棧的最小值
    void pop() {
        s.pop();
        s_min.pop();
    }
    int top() {
        return s.top();
    }
    int min() {
        return s_min.top();//返回最小棧的棧頂
    }
private:
    stack<int> s;
    stack<int> s_min;//輔助棧,保存最小元素

};

棧的壓入、彈出序列

參考:https://www.nowcoder.com/questionTerminal/d77d11405cc7470d82554cb392585106?f=discussion

參考:https://www.cnblogs.com/silentteller/p/11918863.html

補充出棧順序:https://blog.csdn.net/qq_26286193/article/details/80216479

思路:

設:入棧序列A,出棧序列B,臨時變量的保存temp

將A出棧結果壓入temp,校驗B和temp是否相同

1 掃描A,出棧結果壓入temp;

2 掃描B,如果temp[i] == B[i], 第i元素相同,向下一位移動

                       -對於temp,彈出棧頂元素

                       -對於B,i+1

                如果temp[i] != B[i],第i個元素不同,跳出循環

3 檢查temp是否爲空,空則說明已經檢查完了,結果爲真

           temp非空,則說明檢查到某一位,對不上,結果爲假

爲了省時(魯棒性),A出棧一個元素進入temp,就校驗一個元素

則需要兩重循環

【入棧出棧】

【我暫時沒理解如果45321的話,壓入4需要彈出,再壓入5這在代碼裏怎麼體現的呢】

我理解了:while循環如果循環條件不成立,s會一直將pushV元素壓進去,知道遇到4的時候,s.top() =popV[4];

class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if(pushV.size() == 0) return false;//如果入棧序列爲空,則輸出false
        
        for(int i = 0,j = 0 ;i < pushV.size();){//當入棧序列掃描了一遍則跳出循環
            stack.push(pushV[i++]);//將入棧序列的出棧保存進輔助堆棧stack;stack保存的是出棧順序
            //校驗stack和出棧序列是否相同
            while(j < popV.size() && stack.top() == popV[j]){//跳出循環:出棧隊列掃描完了,數組最後一個元素不等於出棧序列當前元素
                stack.pop();//堆棧彈出棧頂元素
                j++;//向後掃描
            }      
        }
        return stack.empty();//如果堆棧裏元素都被掃描完了,數組爲空,則是它的出棧序列;否則不是
    }
private:
    stack<int> stack;//輔助堆棧
};
class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        if(pushV.size() == 0) return false;//如果入棧序列爲空,則輸出false
        vector<int> stack;//新建一個數組(矢量),保存臨時變量
        
        for(int i = 0,j = 0 ;i < pushV.size();){//當入棧序列掃描了一遍則跳出循環
            stack.push_back(pushV[i++]);//將入棧序列保存進數組
            while(j < popV.size() && stack.back() == popV[j]){//跳出循環:出棧隊列掃描完了,數組最後一個元素不等於出棧序列當前元素
                stack.pop_back();//數組刪除最後元素
                j++;//向後掃描
            }      
        }
        return stack.empty();//如果數組裏元素都被掃描完了,數組爲空,則是它的出棧序列;否則不是
    }
};

刪除鏈表中的重複結點

leetcode上做過

印象中要注意的有:插入一個假頭指針,因爲第一個元素可能就是重複的

有兩種方法:遞歸, 非遞歸(循環)

遞歸:終止條件是到達鏈表結尾

1 指針*head指向鏈表頭結點,指針*p指向鏈表頭結點的next

2 p存在:比較head->val 與 p->val是否相等,

           2.1 相等,新建臨時變量*temp temp=p;p=p->next;delete p;刪除了這一個重複的元素,且p向下移動,遞歸刪除p

           2.2不相等,遞歸刪除head->next

  p不存在,返回head

3 返回頭結點head

非遞歸:

維護兩個指針,pre負責向後遍歷,cur負責連接不重複的元素(遇到重複的向後走)

1 插入假的頭結點dummy,新建指針pre指向dummy,

2 當pre->next存在,即鏈表非空,新建指針cur指向頭結點,在cur->next存在前提下,比較二者值,

        相同,cur後移;如果第一個元素重複(cur = pre->next),那pre後移;如果不是,那將pre和cur->next連接起來

        不同,cur和pre都後移

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {

        if(!pHead || !pHead->next) return pHead;
        ListNode* dummy = new ListNode(-1);
        dummy->next = pHead;
        ListNode* pre = dummy;
        
        while(pre->next){
            ListNode* cur = pre ->next;
            while(cur->next && cur->next->val == cur->val){
                cur = cur->next;
            }
            if(cur != pre->next)
                pre->next = cur->next;
            else pre = pre->next;
        }
        return dummy->next;
    }
};

鏈表中環的入口 3/5

參考:https://www.cnblogs.com/yorkyang/p/10876604.html

一開始沒理解上面鏈接中的公式5,後來自己畫圖理解了

這題主要在於推算規律,編程不難

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {

        if(pHead == nullptr)
            return nullptr;
        ListNode* fast = pHead, *slow = pHead, *pos = nullptr; 
        //循環鏈表
        while(fast && slow){
            fast = fast->next;
            slow = slow -> next;
        
        if(fast){//fast走兩步,如果鏈表沒有環,一定fast先遇到
            fast = fast ->next;
        }
        else
            return nullptr;//出口A,如果fast遇到null則沒有環,返回nullptr
        if(fast == slow){
            pos = slow;//鏈表相遇,地點pos,說明有環
            break;
        }
    }
    ListNode* start = pHead;//鏈表起點
    if(pos != nullptr){
        while(pos && start){
            if(pos == start)//二者相遇
                return start;//出口B找到環入口
            pos = pos->next;
            start = start-> next;
        }
    }
    else{//這個else部分可以不加
        return nullptr;
    }
    }
};

兩個鏈表的第一個公共結點

參考:https://www.cnblogs.com/lishanlei/p/10707681.html

鏈表公共結點:從某一結點開始,兩個鏈表共享子鏈表
公共結點:從某一結點起,兩個鏈表共享子鏈表

因爲共享尾巴部分,所以想辦法比較尾巴,也就是倒着比較,倒着的話考慮堆棧/數組

思路:

1 創建兩個輔助堆棧A,B

2 分別將鏈表中值壓入堆棧,此時尾巴在棧頂

3 比較棧頂元素,相同則存進臨時變量temp,然後AB都pop出棧頂,再次進入循環,比較下一對元素

                         不同,跳出比較的循環

 【公共結點】【堆棧】

編程不難,主要是能想到堆棧

class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        if(pHead1 == nullptr || pHead2 == nullptr)
            return nullptr;//出口A若其中一個鏈表或兩個鏈表都爲空,則返回nullptr
        while(pHead1 != nullptr){//將鏈表1節點壓入棧a
            a.push(pHead1);
            pHead1 = pHead1->next;
        }
        while(pHead2 != nullptr){//將鏈表2節點壓入棧b
            b.push(pHead2);
            pHead2 = pHead2 ->next;
        }
        ListNode* temp = nullptr;//新建一個ListNode*類型的臨時變量,用來保存最一個相同節點,即公共節點
        while(!a.empty() && ! b.empty()){//循環出口,其中一個/兩個棧都空,則返回temp。此時temp = nullptr;
           
            if(a.top()->val == b.top()->val){
                temp = a.top();//找到相同節點,保存到temp,當遇到下一個相同節點,更新temp
               //
            }
            a.pop();//若有一個節點相同,則兩個棧都彈出,比較下一個,即新的棧頂元素的值
            b.pop();
        }
        return temp;
        
    }
private:
    stack<ListNode*> a;//輔助棧a,保存鏈表1節點
    stack<ListNode*> b;//輔助棧b,保存鏈表2節點
};

複雜鏈表的複製

參考:https://www.cnblogs.com/silentteller/p/11931355.html(哈希表映射)

參考:https://blog.csdn.net/qq_41562704/article/details/89478626(自己寫複製函數)

參考:https://blog.csdn.net/m0_37428263/article/details/99446872

參考:http://blog.sina.com.cn/s/blog_a1ce3d4b0102wjgn.html

思路:

1 複製節點,插在該節點後面

2 複製random指針

3 拆分

解決問題 提交時間 狀態 運行時間 佔用內存 使用語言
複雜鏈表的複製(自己寫複製節點random的函數) 2020-03-06 答案正確 2 ms 488K C++
複雜鏈表的複製(哈希表) 2020-03-06 答案正確 4 ms 504K C++

 由上表知哈希慢,但是查找方便O(1)

要是想不到hashmap,代碼量翻倍

代碼待補充

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章