0504刷题

剑指 Offer 09.用两个栈实现队列

剑指 Offer 09. 用两个栈实现队列

class CQueue {
public:
    CQueue() {
        
    }
    
    void appendTail(int value) {
        stk1.push(value);
    }

    int deleteHead() {
        if(stk2.empty())
        {
            if(stk1.empty()) return -1;
            while(!stk1.empty())
            {
                int value1=stk1.top();
                stk1.pop();
                stk2.push(value1);
            }
        }
       
        int value2=stk2.top();
        stk2.pop();
        return value2;
    }

private:
    stack<int> stk1;
    stack<int> stk2;
};

说明:

deleteHead说明:stk2是用来反向存储stk1中的数据,因此可以保证stk2的top()元素就是Head元素。
所以算法的流程就是:
1.如果stk2为空,则将stk1中的元素pop到stk2中;
2.如果此时stk2仍未空,对应于stk1为空的情况,则返回-13.如果stk2不为空,意味着之前就将stk1中的所有数据push到了stk2中,因此stk2的top()元素就是Head元素。

为了方便理解,可以将deleteHead写成:

删除元素对应方法 deleteHead
如果 stk2 为空,则将 stk1 里的所有元素弹出插入到 stk2 里
如果 stk2 仍为空,则返回 -1,否则从 stk2 弹出一个元素并返回

int deleteHead() {
        /* 只有当输出栈为空的情况下才将输入栈的数据全部导入到输出栈 */
        if(stk2.empty()) {
            while(!stk1.empty()) {
                stk2.push(stk1.top());
                stk1.pop();
            }
        }
        /* 如果栈为空输出-1 */
        if (stk2.empty()) {
            return -1;
        }
        else {
            /* 输出栈获取输出的元素值 */
            int result = stk2.top();
            /* 获取值后将此元素弹出 */
            stk2.pop();
            return result;
        }
}

参考:

官方解法

野生解法

剑指 Offer 30. 包含min函数的栈

剑指 Offer 30. 包含min函数的栈
class MinStack {
public:
    /** initialize your data structure here. */
    MinStack() {
        min_stack.push(INT_MAX);//此处需要push进来最大的INT_MAX
    }
    
    void push(int x) {
        stk.push(x);
        min_stack.push(::min(min_stack.top(),x));
    }
    
    void pop() {
        stk.pop();
        min_stack.pop();
    }
    
    int top() {
        return stk.top(); 
    }
    
    int min() {
        return min_stack.top();
    }
private:
    stack<int> stk;
    stack<int> min_stack;
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(x);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->min();
 */

说明:

定义两个stack,分别为stk和min_stack。

stk用来存储所有的元素。

min_stack只用来存储最小的元素,将新的元素x和min_stack的top()元素进行比较,如果x更小,才会push进min_stack。因此需要定义INT_MAX进行比较,如下:

MinStack() 
{
	min_stack.push(INT_MAX);//此处需要push进来最大的INT_MAX
}

缺少本行会报错:

Line 175: Char 16: runtime error: reference binding to misaligned address 0xbebebebebebec0ba for type 'int', which requires 4 byte alignment (stl_deque.h)
0xbebebebebebec0ba: note: pointer points here
<memory cannot be printed>
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/stl_deque.h:180:16

剑指 Offer 35. 复杂链表的复制

剑指 Offer 35. 复杂链表的复制

方法1:哈希表

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(head==nullptr) return head;
        unordered_map<Node*,Node*> umap;
        Node* cur=head;

        //第一次遍历,初始化哈希表
        //复制各节点,并建立 “原节点 -> 新节点” 的 unordered_map 映射
        while(cur!=nullptr)
        {
            umap[cur]=new Node(cur->val);
            cur=cur->next;
        }

        //第二次遍历,定义random和next。
        cur=head;
        while(cur!=nullptr)
        {
            umap[cur]->next=umap[cur->next];
            umap[cur]->random=umap[cur->random];
            cur=cur->next;
        }

        return umap[head];
    }
};

利用哈希表的查询特点,考虑构建 原链表节点新链表对应节点 的键值对映射关系,再遍历构建新链表各节点的 next 和 random 引用指向即可。

