面试150——第四周

目录

最小栈

逆波兰表达式

基本计算器

环形链表

两数相加

随机链表的复制

反转链表二

K个一组翻转链表

删除链表的倒数第N个结点

删除排序链表中的重复元素

旋转链表

分割链表

LRU缓存


最小栈

解法:栈

栈stack的类型为pair,第一个int是当前元素;第二个int是以当前位置为结尾的最小值,这个最小值的获取:push到栈顶之前,去栈顶元素的最小值与当前要插入的新元素去min即可

cpp 复制代码
class MinStack
{
private:
    stack<pair<int, int>> st; // <元素,当前位置为结尾的最小值>
public:
    MinStack() {}
    void push(int val)
    {
        if (st.empty())
            st.push({val, val});
        else
            st.push({val, min(st.top().second, val)});
    }

    void pop()
    {
        st.pop();
    }

    int top()
    {
        return st.top().first;
    }

    int getMin()
    {
        return st.top().second;
    }
};

逆波兰表达式

解法:栈

使用栈收集数字字符,遇到+-*/时就继续计算

cpp 复制代码
class Solution
{
public:
    int evalRPN(vector<string> &tokens)
    {
        stack<string> st;
        for (auto &token : tokens)
        {
            // if(st.size()>=2)
            //{
            if (token == "+")
            {
                int a = stoi(st.top());
                st.pop();
                int b = stoi(st.top());
                st.pop();
                st.push(to_string(a + b));
            }
            else if (token == "-")
            {
                int a = stoi(st.top());
                st.pop();
                int b = stoi(st.top());
                st.pop();
                st.push(to_string(b - a));
            }
            else if (token == "*")
            {
                int a = stoi(st.top());
                st.pop();
                int b = stoi(st.top());
                st.pop();
                st.push(to_string(a * b));
            }
            else if (token == "/")
            {
                int a = stoi(st.top());
                st.pop();
                int b = stoi(st.top());
                st.pop();
                st.push(to_string(b / a));
            }
            else
                st.push(token);
            cout << st.top() << endl;
        }
        return stoi(st.top());
    }
};

但是if else感觉很累赘,可以使用C++11的包装器来处理变得简洁

cpp 复制代码
class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        map<string,function<int(int,int)>> oper =
        { {"+",[](int a,int b) {return a + b;}},
          {"-",[](int a,int b) {return a - b;}},
          {"*",[](int a,int b) {return a * b;}},
          {"/",[](int a,int b) {return a / b;}}
        };
        stack<int> st;
        for(int i=0;i < tokens.size();i++)
        {
            if(tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/")
            {
                string op = tokens[i];
                int b = st.top();st.pop();
                int a = st.top();st.pop();
                int result = oper[op](a,b);
                st.push(result);
            }
            else
            {
                st.push(stoi(tokens[i]));
            }
        }
        return st.top();
    }
};

基本计算器

解法:栈

使用两个栈,一个收集数字num,一个收集符号pos按照以下情况进行处理

  • 遇到' '时,跳过
  • 遇到'('时,pos收集
  • 遇到')'时,拿一个符号和两个数字进行计算(前提栈不为空),把结果放到num中...
  • 遇到数字时,定义变量sum=0,采用 sum*10 + 数字的方式收集,放到num中

但是要处理 **"+1-( -2)"**的特殊情况,所以:

  • 先把所有空格先处理掉
  • 开始枚举前在num中加入0;+或者-前面时'('时num也加入0保证结果正确
cpp 复制代码
class Solution
{
public:
    int calculate(string s)
    {
        string tmp;
        for (auto &ch : s)
        {
            if (ch != ' ')
                tmp += ch;
        }
        s = tmp;
        stack<long long> num;
        stack<char> pos;
        // 特殊处理
        num.push(0);
        int n = s.size();
        for (int i = 0; i < n; i++)
        {
            // 处理特殊例子 "+1-(     -2)" -> "0+1-(0-2)"
            // if(s[i]==' ') continue;
            if (s[i] == '(')
                pos.push(s[i]);
            else if (s[i] == ')')
            {
                cal(num, pos);
                pos.pop();
            }
            else if (s[i] >= '0' && s[i] <= '9')
            {
                long long sum = 0, j = i;
                while (j < n && s[j] >= '0' && s[j] <= '9')
                {
                    sum = sum * 10 + (s[j++] - '0');
                }

                num.push(sum);
                i = j - 1;
            }
            else
            {
                // 特殊处理
                if (i - 1 >= 0 && s[i - 1] == '(')
                    num.push(0);
                cal(num, pos);
                pos.push(s[i]);
            }
        }
        cal(num, pos);
        return num.top();
    }
    void cal(stack<long long> &num, stack<char> &pos)
    {
        while (num.size() >= 2 && !pos.empty() && pos.top() != '(')
        {
            long long two = num.top();
            num.pop();
            long long one = num.top();
            num.pop();
            char p = pos.top();
            pos.pop();
            if (p == '+')
                num.push(one + two);
            else if (p == '-')
                num.push(one - two);
        }
    }
};

