腾讯百度阿里华为常见算法面试题TOP100(3):链表、栈、特殊技巧

之前总结过字节跳动TOP50算法面试题:

字节跳动常见算法面试题top50整理_沉迷单车的追风少年-CSDN博客_字节算法面试题

链表

160.相交链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        // 思路:先计算两个链表的长度lenA、lenB,让长的链表先走(lenA-lenB)步
        // 然后两个链表在同一起跑线上,同时前进直到重叠
        int lenA = 0;
        int lenB = 0;
        ListNode* lenNodeA = headA;
        ListNode* lenNodeB = headB;
        while (lenNodeA) {
            lenA ++;
            lenNodeA = lenNodeA->next;
        }
        while (lenNodeB) {
            lenB ++;
            lenNodeB = lenNodeB->next;
        }
        // 让lenA保持最大长度
        if (lenA < lenB) {
            swap(headA, headB);
            swap(lenA, lenB);
        } 
        int n = lenA - lenB;
        while(n--) {
            headA = headA->next;
        }
        while (headA && headB) {
            if (headA == headB) {
                return headA;
            }
            headA = headA->next;
            headB = headB->next;
        }
        // 无重叠
        return nullptr;
    }
};

206.反转链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == nullptr || head->next == nullptr) {
            return head;
        }
        ListNode* temp; // 保存cur的下一个节点
        ListNode* pre = nullptr;
        while (head) {
            temp = head->next; // 保存下一个节点
            head->next = pre; // 翻转操作
            // 更新pre head指针
            pre = head;
            head = temp;
        }
        return pre;
    }
};

234.回文链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        vector<int> array;
        while (head) {
            array.push_back(head->val);
            head = head->next;
        }
        int i = 0, j = array.size() - 1;
        while (i <= j) {
            if (array[i] != array[j]) {
                return false;
            }
            i++;
            j--;
        }
        return true;
    }
};

141.环形链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if (head == nullptr || head->next == nullptr) {
            return false;
        }
        // 经典快慢指针
        ListNode* fast = head->next;
        ListNode* slow = head;
        while (fast->next && fast->next->next && slow->next) {
            if (fast == slow) {
                return true;
            }
            slow = slow->next;
            fast = fast->next->next;
        }
        return false;
    }
};

142.环形链表II

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        //肯定没有环的情况
        if (head == nullptr || head->next == nullptr) {
            return nullptr;
        }
        ListNode* slow = head;
        ListNode* fast = head;
        bool flag = false;
        while (fast->next && fast->next->next && slow->next) {
            slow = slow->next;
            fast = fast->next->next;
            if (fast == slow) {
                flag = true;
                break;
            }
        }
        // 没有环的情况
        if (!flag) {
            return nullptr;
        }
        //将慢指针归位到头结点
        slow = head;
        //快慢指针以同样的速度再走一遍
        while (slow != fast) {
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }
};

21.合并两个有序链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* dummy = new ListNode(-1);
        ListNode* cur = dummy;
        while (list1 && list2) {
            if (list1->val > list2->val) {
                cur->next = list2;
                list2 = list2->next;
            } else {
                cur->next = list1;
                list1 = list1->next;
            }
            // 记得移动指针
            cur = cur->next;
        }
        // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        if (list1 == nullptr && list2 != nullptr) {
            cur->next = list2;
        } else if (list1 != nullptr && list2 == nullptr) {
            cur->next = list1;
        }

        return dummy->next;
    }
};

