leetcode之链表2
1.两两交换链表中的节点(中等)
https://leetcode-cn.com/problems/swap-nodes-in-pairs/
输入:head = [1,2,3,4]
输出:[2,1,4,3]
示例 2:
输入:head = []
输出:[]
示例 3:
输入:head = [1]
输出:[1]
1. 递归解法
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head==NULL||head->next==NULL) return head;
ListNode *newHead = head->next;
head->next = swapPairs(newHead->next);
newHead->next = head;
return newHead;
}
};
时间复杂度 o(n),空间复杂度 o(n)[空间复杂度主要取决于递归调用的栈空间]
2. 迭代解法
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
if(head==NULL || head->next==NULL) return head;
ListNode *dummy = new ListNode(0);
dummy->next = head;
ListNode *p = dummy;
while(p->next!=NULL && p->next->next!=NULL){
ListNode *node1 = p->next;
ListNode *node2 = p->next->next;
p->next = node2;
node1->next = node2->next;
node2->next = node1;
p = node1;
}
return dummy->next;
}
};
时间复杂度 o(n) 空间复杂度 o(1)
反转链表 ||
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
普通反转的写法这里就不写了,自己琢磨
迭代
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
ListNode *dummy = new ListNode(0);
dummy->next = head;
ListNode *pre = dummy;
int count=1;
while(count<left){
count++;
pre = pre->next;
}
ListNode *cur = pre->next;
ListNode* post;
for(int i=0;i<right-left;i++){
post = cur->next;
cur->next = post->next;
post->next = pre->next;
pre->next = post;
}
return dummy->next;
}
};
感觉有点像前插法构建链表(记得不是太清了)
时间复杂度 o(n) , 空间复杂度 o(1)
两数相加||
https://leetcode-cn.com/problems/next-greater-node-in-linked-list/
给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
进阶:
如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。
示例:
输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 8 -> 0 -> 7
本题的主要难点在于链表中数位的顺序与我们做加法的顺序是相反的,为了逆序处理所有数位,我们可以使用栈:把所有数字压入栈中,再依次取出相加。计算过程中需要注意进位的情况。
栈
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
stack<int> res1;
stack<int> res2;
while(l1!=NULL){
res1.push(l1->val);
l1 = l1->next;
}
while(l2!=NULL){
res2.push(l2->val);
l2 = l2->next;
}
int count = 0;
ListNode *dummy = new ListNode(0);
dummy->next = NULL;
ListNode *res = dummy;
while(!res1.empty() || !res2.empty() || count){
int a = res1.empty()?0:res1.top();
int b = res2.empty()?0:res2.top();
if(!res1.empty()) res1.pop();
if(!res2.empty()) res2.pop();
ListNode *temp = new ListNode((a+b+count)%10);
count = (a+b+count)/10;
temp->next = dummy->next;
dummy->next = temp;
}
return dummy->next;
}
};
- 时间复杂度:O(max(m, n))O(max(m,n)),其中 mm 与 nn 分别为两个链表的长度。我们需要遍历每个链表。
- 空间复杂度:O(m + n)O(m+n),其中 mm 与 nn 分别为两个链表的长度。这是我们把链表内容放入栈中所用的空间。
链表中下一个更大节点
https://leetcode-cn.com/problems/next-greater-node-in-linked-list/
给出一个以头节点 head 作为第一个节点的链表。链表中的节点分别编号为:node_1, node_2, node_3, … 。
每个节点都可能有下一个更大值(next larger value):对于 node_i,如果其 next_larger(node_i) 是 node_j.val,那么就有 j > i 且 node_j.val > node_i.val,而 j 是可能的选项中最小的那个。如果不存在这样的 j,那么下一个更大值为 0 。
返回整数答案数组 answer,其中 answer[i] = next_larger(node_{i+1}) 。
注意:在下面的示例中,诸如 [2,1,5] 这样的输入(不是输出)是链表的序列化表示,其头节点的值为 2,第二个节点值为 1,第三个节点值为 5 。
示例 1:
输入:[2,1,5]
输出:[5,5,0]
示例 2:
输入:[2,7,4,3,5]
输出:[7,0,5,5,0]
示例 3:
输入:[1,7,5,1,9,2,5,1]
输出:[7,9,9,9,0,5,0,0]
利用栈
class Solution {
public:
vector<int> nextLargerNodes(ListNode* head) {
stack<int> s;
vector<int> temp;
while(head!=NULL){
temp.push_back(head->val);
head = head->next;
}
vector<int> res(temp.size(),0);
for(int i=0;i<temp.size();i++){
while(!s.empty() && temp[s.top()]<temp[i]){
res[s.top()] = temp[i];
s.pop();
}
s.push(i);
}
return res;
}
};
时间复杂度 o(n) 空间复杂度 o(n)
从链表中删去总和值为零的连续节点
https://leetcode-cn.com/problems/remove-zero-sum-consecutive-nodes-from-linked-list/
给你一个链表的头节点 head,请你编写代码,反复删去链表中由 总和 值为 0 的连续节点组成的序列,直到不存在这样的序列为止。
删除完毕后,请你返回最终结果链表的头节点。
你可以返回任何满足题目要求的答案。
(注意,下面示例中的所有序列,都是对 ListNode 对象序列化的表示。)
示例 1:
输入:head = [1,2,-3,3,1]
输出:[3,1]
提示:答案 [1,2,1] 也是正确的.
示例 2:
输入:head = [1,2,3,-3,4]
输出:[1,2,4]
示例 3:
输入:head = [1,2,3,-3,-2]
输出:[1]
- 暴力法
class Solution {
public:
ListNode* removeZeroSumSublists(ListNode* head) {
if(head==NULL) return head;
ListNode *dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode *cur=dummyHead;
while(cur->next!=NULL){
ListNode *temp = cur->next;
int count = 0;
while(temp!=NULL){
count+=temp->val;
if(count==0){
cur->next = temp->next;
break;
}
temp = temp->next;
}
if(temp==NULL){
cur = cur->next;
}
}
return dummyHead->next;
}
};
时间复杂度 o(n^2)
2. 前缀和套路sum[i:j] = presum[j] - presum[i]
class Solution {
public:
ListNode* removeZeroSumSublists(ListNode* head) {
ListNode *dummyHead = new ListNode(0);
dummyHead->next = head;
unordered_map<int,ListNode*> res;
int count = 0;
ListNode *dummy = dummyHead;
while(dummy!=NULL){
count+=dummy->val;
res[count] = dummy;
dummy = dummy->next;
}
int num = 0;
ListNode *p = dummyHead;
while(p!=NULL){
num += p->val;
p->next = res[num]->next;
p = p->next;
}
return dummyHead->next;
}
};
时间复杂度 o(n)
二叉树中的列表
https://leetcode-cn.com/problems/linked-list-in-binary-tree/
给你一棵以 root 为根的二叉树和一个 head 为第一个节点的链表。
如果在二叉树中,存在一条一直向下的路径,且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值,那么请你返回 True ,否则返回 False 。
一直向下的路径的意思是:从树中某个节点开始,一直连续向下的路径。
class Solution {
public:
bool isSubPath(ListNode* head, TreeNode* root) {
if(root==NULL) return false;
if(head==NULL) return true;
return dfs(head,root) || isSubPath(head,root->left) || isSubPath(head,root->right);
}
bool dfs(ListNode* head,TreeNode* root){
if(head==NULL) return true;
if(root==NULL) return false;
if(head->val!=root->val) return false;
else{
return dfs(head->next,root->left) || dfs(head->next,root->right);
}
}
};
复杂度分析
- 时间复杂度:最坏情况下需要对所有节点进行匹配。假设一共有 n个节点,对于一个节点为根的子树,如果它是满二叉树,且每次匹配均为到链表的最后一个节点的时候匹配失败,那么一共被匹配到的节点数为 2^{len+1} -1,即这个节点为根的子树往下 len层的满二叉树的节点数,其中 len为链表的长度,而二叉树总节点数最多 n个,所以枚举节点最多匹配 min(2^{len+1},n),最坏情况下需要 O(n* min(2^{len+1},n)) 的时间复杂度。
- 空间复杂度:由于递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度。考虑枚举一个节点为起点递归判断所需的空间,假设该节点在第 x层,即递归枚举时已经用了 O(x) 的空间,这个节点再往下匹配链表长度 yy 层节点时需要使用 O(y)的空间,所以一共需要 O(x+y) 的空间,而 x+y必然不会超过树的高度,所以最后的空间复杂度为树的高度,即 O(height),height为树的高度。
分隔链表
编写程序以 x 为基准分割链表,使得所有小于 x 的节点排在大于或等于 x 的节点之前。如果链表中包含 x,x 只需出现在小于 x 的元素之后(如下所示)。分割元素 x 只需处于“右半部分”即可,其不需要被置于左右两部分之间。
示例:
输入: head = 3->5->8->5->10->2->1, x = 5
输出: 3->1->2->10->5->5->8
1.转换为线性表 (这里不介绍了)
2.双指针
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
if(head==NULL || head->next==NULL) return head;
ListNode *smallHead = new ListNode(0);
ListNode *largeHead = new ListNode(0);
ListNode *small = smallHead,*large = largeHead;
while(head!=NULL){
if(head->val<x){
small->next = head;
small = small->next;
}else{
large->next = head;
large = large->next;
}
head = head->next;
}
large->next = NULL;
small->next = largeHead->next;
return smallHead->next;
}
};
时间复杂度 o(n) 空间复杂度 o(1)
复制带随机指针的链表
字节题,剑指offer,必须会
https://leetcode-cn.com/problems/copy-list-with-random-pointer/
class Solution {
public:
Node* copyRandomList(Node* head) {
if(head==NULL) return head;
Node* pre = head;
while(pre!=NULL){
Node *temp = new Node(pre->val);
temp->next = pre->next;
pre->next = temp;
pre = temp->next;
}
pre = head;
while(pre!=NULL){
pre->next->random = pre->random == NULL?NULL:pre->random->next;
pre = pre->next->next;
}
Node *old_node = head;
Node *new_node = head->next;
Node *res = new_node;
while(old_node!=NULL){
old_node->next = old_node->next->next;
new_node->next = new_node->next==NULL?NULL:new_node->next->next;
old_node = old_node->next;
new_node = new_node->next;
}
return res;
}
};
详情看题解
https://leetcode-cn.com/problems/copy-list-with-random-pointer/solution/