如果有 * / 需要考虑优先级呢?

那就使用 map 来储存优先级,进行计算之前先判断栈顶优先级是否大于等于当前s[i]的优先级,是的话才进行计算,否则就把 * 或者 / 加入到pos中,后面再计算

cpp 复制代码
class Solution
{
public:
    // */时添加优先级标记
    map<char, int> PosToPriority = {{'+', 0}, {'-', 0}, {'*', 1}, {'/', 1}};
    int calculate(string s)
    {
        string tmp;
        for (auto &ch : s)
        {
            if (ch != ' ')
                tmp += ch;
        }
        s = tmp;
        stack<long long> num;
        stack<char> pos;
        num.push(0);
        int n = s.size();
        for (int i = 0; i < n; i++)
        {
            // 处理特殊例子 "+1-(     -2)" -> "0+1-(0-2)"
            // if(s[i]==' ') continue;
            if (s[i] == '(')
                pos.push(s[i]);
            else if (s[i] == ')')
            {
                // 这里不用再进行优先级判断了,因为else已经处理了
                while (!pos.empty() && pos.top() != '(')
                    cal(num, pos);
                pos.pop();
            }
            else if (s[i] >= '0' && s[i] <= '9')
            {
                long long sum = 0, j = i;
                while (j < n && s[j] >= '0' && s[j] <= '9')
                {
                    sum = sum * 10 + (s[j++] - '0');
                }
                num.push(sum);
                i = j - 1;
            }
            else
            {
                if (i - 1 >= 0 && s[i - 1] == '(')
                    num.push(0);
                // 优先级判断是否要计算
                while (!pos.empty() && pos.top() != '(' && PosToPriority[pos.top()] >= PosToPriority[s[i]])
                    cal(num, pos);
                pos.push(s[i]);
            }
        }
        while (!pos.empty())
            cal(num, pos);
        return num.top();
    }
    void cal(stack<long long> &num, stack<char> &pos)
    {
        if (num.size() < 2 || pos.top() == '(')
            return;
        long long two = num.top();
        num.pop();
        long long one = num.top();
        num.pop();
        char p = pos.top();
        pos.pop();
        if (p == '+')
            num.push(one + two);
        else if (p == '-')
            num.push(one - two);
        else if (p == '*')
            num.push(one * two);
        else
            num.push(one / two);
    }
};

环形链表

解法:快慢双指针

cpp 复制代码
class Solution
{
public:
    bool hasCycle(ListNode *head)
    {
        ListNode *slow = head, *fast = head;
        while (fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
            if (slow == fast)
                return true;
        }
        return false;
    }
};

两数相加

解法:模拟

模拟数学加法的方法来实现

cpp 复制代码
class Solution
{
public:
    ListNode *addTwoNumbers(ListNode *l1, ListNode *l2)
    {
        ListNode *head = new ListNode(-1);
        ListNode *tail = head;
        int tmp = 0;
        while (l1 || l2 || tmp)
        {
            int sum = tmp;
            if (l1)
            {
                sum += l1->val;
                l1 = l1->next;
            }
            if (l2)
            {
                sum += l2->val;
                l2 = l2->next;
            }
            tail->next = new ListNode(sum % 10);
            tail = tail->next;
            tmp = sum / 10;
        }
        ListNode *ret = head->next;
        delete head;
        return ret;
    }
};

随机链表的复制

解法:模拟

复制(new)相同的节点放在原节点后面

这样复制节点的random就是原节点random的next

再把new的节点提取出来,同时恢复原链表

cpp 复制代码
class Solution
{
public:
    Node *copyRandomList(Node *head)
    {
        if (head == nullptr)
            return head;
        // 每个节点后面复制一份
        Node *tmp = head;
        while (tmp)
        {
            Node *Next = tmp->next;
            tmp->next = new Node(tmp->val);
            tmp->next->next = Next;
            tmp = Next;
        }
        // 处理 random
        tmp = head;
        while (tmp)
        {
            if (tmp->random != nullptr)
                tmp->next->random = tmp->random->next;
            else
                tmp->random = nullptr;
            tmp = tmp->next->next;
        }
        // 新节点取出来,同时保持原链表不变
        Node *prev = head;
        tmp = head->next;
        Node *ret = new Node(-1);
        Node *tail = ret;
        while (prev)
        {
            Node *Next = tmp->next;
            tail->next = tmp;
            prev->next = Next;

            tail = tail->next;
            prev = Next;
            if (prev)
                tmp = prev->next;
        }
        return ret->next;
    }
};