2.两数相加

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        // 计算长度
        int len1 = 0;
        int len2 = 0;
        ListNode* p = l1;
        while (p->next != nullptr) {
            len1 += 1;
            p = p->next;
        }
        ListNode* q = l2;
        while (q->next != nullptr) {
            len2 += 1;
            q = q->next;
        }
        // 将短的链表补0
        if (len1 < len2) {
            for (int i = 0; i < len2- len1; i++) {
                p->next = new ListNode(0);
                p = p->next;
            }
        } else {
            for (int i = 0; i < len1 - len2; i++) {
                q->next = new ListNode(0);
                q = q->next;
            }
        }
        // 归位
        p = l1;
        q = l2;
        // 记录进位
        bool flag = false;
        ListNode* dummy = new ListNode(-1);
        dummy->next = l1;
        int i = 0;
        // l3移位
        ListNode* w = dummy;
        while (p != nullptr || q != nullptr) {
            i = flag + p->val + q->val;
            flag = i >= 10 ? true : false; 
            w->next = new ListNode(i%10);
            w = w->next;
            p = p->next;
            q = q->next;
        }
        // 最后一位是否进位
        if (flag) {
            w->next = new ListNode(1);
            w = w->next;
        }
        return dummy->next;
    }
};

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

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 快慢指针
        ListNode* fast = head;
        ListNode* slow = head;
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        // 快指针先走n步
        while (n--) {
            fast = fast->next;
        }
        // n等于链表长度时特殊处理
        if (fast == nullptr) {
            return head->next;
        }
        // 再一起走到终点
        while (fast != nullptr && fast->next != nullptr) {
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next;
        return dummy->next;
    }
};

25.k个一组翻转链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        int lenght = 0;
        while (head) {
            lenght++;
            head = head->next;
        }
        head = dummy->next;
        ListNode* cur = head, *next, *pre = dummy;
        for (int i = 0; i < lenght / k; i++) {
            // 这一步变成反转链表
            for (int j = 0; j < k - 1; j++) {
                next = cur->next;
                cur->next = next->next;
                next->next = pre->next;
                pre->next = next;
            }
            pre = cur;
            cur = pre->next;
        }
        return dummy->next;
    }
};

138.复制带随机指针的链表

/*
// 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*> hash;
        Node* node = head;
        // 第一步先只复制value,建立映射关系
        while (node) {
            Node* clone = new Node(node->val, nullptr, nullptr);
            hash[node] = clone;
            node = node->next;
        }
        node = head;
        // 根据建立的映射关系复制next和random指针
        while (node) {
            hash[node]->next = hash[node->next];
            hash[node]->random = hash[node->random];
            node = node->next;
        }
        // 返回克隆节点的头节点
        return hash[head];
    }
};

148.排序链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(!head||!head->next)
            return head;
        ListNode* pre = head;
        ListNode* slow,*fast;
        slow = head;
        fast = head;
        while(fast&&fast->next){//快慢指针找到中间点
            pre = slow;
            slow = slow->next;
            fast = fast->next->next;
        }
        pre->next = nullptr;//从中间点断开链表
        return mergeTowList(sortList(head),sortList(slow));
    }
    // 归并排序
    ListNode* mergeTowList(ListNode* l1,ListNode* l2){
        if(!l1)
            return l2;
        if(!l2)
            return l1;
        if(l1->val<l2->val){
            l1->next = mergeTowList(l1->next,l2);
            return l1;
        }else{
            l2->next = mergeTowList(l1,l2->next);
            return l2;
        }
    }
};

23.合并K个升序链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        // 将值放入小顶堆,然后构造链表
        priority_queue<int, vector<int>, greater<int> > p;
        for (auto elem : lists) {
            while (elem != nullptr) {
                p.push(elem->val);
                elem = elem->next;
            }
        }
        ListNode* head = new ListNode(-1);
        ListNode* dummy = head;
        while (!p.empty()) {
            ListNode* temp = new ListNode(p.top());
            p.pop();
            head->next = temp;
            head = head->next;
        }
        return dummy->next;
    }
};

146.LRU缓存

class LRUCache {
private:    
    int maxsize = 0;
    list<pair<int, int>> cache;
    unordered_map<int, list<pair<int, int>>::iterator > map;
public:
    LRUCache(int capacity) {
        this->maxsize = capacity;
    }
    
    int get(int key) {
        if (map.find(key) == map.end()) {   // 未找到
            return -1;
        }
        // 找到了, 把key对应的移动到链表的最前端
        pair<int, int> p = *map[key];
        cache.erase(map[key]);
        cache.push_front(p);
        // map中更新
        map[key] = cache.begin();
        return p.second;
    }
    
    void put(int key, int value) {
        if (map.find(key) == map.end()) {
            if (cache.size() == maxsize) { // 满员后删除链表末尾最少使用的
                map.erase(cache.back().first);
                cache.pop_back();
            }
            cache.push_front(make_pair(key, value));
            map[key] = cache.begin();
        } else {   // 未满员只需把key对应的移动到链表的最前端表示刚使用过
            cache.erase(map[key]);
            cache.push_front(make_pair(key, value));
            map[key] = cache.begin();
        }
    }
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */

