剑指offer刷题 3

目录

用两个栈实现队列 3/4

包含 min函数的栈

栈的压入、弹出序列

删除链表中的重复结点

链表中环的入口 3/5

两个链表的第一个公共结点

复杂链表的复制


用两个栈实现队列 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 ms488KC++
复杂链表的复制(哈希表)2020-03-06答案正确4 ms504KC++

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

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

代码待补充

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值