LeetCode 剑指offer刷题记录(Day1 No.01~No.05)

No 01.  用两个栈实现队列

 问题描述

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

 示例:

输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]

 核心思想利用两个栈实现队列,在队列有输出请求时,若栈1非空栈2空,栈1所有元素弹入栈2,再从栈2顶部弹出一个元素,若栈2本身非空则直接从栈2顶部弹出元素

 代码示例

class CQueue {
public:
    stack<int> stack1;
    stack<int> stack2;
    CQueue() {

    }
    
    void appendTail(int value) {
        stack1.push(value);
    }
    
    int deleteHead() {
        int res;
        if(stack1.empty()&&stack2.empty()){
            return -1;
        } 
        else if(!stack1.empty()&&stack2.empty()){
            while(!stack1.empty()){
                int tmp=stack1.top();
                stack1.pop();
                stack2.push(tmp);
            }
            res=stack2.top();
            stack2.pop();
        }
        else if(!stack2.empty()){
                res=stack2.top();
                stack2.pop();
        }
     
        return res;
        
    }
};




/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue* obj = new CQueue();
 * obj->appendTail(value);
 * int param_2 = obj->deleteHead();
 */

No 02.   包含min函数的栈

问题描述

定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

示例

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.min();   --> 返回 -2.

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/bao-han-minhan-shu-de-zhan-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

 核心思想:利用辅助栈实现在0(1)的负载度内得到栈的最小元素

  代码示例:

  初始版本:

class MinStack {
public:
    /* 定义两个栈容器 */
    stack<int> st;
    stack<int> minStack;

    /** 构造函数清空栈容器  */
    MinStack() {
        while(!st.empty()) {
            st.pop();
        }
        while(!minStack.empty()) {
            minStack.pop();
        }
        /* 初始化最小栈的栈顶元素为最大值为了防止top访问空指针报错 */
        minStack.push(INT_MAX);
    }
    
    void push(int x) {
        st.push(x);
        /* 比较最小栈的栈顶的值和当前值val的大小,将最小值押入最小栈也就是记录了当前st栈的最小值为栈顶元素 */
        int minVal = std::min(minStack.top(), x);
        /* 将最小值押入最小栈 */
        minStack.push(minVal);
    }
    
    void pop() {
        /* 弹出两个栈的栈顶元素 */
        st.pop();
        minStack.pop();
    }
    
    int top() {
        return st.top();
    }
    
    int min() {
        /* 取最小栈的栈顶元素就是此时st栈的最小值 */
        return minStack.top();
    }
};

 更改版本: 

class MinStack {
private:
    stack<int> stkIn;
    stack<int> stkMin;
public:
    /** initialize your data structure here. */
    MinStack() {}   
    void push(int x) {
        stkIn.push(x);
        if(stkMin.empty() or stkMin.top() >= x){
            stkMin.push(x);
        }
    }   
    void pop() {
        if(!stkMin.empty() and stkIn.top() == stkMin.top()){
            stkMin.pop();
        }
        if (!stkIn.empty()) {
            stkIn.pop();
        }
    }   
    int top() {
        return stkIn.top();
    }    
    int min() {
        return stkMin.top();
    }
};

 注意事项:一开始只是简单的pop两个栈,但是在编译阶段会报错,在每个pop前面给minStack的push前面加个判断是否空可以防止报错,亦可以通过在构造函数给栈塞个INT_MAX,以此防止top访问空指针报错

No 03.  从尾到头打印链表

问题描述:输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例:

输入:head = [1,3,2]
输出:[2,3,1]

核心思想:可以构建辅助栈,利用栈的特性(先进后出),将表内元素逆置。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
      stack<int>s;
      vector<int>v;
      ListNode* a=head;
      while(a!=NULL){
          s.push(a->val);
          a=a->next;   
      }  

      while(!s.empty()){
        v.push_back(s.top());
        s.pop();
      }  
    return v;
    }
};

注意:类内只能构建变量和函数

No 04.  反转链表

问题描述: 定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例: 

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

原本构想: 本想延续上一道的思想将其存入栈内,再构建一个新链表,将其重新赋值,但是写到最后发现不会用指针指向容器。。。。若以后发现方法再补充

失败的代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
    stack<int>s;
    ListNode *a=head;
    list<int>l;

    while(a!=NULL){
    s.push(a->val);
    a=a->next;
    }

    while(!s.empty()){
    l.push_back(s.top());
    s.pop();
    }

    ListNode* b=&(l.begin());
    return b;

    }

   
};

核心思想(双指针法):

声明两个指针,一个从头开始一个从空指针开始,每次用tmp储存cur原本指向的节点,之后让cur指向pre,之后更新pre和cur指针用tmp给cur指路,cur给pre指路,

 代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
    ListNode* cur=head;
    ListNode* pre=nullptr;
    while(cur!=NULL){
        ListNode* tmp=cur->next;
        cur->next=pre;
        pre=cur;
        cur=tmp;
    }
    return pre;
    } 
};

No  05.   复杂链表的复制

题目描述:

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

示例: 

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

 设计思路:

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

  代码:

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    Node* copyRandomList(Node* head) {
    unordered_map<Node* ,Node*> m;
    Node* cur=head;
    while(cur!=NULL){    //复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
        m[cur]=new Node(cur->val);
        cur=cur->next;
    }
    cur=head;

    while(cur!=NULL){     //将新就链表的指针指向规则给新链表附上
        m[cur]->next=m[cur->next];
        m[cur]->random=m[cur->random];
        cur=cur->next;
    }

    return m[head];
    }
};

备注:1.哈希映射    m[cur]->next=m[cur->next];  代表将原链cur指向节点的下一个节点连上新链next(即完成一组next关系的赋值)

           2. map与unordered_map的区别

map

优点:

 1.有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作;

 2.红黑树,内部实现一个红黑树使得map的很多操作在O(lgn)的时间复杂度下就可以实现,因此效率非常的高。

缺点:空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间

应用场景:对于那些有顺序要求的问题,用map会更高效一些

unordered_map

优点:因为内部实现了哈希表,因此其查找速度非常的快
缺点:哈希表的建立比较耗费时间
应用场景:对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值