20.有效的括号

class Solution {
public:
    bool isValid(string s) {
        stack<char> sta;
        for (auto elem : s) {
            if (elem == '(' || elem == '[' || elem == '{') {
                sta.push(elem);
            } else if (elem == ')' && !sta.empty() && sta.top() == '(') {
                sta.pop();
            } else if (elem == '}' && !sta.empty() && sta.top() == '{') {
                sta.pop();
            } else if (elem == ']' && !sta.empty() && sta.top() == '[') {
                sta.pop();
            } else {
                sta.push(elem);
            }
        }
        return sta.empty();
    }
};

155.最小栈

class MinStack {
private:
    // 用一个最小栈维护最小值
    stack<int> sta;
    stack<int> min_sta;

public:
    MinStack() {
        
    }
    
    void push(int val) {
        sta.push(val);
        if (min_sta.empty() || sta.top() <= min_sta.top()) {
            min_sta.push(sta.top());
        }
    }
    
    void pop() {
        if (!sta.empty()) {
            if (sta.top() == min_sta.top()) {
                min_sta.pop();
            }
            sta.pop();
        }
    }
    
    int top() {
        return sta.top();
    }
    
    int getMin() {
        return min_sta.top();
    }
};

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

394.字符串解码

class Solution {
public:
    string decodeString(string s) {
        string ans = "";
        // 第一个栈用来存放数字
        stack<int> nums;
        // 第二个栈用来存放字符串
        stack<string> strs;
        int num = 0;
        for (int i = 0; i < s.size(); i++) {
            if (s[i] >= '0' && s[i] <= '9') {
                num = num * 10 + s[i] - '0';
            } else if ((s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= 'a' && s[i] <= 'z')) {
                ans += s[i];
            } else if (s[i] == '[') { //将'['前的数字压入nums栈内,字母字符串压入strs栈内
                nums.push(num);
                num = 0;
                strs.push(ans);
                ans = "";
            } else if (s[i] == ']') { // 与'['匹配出栈
                int times = nums.top();
                nums.pop();
                for (int j = 0; j < times; j++) {
                    strs.top() += ans;
                }
                ans = strs.top();
                strs.pop();
            }
        } 
        return ans;
    }
};

739.每日温度

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        /* 经典单调栈
        维护递减栈,后入栈的元素总比栈顶元素小。
        比对当前元素与栈顶元素的大小
        若当前元素 < 栈顶元素:入栈
        若当前元素 > 栈顶元素:弹出栈顶元素,记录两者下标差值即为所求天数
        */
        vector<int> ans(temperatures.size(), 0);
        stack<int> sta;     // 维护一个递增栈
        for (int i = 0; i < temperatures.size(); i++) {
            while (!sta.empty() && temperatures[i] > temperatures[sta.top()]) {
                ans[sta.top()] = i - sta.top();
                sta.pop();
            }
            sta.push(i);
        }
        return ans;
    }
};

84.柱状图中最大的矩形

暴力解法,会超时:

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        // 找左右两边第一个小于当前的柱子作为边界点,会超时,需要用单调栈优化
        int ans = 0;
        for (int i = 0; i < heights.size(); i++) {
            int w = 1;
            int j = i;
            while (--j >= 0 && heights[j] >= heights[i]) {
                w++;
            }
            j = i;
            while (++j < heights.size() && heights[j] >= heights[i]) {
                w++;
            }
            ans = max(ans, w * heights[i]);
        }
        return ans;
    }
};

单调栈解法:

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        // 单调栈, 找每个柱子左右两边第一个小于该柱子的柱子
        int ans = 0;
        // 在柱体数组的头和尾加了两个高度为 0 的柱体
        heights.push_back(0);
        heights.insert(heights.begin(), 0);
        stack<int> sta;
        sta.push(0);
        for (int i = 0; i < heights.size(); i++) {
            // 对栈中柱体来说,栈中的下一个柱体就是其「左边第一个小于自身的柱体」
            // 若当前柱体i的高度小于栈顶柱体的高度,说明i是栈顶柱体的「右边第一个小于栈顶柱体的柱体」
            while (heights[i] < heights[sta.top()]) {
                int h = heights[sta.top()];
                sta.pop();
                int w = i - sta.top() - 1;
                ans = max(ans, w * h);
            }
            sta.push(i);
        }
        return ans;
    }
};

