class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
ListNode* current = head;
while (current)
{
while (current->next && current->next->val == current->val)
{
ListNode* rmNode = current->next;
current->next = current->next->next;
delete rmNode;
}
current = current->next;
}
return head;
}
};
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if (head == NULL)
return head;
// 链表头也可能删除,所以用dummy node进行辅助
ListNode dummy(0);
dummy.next = head;
head = &dummy;
while (head->next && head->next->next)
{
if (head->next->val == head->next->next->val)
{
int rmVal = head->next->val;
while (head->next && head->next->val == rmVal)
{
ListNode* rmNode = head->next;
head->next = head->next->next;
delete rmNode;
}
}
else
head = head->next;
}
return dummy.next;
}
};
翻转一个单链表
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* reverse = NULL;
ListNode* next;
while (head)
{
next = head->next;
head->next = reverse;
reverse = head;
head = next;
}
return reverse;
}
};
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
class Solution {
public:
/**
* prev cur
* | |
* 1 -> 2 -> 3 -> 4 -> 5 -> NULL
*
* prev
* |--------------------- cur
* | | |
* 1 4 -> 3 -> 2 -> NULL 5
*/
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode dummy(0);
dummy.next = head;
ListNode* prev = &dummy;
for (int i = 1; i < m; ++i)
{
prev = prev->next;
}
ListNode* cur = prev->next;
ListNode* reverse = NULL;
for (int i = m; i <= n; ++i)
{
ListNode* next = cur->next;
cur->next = reverse;
reverse = cur;
cur = next;
}
prev->next->next = cur;
prev->next = reverse;
return dummy.next;
}
};
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode dummy(0);
dummy.next = head;
ListNode* prev = &dummy;
while (isNeedRevese(prev->next, k))
{
ListNode* reverse = NULL;
ListNode* cur = prev->next;
for (int i = 0; i < k; ++i)
{
ListNode* next = cur->next;
cur->next = reverse;
reverse = cur;
cur = next;
}
prev->next->next = cur;
ListNode* tmp = prev;
prev = prev->next;
tmp->next = reverse;
}
return dummy.next;
}
bool isNeedRevese(ListNode* head, int k)
{
ListNode* cur = head;
int i = 0;
for (; cur && i < k; ++i)
cur = cur->next;
return i == k;
}
};
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode dummy(0);
ListNode* p = &dummy;
while (l1 && l2)
{
if (l1->val < l2->val)
{
p->next = l1;
l1 = l1->next;
}
else
{
p->next = l2;
l2 = l2->next;
}
p = p->next;
}
p->next = l1 ? l1 : l2;
return dummy.next;
}
};
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
示例:
输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode smallerDummy(0); // 小于x的dummy
ListNode biggerDummy(0); // 大于x的dummy
ListNode* smaller = &smallerDummy;
ListNode* bigger = &biggerDummy;
while (head)
{
if (head->val < x) {
smaller->next = head;
smaller = smaller->next;
} else {
bigger->next = head;
bigger = bigger->next;
}
head = head->next;
}
bigger->next = NULL;
smaller->next = biggerDummy.next;
return smallerDummy.next;
}
};
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序
思路:归并排序,找中点和合并操作
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (head == NULL || head->next == NULL)
return head;
ListNode* mid = middle(head);
ListNode* tail = sortList(mid->next);
mid->next = NULL;
head = sortList(head);
return mergeList(head, tail);
}
ListNode* middle(ListNode* head)
{
ListNode* slow = head;
ListNode* fast = head->next;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
ListNode* mergeList(ListNode* l1, ListNode* l2)
{
ListNode dummy(0);
ListNode* p = &dummy;
while (l1 && l2)
{
if (l1->val < l2->val) {
p->next = l1;
l1 = l1->next;
} else {
p->next = l2;
l2 = l2->next;
}
p = p->next;
}
p->next = l1 ? l1 : l2;
return dummy.next;
}
};
给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
给定链表 1->2->3->4, 重新排列为 1->4->2->3.
class Solution {
public:
void reorderList(ListNode* head) {
if (head == NULL || head->next == NULL)
return;
ListNode* mid = middle(head);
ListNode* tail = reverseList(mid->next);
mid->next = NULL;
ListNode dummy(0);
ListNode* p = &dummy;
bool isHead = true; //是否取head链表的节点
while (head && tail) {
if (isHead) {
p->next = head;
head = head->next;
} else {
p->next = tail;
tail = tail->next;
}
p = p->next;
isHead = !isHead; // 取非,实现间或取值
}
p->next = head ? head : tail;
head = dummy.next;
}
ListNode* reverseList(ListNode* head)
{
ListNode* reverse = NULL;
while (head) {
ListNode* next = head->next;
head->next = reverse;
reverse = head;
head = next;
}
return reverse;
}
ListNode* middle(ListNode* head)
{
ListNode* slow = head;
ListNode* fast = head->next;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
};
给定一个链表,判断链表中是否有环。
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* slow = head;
ListNode* fast = head;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
return true;
}
return false;
}
};
证明:让两个指针其中一个从链表头 head 出发,一次走一步,让另一个指针从相遇点出发,也一次走一步,相遇点就是环的入口。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* slow = head;
ListNode* fast = head;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
break;
}
// 无环
if (fast == NULL ||fast->next == NULL)
return NULL;
slow = head;
while (slow != fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
};
请判断一个链表是否为回文链表
class Solution {
public:
bool isPalindrome(ListNode* head) {
if (head == NULL || head->next == NULL)
return true;
ListNode* mid = middle(head);
ListNode* tail = reverseList(mid->next);
mid->next = NULL;
while (head && tail)
{
if (head->val != tail->val)
return false;
head = head->next;
tail = tail->next;
}
return true;
}
ListNode* reverseList(ListNode* head)
{
ListNode* reverse = NULL;
while (head) {
ListNode* next = head->next;
head->next = reverse;
reverse = head;
head = next;
}
return reverse;
}
ListNode* middle(ListNode* head)
{
// fast如果初始化为head->next则中点在slow->next
// fast初始化为head,则中点在slow
ListNode* slow = head;
ListNode* fast = head->next;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
};
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的 深拷贝。
我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 val, random_index 表示:
val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
思路:1、hash 表存储指针,2、复制节点跟在原节点后面
class Solution {
public:
Node* copyRandomList(Node* head) {
if (head == NULL)
return head;
// 复制节点,紧挨到到后面
// 1->2->3 ==> 1->1'->2->2'->3->3'
Node* p = head;
while (p)
{
Node* node = new Node(p->val);
node->next = p->next;
p->next = node;
p = node->next;
}
// 处理random指针
p = head;
while (p)
{
if (p->random != NULL)
p->next->random = p->random->next;
p = p->next->next;
}
// 分离两个链表
Node dummy(0);
Node* newList = &dummy;
p = head;
while (p)
{
newList->next = p->next;
p->next = p->next->next;
newList = newList->next;
p = p->next;
}
// 原始链表头:head 1->2->3
// 克隆的链表头:newList 1'->2'->3'
return dummy.next;
}
};
链表必须要掌握的一些点,通过上面的练习题,基本可以应付链表类的各种题目。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。