反转链表二

解法:模拟

把要反转的链表头begin和尾end提取出来,同时保存beginPrev和endNext

把要反转的链表进行反转(头插或递归)

进行链接放回

cpp 复制代码
class Solution
{
public:
    ListNode *reverse(ListNode *left, ListNode *right)
    {
        if (left == right)
            return left;
        ListNode *tmp = reverse(left->next, right);
        tmp->next = left;
        left->next = nullptr;
        return left;
    }
    ListNode *reverseBetween(ListNode *head, int left, int right)
    {
        ListNode *begin = head, *end = head;
        ListNode *leftPrev = nullptr, *rightNext = begin->next;
        for (int i = 0; i < left - 1; i++)
        {
            leftPrev = begin;
            begin = begin->next;
        }
        for (int i = 0; i < right - 1; i++)
        {
            end = rightNext;
            rightNext = end->next;
        }
        ListNode *newEnd = reverse(begin, end);
        ListNode *newBegin = end;

        if (leftPrev)
            leftPrev->next = newBegin;
        newEnd->next = rightNext;
        if (leftPrev == nullptr)
            return newBegin;
        else
            return head;
    }
};

K个一组翻转链表

解法:模拟

找出要翻转链表的起始与结束位置

进行链表的反转操作

使用新链表保存反转后的链表

cpp 复制代码
class Solution
{
public:
    ListNode *ret = new ListNode(-1);
    ListNode *retCur = ret;
    ListNode *Reverse(ListNode *begin, ListNode *end)
    {
        if (begin == end)
            return begin;
        ListNode *head = Reverse(begin->next, end);
        begin->next->next = begin;
        begin->next = nullptr;
        return head;
    }
    ListNode *reverseKGroup(ListNode *head, int k)
    {
        while (head)
        {
            ListNode *cur = head;
            int tmp = k - 1;
            while (cur && (tmp--))
                cur = cur->next;
            // cur为空说明链表不够k个,结束循环
            if (cur == nullptr)
                break;
            // 先记录下一个链表的起始位置,不然后面找不到了
            ListNode *nextListBegin = cur->next;
            // 再继续反转
            ListNode *begin = Reverse(head, cur);
            retCur->next = begin;
            // 更新位置
            retCur = head;
            cur = nextListBegin;
            head = cur;
        }
        retCur->next = head;
        ListNode *ans = ret->next;
        delete ret;
        return ans;
    }
};

删除链表的倒数第N个结点

解法:双指针

链表加个哨兵节点(处理 n == 链表总节点个数)

定义两个指针,一个指向哨兵节点left,另一个从哨兵节点出现的第n个节点处right

同时移动,直到righ为尾节点

此时 left->next 就是待删除的节点

cpp 复制代码
class Solution
{
public:
    ListNode *removeNthFromEnd(ListNode *head, int n)
    {
        // n == head.size() 的情况 带头链表可以处理
        ListNode *ret = new ListNode(-1);
        ret->next = head;
        ListNode *left = ret, *right = ret;
        while (n--)
            right = right->next;
        while (right && right->next)
        {
            left = left->next;
            right = right->next;
        }
        ListNode *tmp = left->next;
        left->next = left->next->next;
        delete tmp;

        ListNode *ans = ret->next;
        delete ret;
        return ans;
    }
};

删除排序链表中的重复元素

cpp 复制代码
class Solution
{
public:
    ListNode *deleteDuplicates(ListNode *head)
    {
        ListNode *ret = new ListNode(-101);
        ret->next = head;

        ListNode *prev = ret, *cur = ret;
        while (cur && cur->next)
        {
            // 删除重复元素
            if (cur->val == cur->next->val)
            {
                ListNode *begin = cur, *end = cur;
                while (end->next && end->val == end->next->val)
                    end = end->next;
                // 此时 begin 和 end 是待删除链表的头和尾
                prev->next = end->next;
                while (true)
                {
                    ListNode *next = begin->next;
                    delete begin;
                    begin = next;
                    if (begin == end)
                    {
                        // 删除最后一个节点之前,先更新当前节点的位置
                        cur = end->next;
                        delete begin;
                        break;
                    }
                }
            }
            else
            {
                prev = cur;
                cur = cur->next;
            }
        }
        ListNode *ans = ret->next;
        delete ret;
        return ans;
    }
};

