链表
1.旋转链表
1.1题目描述
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
![](https://img-blog.csdnimg.cn/img_convert/569170038a5274520c4d0f51ff5f8643.png)
1.2题目分析
1、首先遍历整个链表,求出链表的长度n,并找出链表的尾节点tail。
2、由于k可能很大,所以我们令 k = k % n,然后再次从头节点head开始遍历,找到第n - k个节点p,那么1 ~ p是链表的前 n - k个节点,p+1 ~ n是链表的后k个节点。
3、接下来就是依次执行 tail->next = head,head = p->next,p->next = nullptr,将链表的后k个节点和前 n - k个节点拼接到一块,并让head指向新的头节点(p->next),新的尾节点即p节点的next指针指向null。
4、最后返回链表的新的头节点head。
时间复杂度分析: 链表一共被遍历两次,因此总的时间复杂度为O(n),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* rotateRight(ListNode* head, int k) {
if(head == nullptr || k == 0) return head;
ListNode* p = head; //遍历指针
ListNode* tail; //记录末尾结点
int n = 0;
while(p) {
++n;
tail = p;
p = p->next;
}
k = k % n;
p = head;
// while(k >= 0) {
// --k;
// mid = p;
// p = p->next;
// }
for(int i = 0; i < n - k - 1; ++i) {
p = p->next;
}
//head....p; p->next...tail //head应该变为p->next
tail->next = head;
head = p->next;
p->next = nullptr;
return head;
}
};
2.反转链表
2.1题目描述
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
2.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* reverseList(ListNode* head) {
ListNode* prev = nullptr, *next;
while(head){
/*让prev保留反向的链表,然后将原本链表上的结点一个一个摘下后放入反向链表里*/
next = head->next;
head->next = prev;
prev = head;
head = next;
}
return prev;
}
};
3.反转链表 II
3.1题目描述
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
示例 1:
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
3.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* reverseBetween(ListNode* head, int left, int right) {
// if(right == 1) {
// return head;
// }
int cnt = 1;
ListNode* p = head;
ListNode* leftprev = nullptr;
//找到要翻转的最左端结点
while(cnt < left) {
leftprev = p;
p = p->next;
++cnt;
}
ListNode* prev = nullptr;
ListNode* next;
ListNode* tail = p;
//开始翻转并且进行边界判断
while(cnt < right) {
next = p->next;
p->next = prev;
prev = p;
p = next;
cnt++;
}
//组装 -- 分类讨论,判断是否从第一个元素开始翻转,left = 1
next = p->next;
p->next = prev;
if(left == 1) {
head = p;
tail->next = next;
}else{
leftprev->next = p;
tail->next = next;
}
return head;
}
};
虚拟指针dummyNode(优化方法1中的分类讨论)
/**
* 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* reverseBetween(ListNode* head, int left, int right) {
ListNode* dummy = new ListNode(-1); //虚拟结点
dummy->next = head;
ListNode* pre = dummy;
for(int i = 0; i < left - 1; ++i) {
pre = pre->next;
}
ListNode* cur = pre->next, *next;
//反转链表 -- 交换的思想
for(int i = 0; i < right - left; ++i) {
next = cur->next;
cur->next = next->next;
next->next = pre->next;
pre->next = next;
}
return dummy->next;
}
};
4.两两交换链表中的节点
4.1题目描述
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
![](https://img-blog.csdnimg.cn/img_convert/9f8341a8bebc279ca1a3a9e9e6fe9615.png)
4.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* swapPairs(ListNode* head) {
if(head == nullptr || head->next == nullptr) {
return head;
}
ListNode* ans = head->next;
head->next = swapPairs(ans->next);
ans->next = head;
return ans;
}
};
/*
时间复杂度O(N),空间复杂度为O(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* swapPairs(ListNode* head) {
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* q = dummy;
while(q->next && q->next->next) {
ListNode* p1 = q->next;
ListNode* p2 = q->next->next;
q->next = p2;
p1->next = p2->next;
p2->next = p1;
q = p1;
}
return dummy->next;
}
};
/*
时间复杂度O(N),空间复杂度为O(1)
*/
5.删除排序链表中的重复元素
5.1题目描述
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
示例 1:
![](https://img-blog.csdnimg.cn/img_convert/467b4874dabda10e24b83d5c9d9b96e6.jpeg)
输入:head = [1,1,2]
输出:[1,2]
示例 2:
![](https://img-blog.csdnimg.cn/img_convert/ad81c6c862ab66186af266d84470d62d.jpeg)
输入:head = [1,1,2,3,3]
输出:[1,2,3]
5.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* deleteDuplicates(ListNode* head) {
if(head == nullptr) return nullptr;
ListNode* nextNode = head->next, *prev = head;
//ListNode* node;
while(nextNode){
while(nextNode && prev->val == nextNode->val){
//node = nextNode;
nextNode = nextNode->next;
//delete[] node;
}
prev->next = nextNode;
prev = nextNode;
}
return head;
}
};
6删除排序链表中的重复元素 II
6.1题目描述
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
示例 1:
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
示例 2:
输入:head = [1,1,1,2,3]
输出:[2,3]
6.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* deleteDuplicates(ListNode* head) {
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* prev = dummy, *cur = dummy->next;
ListNode* p;
while(cur && cur->next) {
if(cur->val == cur->next->val) {
/*删除重复结点*/
int val = cur->val;
while(cur && cur->val == val) {
p = cur;
cur = cur->next;
delete p;
}
}else{
/*连接不重复的结点*/
prev->next = cur;
cur = cur->next;
prev = prev->next;
}
}
if(cur) {
/*连接最后一个unique结点*/
prev->next = cur;
}else{
/*防止出现类似[1,1,1]这样,使得prev->next指向一块被删除的内存(野指针)*/
prev->next = nullptr;
}
return dummy->next;
}
};
7. 链表的中间结点
7.1题目描述
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
7.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* middleNode(ListNode* head) {
/*寻找链表的中间结点 -- 快慢指针*/
ListNode* fast = head, *slow = head;
while(fast->next && fast->next->next) {
slow = slow->next;
fast = fast->next->next;
}
return fast->next == nullptr ? slow : slow->next;
}
};
8.重排链表
8.1题目描述
给定一个单链表 L 的头节点 head ,单链表 L 表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4]
输出:[1,4,2,3]
示例 2:
输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]
8.2题目分析
线性表.先将所有的结点放入vector中(可以随机访问),然后再重排。
/**
* 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:
void reorderList(ListNode* head) {
if(head == nullptr) {
return;
}
vector<ListNode*> coll;
ListNode* p = head;
while(p) {
coll.push_back(p);
p = p->next;
}
int i = 0, j = coll.size() - 1;
while(i < j) {
coll[i]->next = coll[j];
++i;
if(i == j) {
break;
}
coll[j]->next = coll[i];
--j;
}
coll[i]->next = nullptr;
// int n = coll.size();
// int mid = (n - 1) / 2;
// p = head;
// for(int i = 0; i < mid; ++i) {
// p->next = coll[i];
// p = p->next;
// p->next = coll[n - i - 1];
// p = p->next;
// }
// p = nullptr;
}
};
寻找链表中点+反转链表+合并链表
/**
* 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:
void reorderList(ListNode* head) {
if(head == nullptr) {
return;
}
ListNode* mid = middleNode(head);
ListNode* l1 = head, *l2 = mid->next;
mid->next = nullptr; //让l1与l2分开,分成两条链
l2 = reverseList(l2);
mergeList(l1, l2);
}
ListNode* middleNode(ListNode* head) {
/*寻找链表的中间结点 -- 快慢指针*/
ListNode* fast = head, *slow = head;
while(fast->next && fast->next->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr, *cur = head;
while(cur) {
ListNode* nextPtr = cur->next;
cur->next = prev;
prev = cur;
cur = nextPtr;
}
return prev;
}
void mergeList(ListNode* l1, ListNode* l2) {
ListNode* p1 = l1, *p2 = l2;
while(l1 && l2) {
p1 = p1->next;
p2 = p2->next;
l1->next = l2;
l1 = p1;
l2->next = l1;
l2 = p2;
}
}
};