腾讯百度阿里华为常见算法面试题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];
}
}
}
};