旋转链表

cpp 复制代码
class Solution
{
public:
    ListNode *rotateRight(ListNode *head, int k)
    {
        if (head == nullptr || k == 0)
            return head;
        int cnt = 0;
        ListNode *tail = head;
        while (tail)
        {
            cnt++;
            tail = tail->next;
        }
        k %= cnt;
        if (k == 0)
            return head;
        int tmp = cnt - k;
        tail = head;
        while (--tmp)
            tail = tail->next;
        // 第二段链表的头和尾
        ListNode *head1 = tail->next;
        // 尾节点的下一个置空
        tail->next = nullptr;
        ListNode *tail1 = head1;
        while (tail1->next)
            tail1 = tail1->next;
        tail1->next = head;
        return head1;
    }
};

分割链表

解法:模拟

创建比x小的链表small和big

遍历收集节点到指定的链表中

最后两个链表合并之后进行返回

cpp 复制代码
class Solution
{
public:
    ListNode *partition(ListNode *head, int x)
    {
        ListNode *small = new ListNode(-1);
        ListNode *big = new ListNode(-1);
        ListNode *smallTail = small, *bigTail = big;
        while (head)
        {
            if (head->val < x)
            {
                smallTail->next = head;
                smallTail = head;
            }
            else
            {
                bigTail->next = head;
                bigTail = head;
            }
            head = head->next;
        }
        smallTail->next = big->next;
        bigTail->next = nullptr;
        delete big;
        ListNode *ret = small->next;
        delete small;
        return ret;
    }
};

LRU缓存

解法:链表

使用哈希表和双链表储存信息key和value,哈希表<key,ListNode*>

主要处理put方法:

当储存中有key值时:通过哈希表找到指定节点,并修改当中的val,最后把该节点进行头插

当节点个数为0时:把尾节点拿出来销毁,同时哈希表储存的键值对也删除,cnt++

创建新节点,哈希表和双链表储存信息(头插新链表),cnt++

cpp 复制代码
// LRU 缓存
class LRUCache
{
public:
    struct ListNode
    {
        struct ListNode *next;
        struct ListNode *prev;
        int key;
        int val;
        ListNode(int Key, int Val)
            : key(Key), val(Val)
        {}
    };

    unordered_map<int, ListNode *> hash;
    ListNode *head = new ListNode(-1, -1);
    int cnt = 0;
    LRUCache(int capacity)
    {
        head->next = head;
        head->prev = head;
        cnt = capacity;
    }
    // 取节点
    void DatchNode(ListNode *node)
    {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }
    // 头插
    void PushFromHead(ListNode *node)
    {
        ListNode *next = head->next;
        head->next = node;
        node->prev = head;
        node->next = next;
        next->prev = node;
    }

    int get(int key)
    {
        if (hash.contains(key))
        {
            ListNode *tmp = hash[key];
            DatchNode(tmp);
            PushFromHead(tmp);
            return tmp->val;
        }
        return -1;
    }

    void put(int key, int value)
    {
        if (hash.contains(key))
        {
            ListNode *tmp = hash[key];
            tmp->val = value;
            hash[key] = tmp;
            DatchNode(tmp);
            PushFromHead(tmp);
            return;
        }
        else if (cnt == 0)
        {
            ListNode *tmp = head->prev;
            DatchNode(tmp);
            hash.erase(tmp->key);
            delete tmp;
            cnt++;
        }
        ListNode *newHead = new ListNode(key, value);
        hash[key] = newHead;
        PushFromHead(newHead);
        cnt--;
    }
};
相关推荐
溜达的大象2 小时前
BEV感知算法技术演进之路:从传感器标定到端到端模型应用
算法
难得的我们2 小时前
C++中的状态模式
开发语言·c++·算法
xhbaitxl2 小时前
算法学习day27-贪心算法
学习·算法·贪心算法
啊阿狸不会拉杆2 小时前
《计算机操作系统》第十章 - 多处理机操作系统
c++·算法·计算机组成原理·os·计算机操作系统
KmjJgWeb2 小时前
柑橘木虱目标检测与识别——基于改进YOLOv26算法的高精度检测方法研究
算法·yolo·目标检测
yongui478342 小时前
异构网络垂直切换算法MATLAB仿真实现
网络·算法·matlab
sin_hielo2 小时前
leetcode 1200
数据结构·算法·leetcode
ZPC82102 小时前
通过神经网络识别图像位置
人工智能·算法·机器人
时艰.2 小时前
JVM 垃圾收集器ParNew&CMS与三色标记算法
java·jvm·算法