技巧类

136.只出现一次的数字

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        // 位运算 
        // 同样的数字或运算后等于0, 所以最后剩下来的数字一定是只出现过一次的
        int ans = nums[0];
        for (int i = 1; i < nums.size(); i++) {
            ans ^= nums[i];
        }
        return ans;
    }
};

169.多数元素

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        // 摩尔投票法
        // 遍历后面的数如果相同则count+1,不同则减一
        // 当count为0时则更换新的数字为候选数(成立前提:有出现次数大于n/2的数存在)
        int ans = nums[0];
        int count = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (count == 0) {
                ans = nums[i];
            }
            if (ans == nums[i]) {
                count ++;
            } else {
                count --;
            }
        }
        return ans;
    }
};

75.颜色分类

class Solution {
public:
    void sortColors(vector<int>& nums) {
        // 三指针解法
        int cur = 0, left = 0, right = nums.size() - 1;
        while (cur <= right) {
            if (nums[cur] == 0) {
                swap(nums[cur++], nums[left++]);
            } else if (nums[cur] == 2) {
                swap(nums[cur], nums[right--]);
            } else{
                cur ++;
            }
        }
        return;
    }
};

31.下一个排列

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        // 双指针两边遍历法
        // 先找到右边第一个非升序的数
        // 再找右边第一个大于刚才找的非降序的数
        // 最后重排右边找到的那个数到数组末尾区间
        int i = nums.size() - 2, j = nums.size() - 1;
        while (i >=0 && nums[i+1] <= nums[i]) {
            i--;
        }
        if (i >= 0) {
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            swap(nums[i], nums[j]);
        }
        sort(nums.begin()+i+1, nums.end());
        return;
    }
};

287.寻找重复数

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        /**
        快慢指针思想, fast 和 slow 是指针, nums[slow] 表示取指针对应的元素
        注意 nums 数组中的数字都是在 1 到 n 之间的(在数组中进行游走不会越界),
        因为有重复数字的出现, 所以这个游走必然是成环的, 环的入口就是重复的元素, 
        即按照寻找链表环入口的思路来做
        **/
        int fast = 0, slow = 0;
        while (true) {
            fast = nums[nums[fast]];
            slow = nums[slow];
            if (slow == fast) {
                fast = 0;
                while (nums[slow] != nums[fast]) {
                    fast = nums[fast];
                    slow = nums[slow];
                }
                return nums[slow];
            }
        }
    }
};
相关推荐
枫叶丹41 小时前
【HarmonyOS之旅】HarmonyOS开发基础知识(三)
华为od·华为·华为云·harmonyos
SoraLuna6 小时前
「Mac畅玩鸿蒙与硬件47」UI互动应用篇24 - 虚拟音乐控制台
开发语言·macos·ui·华为·harmonyos
AORO_BEIDOU10 小时前
单北斗+鸿蒙系统+国产芯片,遨游防爆手机自主可控“三保险”
华为·智能手机·harmonyos
博览鸿蒙11 小时前
鸿蒙操作系统(HarmonyOS)的应用开发入门
华为·harmonyos
Damon小智18 小时前
HarmonyOS NEXT 技术实践-基于基础视觉服务的多目标识别
华为·harmonyos
匹马夕阳21 小时前
华为笔记本之糟糕的体验
华为·笔记本电脑
egekm_sefg21 小时前
华为、华三交换机纯Web下如何创关键VLANIF、操作STP参数
网络·华为
岳不谢1 天前
华为DHCP高级配置学习笔记
网络·笔记·网络协议·学习·华为
爱笑的眼睛112 天前
uniapp 极速上手鸿蒙开发
华为·uni-app·harmonyos
K.P2 天前
鸿蒙元服务从0到上架【第三篇】(第二招有捷径)
华为·harmonyos·鸿蒙系统