数据结构——线性表(链表,力扣中等篇,增删查改)
文章目录
- 一、增删查改
- 1.1增(插入节点)
- 1.1.1两数后插入公约数
- 1.1.2循环有序链表的插入
- 1.2删(移除节点)
- 1.2.1删除已知的node节点【交换val值】
- 1.2.2移除数组中已存在的节点【unordered_set】
- 1.2.3删除和为0的节点【前缀和】
- 1.3改(交换节点)
- 1.3.1链表中的下一个最大节点
- 1.3.2删除右侧有一个更大数值的节点
- 1.3.3交换K和倒数第K个节点
- 1.3.4合并0之间的节点
- 1.4其他
- 1.4.1链表组件
- 1.4.2螺旋矩阵
- 1.4.3临界值的距离
一、增删查改
1.1增(插入节点)
序号 | 题目 | 链接 |
---|---|---|
1 | 两数后插入公约数 | https://leetcode.cn/problems/insert-greatest-common-divisors-in-linked-list/description/?envType=problem-list-v2&envId=linked-list |
2 | 循环有序链表的插入 | https://leetcode.cn/problems/4ueAj6/description/?envType=problem-list-v2&envId=linked-list |
1.1.1两数后插入公约数
在两个数A和B的中间插入A和B的公约数【公约数的值可以用gcd直接求】
//求最大公约数gcd
ListNode* insertGreatestCommonDivisors(ListNode* head) {if(head==nullptr || head->next==nullptr)return head;ListNode* p=head;while(p->next!=nullptr){ListNode* node=new ListNode(gcd(p->val,p->next->val));node->next=p->next;p->next=node;p=p->next->next;}return head;
}
1.1.2循环有序链表的插入
给定循环单调非递减列表中的一个点,写一个函数向这个列表中插入一个新元素 insertVal ,使这个列表仍然是循环升序的。
Node* insert(Node* head, int insertVal) {Node* node=new Node(insertVal);//创建新节点if(head==nullptr){node->next=node;return node;}if(head->next==head){head->next=node;node->next=head;return head;}Node* cur=head;Node* next=head->next;while(next != head){if(cur->val <= insertVal && insertVal <= next->val) break;if((cur->val > next->val) && (insertVal >= cur->val || insertVal <= next->val) ) break;cur=cur->next;next=next->next;}node->next=next;cur->next=node;return head;
}
1.2删(移除节点)
序号 | 题目 | 链接 |
---|---|---|
1 | 删除链表中值为val的节点(简单) | https://leetcode.cn/problems/remove-linked-list-elements/description/?envType=problem-list-v2&envId=linked-list |
2 | 删除已知的node节点 | https://leetcode.cn/problems/delete-node-in-a-linked-list/description/?envType=problem-list-v2&envId=linked-list |
3 | 移除链表中数组已存在的元素 | https://leetcode.cn/problems/delete-nodes-from-linked-list-present-in-array/?envType=problem-list-v2&envId=linked-list |
4 | 删除和为0的连续节点 | https://leetcode.cn/problems/remove-zero-sum-consecutive-nodes-from-linked-list/description/?envType=problem-list-v2&envId=linked-list |
1.2.1删除已知的node节点【交换val值】
void deleteNode(ListNode* node) {//将node和后面的节点换个位置,然后删除int val=node->val;//暂存节点信息node->val=node->next->val;node->next->val=val;//删除node->next即可node->next=node->next->next;
}
1.2.2移除数组中已存在的节点【unordered_set】
ListNode* modifiedList(vector<int>& nums, ListNode* head) {ListNode* dummy=new ListNode(-1,head);unordered_set<int> s(nums.begin(),nums.end());ListNode* pre=dummy;ListNode* cur=dummy->next;while(cur!=nullptr){if(s.count(cur->val)){pre->next=cur->next;cur = pre->next;}else{cur=cur->next;pre=pre->next;}}return dummy->next;
}
1.2.3删除和为0的节点【前缀和】
前缀和的原理:
反复删去链表中由 总和值为0
的连续节点
组成的序列,直到不存在这样的序列为止。
ListNode* removeZeroSumSublists(ListNode* head) {ListNode* dummy=new ListNode(0,head);//哈希表存储前缀和,以及最后出现的节点unordered_map<int,ListNode*> mp;int prefixsum=0;ListNode* p=dummy;while(p!=nullptr){prefixsum += p->val;mp[prefixsum]=p;// 相同前缀和会覆盖,保留最后一个p=p->next;}//第二次遍历prefixsum=0;p=dummy;while(p!=nullptr){prefixsum+=p->val;p->next=mp[prefixsum]->next;p=p->next;}return dummy->next;
}
1.3改(交换节点)
序号 | 题目 | 链接 |
---|---|---|
1 | 链表中的下一个最大节点 | https://leetcode.cn/problems/next-greater-node-in-linked-list/description/?envType=problem-list-v2&envId=linked-list |
2 | 删除右侧有一个更大数值的节点 | https://leetcode.cn/problems/remove-nodes-from-linked-list/?envType=problem-list-v2&envId=linked-list |
3 | 交换K和倒数第K个节点 | https://leetcode.cn/problems/swapping-nodes-in-a-linked-list/description/?envType=problem-list-v2&envId=linked-list |
4 | 合并0之间的节点 | https://leetcode.cn/problems/merge-nodes-in-between-zeros/description/?envType=problem-list-v2&envId=linked-list |
1.3.1链表中的下一个最大节点
遍历整个链表,找到第一个比当前值大的元素。没有则为0。
解法1:
- p指向当前节点
- q指向当前节点的下一个节点。q向后移动直到找到第一个比p值大的节点,添加到数组中。
vector<int> nextLargerNodes(ListNode* head) {vector<int> result;ListNode* p=head;while(p!=nullptr){ListNode* q=p->next;int maxval=0;while(q!=nullptr){if(q->val > p->val){maxval=q->val;break;//找到第一个最大的就退出}q=q->next;}result.push_back(maxval);p=p->next;}return result;
}
解法2:单调栈。
右侧
和更大
可以使用单调栈。单调栈的关键是维持栈内元素的单调性。
栈中每个索引对应的 values 值从栈底到栈顶逐渐变小。
vector<int> nextLargerNodes(ListNode* head) {//将链表转换为数组vector<int> values;ListNode* p=head;while(p!=nullptr){values.push_back(p->val);p=p->next;}//初始化结果数组int n=values.size();vector<int> result(n,0);//单调栈stack<int> st;for(int i=0;i<n;i++){//将要出栈的元素全部出完while(!st.empty() && values[i] > values[st.top()]){int idx=st.top();st.pop();result[idx]=values[i];}//栈空 || 当前值<栈顶值——入栈st.push(i);}return result;
}
1.3.2删除右侧有一个更大数值的节点
当前值与后面值比较:若存在一个值 > 当前值,将当前值删掉。
思路与上述的单调栈差不多。重点在于栈内存储的是节点,最后返回时候需要使用头插法进行连接。
ListNode* removeNodes(ListNode* head) {stack<ListNode*> st;ListNode* p=head;while(p!=nullptr){//单调栈while(!st.empty() && p->val > st.top()->val){st.pop();}st.push(p);p=p->next;}//单调栈的元素“底大顶小”,因此实际需要从头向下连接//重构链表ListNode* dummy=new ListNode();ListNode* q=dummy;while(!st.empty()){ListNode* node=st.top();st.pop();// 将当前节点插入到dummy和dummy->next之间node->next = dummy->next;dummy->next = node;}return dummy->next;
}
1.3.3交换K和倒数第K个节点
ListNode* swapNodes(ListNode* head, int k) {ListNode* dummy=new ListNode(0,head);//求链表的长度int length=0;ListNode* cur=head;while(cur!=nullptr){cur=cur->next;length++;}//找到第k个节点p,下标为k-1ListNode* p=dummy;for(int i=0;i<=k-1;i++){p=p->next;}//找到倒数第k个节点q,下标为length-kListNode* q=dummy;for(int i=0;i<=length-k;i++){q=q->next;}//交换swap(p->val,q->val);return head;
}
1.3.4合并0之间的节点
ListNode* mergeNodes(ListNode* head) {//新建一个链表ListNode* dummy=new ListNode(0);ListNode* cur=dummy;int sum=0;ListNode* p=head->next;while(p!=nullptr){if(p->val!=0){sum += p->val;}else{ListNode* newnode=new ListNode(sum);cur->next=newnode;cur=cur->next;sum=0;}p=p->next;}return dummy->next;;
}
1.4其他
序号 | 题目 | 链接 |
---|---|---|
1 | 链表组件 | https://leetcode.cn/problems/linked-list-components/description/?envType=problem-list-v2&envId=linked-list |
2 | 螺旋矩阵 | https://leetcode.cn/problems/spiral-matrix-iv/description/?envType=problem-list-v2&envId=linked-list |
3 | 临界值间的距离 | https://leetcode.cn/problems/find-the-minimum-and-maximum-number-of-nodes-between-critical-points/description/?envType=problem-list-v2&envId=linked-list |
1.4.1链表组件
int numComponents(ListNode* head, vector<int>& nums) {// 将nums中的值存入哈希集合,便于快速查找unordered_set<int> numSet(nums.begin(), nums.end());int count=0;int flag=0;ListNode* p=head;while(p!=nullptr){//当前节点值在nums中if(numSet.count(p->val)){//之前没碰到过:新的组件if(!flag){count++;flag=1;}}//当前节点值不在nums中else{ flag=0;}p=p->next;}return count;
}
1.4.2螺旋矩阵
给你两个整数:m 和 n ,表示矩阵的维数。
另给你一个整数链表的头节点 head 。
请你生成一个大小为 m x n 的螺旋矩阵,矩阵包含链表中的所有整数。链表中的整数从矩阵 左上角 开始、顺时针 按 螺旋 顺序填充。如果还存在剩余的空格,则用 -1 填充。
模拟螺旋填充的过程:
- 螺旋填充的路径是 “右→下→左→上” 的循环,每次完成一圈后缩小边界。
- 从左到右填充上边界,完成后上边界下移
- 从上到下填充右边界,完成后右边界左移
- 从右到左填充下边界(如果还有空间),完成后下边界上移
- 从下到上填充左边界(如果还有空间),完成后左边界右移
- 用四个变量控制当前填充的边界:上边界 (top)、下边界 (bottom)、左边界 (left)、右边界 (right)
- 从链表中依次取元素,按照螺旋路径填充,直到所有元素用完或矩阵填满
vector<vector<int>> spiralMatrix(int m, int n, ListNode* head) {// 初始化m×n矩阵,全部填充为-1vector<vector<int>> matrix(m, vector<int>(n, -1));//定义边界int top=0;int bottom=m-1;int left=0;int right=n-1;//ListNode* current=head;while(current!=nullptr && top <= bottom && left <= right){// 1. 从左到右填充上边界for (int col = left; col <= right && current != nullptr; col++) {matrix[top][col] = current->val;current = current->next;}top++; // 上边界下移,该排已填满// 2. 从上到下填充右边界for (int row = top; row <= bottom && current != nullptr; row++) {matrix[row][right] = current->val;current = current->next;}right--; // 右边界左移,该列已填满// 3. 从右到左填充下边界(需确保还有未填充的行)if (top <= bottom) {for (int col = right; col >= left && current != nullptr; col--) {matrix[bottom][col] = current->val;current = current->next;}bottom--; // 下边界上移,该排已填满}// 4. 从下到上填充左边界(需确保还有未填充的列)if (left <= right) {for (int row = bottom; row >= top && current != nullptr; row--) {matrix[row][left] = current->val;current = current->next;}left++; // 左边界右移,该列已填满}}return matrix;
}
1.4.3临界值的距离
模拟即可:
- 使用三个指针prev、curr和next分别指向当前节点的前一个节点、当前节点和后一个节点。
- 对于每个节点,检查它是否是局部极大值点(大于前后节点)或局部极小值点(小于前后节点)。
- 记录所有临界点的位置索引(从 1 开始计数)。
- 计算出最小距离和最大距离。
vector<int> nodesBetweenCriticalPoints(ListNode* head) {if(head==nullptr || head->next==nullptr) return{-1,-1};vector<int> result;ListNode* pre=head;ListNode* cur=head->next;ListNode* next=cur->next;int pos=1;while(next!=nullptr){int ismax=(cur->val > pre->val) && (cur->val > next->val);int ismin=(cur->val < pre->val) && (cur->val < next->val);if(ismax || ismin) result.push_back(pos);//每一组极值pre=cur;cur=next;next=next->next;pos++;}if(result.size()<2) return {-1,-1};//计算最小距离(相邻临界点之间的最小距离)int mind=INT_MAX;for(int i=1;i<result.size();i++){mind=min(mind,result[i]-result[i-1]);}//最大距离(第一个和最后一个极值的距离)int maxd=result.back()-result.front();return {mind,maxd};
}