算法流程:

  1. 若头节点 head 为空节点,直接返回 null ;
  2. 初始化: 哈希表 umap, 节点 cur 指向头节点;
  3. 复制链表:
    1. 建立新节点,并向 umap添加键值对 (原 cur 节点, 新 cur 节点) ;
    2. cur 遍历至原链表下一节点;
  4. 构建新链表的引用指向:
    1. 构建新节点的 next 和 random 引用指向;
    2. cur 遍历至原链表下一节点;
  5. 返回值: 新链表的头节点 umap[cur] ;

复杂度分析:

时间复杂度 O(N) : 两轮遍历链表,使用 O(N)时间。
空间复杂度 O(N) : 哈希表 umap使用线性大小的额外空间。

方法2:官方解答(哈希表+回溯)

class Solution {
public:
    unordered_map<Node*, Node*> umap;

    Node* copyRandomList(Node* head) {
        if (head == nullptr) {
            return nullptr;
        }
        if (!umap.count(head)) {
            Node* headNew = new Node(head->val);
            umap[head] = headNew;
            headNew->next = copyRandomList(head->next);
            headNew->random = copyRandomList(head->random);
        }
        return umap[head];
    }
};

本题要求我们对一个特殊的链表进行深拷贝。如果是普通链表,我们可以直接按照遍历的顺序创建链表节点。而本题中因为随机指针的存在,当我们拷贝节点时,「当前节点的随机指针指向的节点」可能还没创建,因此我们需要变换思路。一个可行方案是,我们利用回溯的方式,让每个节点的拷贝操作相互独立。对于当前节点,我们首先要进行拷贝,然后我们进行「当前节点的后继节点」和「当前节点的随机指针指向的节点」拷贝,拷贝完成后将创建的新节点的指针返回,即可完成当前节点的两指针的赋值。

具体地,我们用哈希表记录每一个节点对应新节点的创建情况。遍历该链表的过程中,我们检查「当前节点的后继节点」和「当前节点的随机指针指向的节点」的创建情况。如果这两个节点中的任何一个节点的新节点没有被创建,我们都立刻递归地进行创建。 当我们拷贝完成,回溯到当前层时,我们即可完成当前节点的指针赋值。注意一个节点可能被多个其他节点指向,因此我们可能递归地多次尝试拷贝某个节点,为了防止重复拷贝,我们需要首先检查当前节点是否被拷贝过,如果已经拷贝过,我们可以直接从哈希表中取出拷贝后的节点的指针并返回即可。

在实际代码中,我们需要特别判断给定节点为空节点的情况。

复杂度分析

时间复杂度:O(n),其中 n 是链表的长度。对于每个节点,我们至多访问其「后继节点」和「随机指针指向的节点」各一次,均摊每个点至多被访问两次。

空间复杂度:O(n),其中 n 是链表的长度。为哈希表的空间开销。

解法3:暴力解法

// my own answer
class Solution {
public:
    //tool函数,找到cur的random节点在head为头结点的链表中的位置。
    //newHead移动一样的count即可。返回移动之后的newHead。
    Node* tool(Node* head,Node* newHead,Node* cur)
    {
        if(cur->random==nullptr) return nullptr;
        
        Node* goal=cur->random;
        int count=0;
        while(head!=goal)
        {
            ++count;
            head=head->next;
        }
        while(count--) newHead=newHead->next;
        return newHead;
    }

    Node* copyRandomList(Node* head) {
        if(head==nullptr) return head;
        Node* newHead=new Node(head->val);
        Node* tmp=newHead;//保存newHead
        Node* cur=head;//保存head
        //遍历链表,使得newHead链表的next正确。复制next节点。
        while(head!=NULL&&head->next!=NULL)
        {
            head=head->next;//此处head会进行改变
            Node* tmp1=new Node(head->val);
            tmp->next=tmp1;
            tmp=tmp1;
        }

        //在遍历一遍复制random节点。
        tmp=newHead;
        head=cur;
        while(cur)
        {
            tmp->random=tool(head,newHead,cur);//找到原来的链表中cur的random
            cur=cur->next;
            tmp=tmp->next;
        } 

        return newHead;
    }
};
参考:

官方解答

野生解答

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值