234. 回文链表
题目描述
给你一个单链表的头节点 head
,请你判断该链表是否为回文链表。如果是,返回 true
;否则,返回 false
。
运行代码
class Solution {
ListNode* frontPointer;
public:
bool recursivelyCheck(ListNode* currentNode) {
if (currentNode != nullptr) {
if (!recursivelyCheck(currentNode->next)) {
return false;
}
if (currentNode->val != frontPointer->val) {
return false;
}
frontPointer = frontPointer->next;
}
return true;
}
bool isPalindrome(ListNode* head) {
frontPointer = head;
return recursivelyCheck(head);
}
};
代码思路
-
recursivelyCheck
函数- 这个函数是递归函数,用于逐个检查链表节点的值是否构成回文。
- 如果当前节点
currentNode
不为空:- 首先递归调用自身,传入当前节点的下一个节点
currentNode->next
。如果递归返回false
,说明后续的节点不满足回文条件,直接返回false
。 - 接着,比较当前节点的值
currentNode->val
和全局变量frontPointer
指向节点的值。如果不相等,说明不满足回文条件,返回false
。 - 最后,将
frontPointer
向后移动一位,即frontPointer = frontPointer->next
。
- 首先递归调用自身,传入当前节点的下一个节点
- 如果当前节点为空,说明已经遍历完链表的一半(对于奇数长度链表,中间节点不参与比较),返回
true
。
-
isPalindrome
函数- 这个函数是对外提供的接口函数。
- 它首先将全局变量
frontPointer
初始化为传入的链表头节点head
。 - 然后调用
recursivelyCheck
函数,传入链表头节点head
开始递归检查。
该算法的核心思想是通过递归的方式,从链表的末尾开始向前比较节点的值。在递归的过程中,一边向后遍历链表,一边与从链表头部开始的节点值进行比较。如果在任何一个位置发现不相等,就说明链表不是回文链表。当递归结束时,如果没有发现不相等的情况,就说明链表是回文链表。
143. 重排链表
题目描述
给定一个单链表 L
的头节点 head
,单链表 L
表示为:L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:L0 → Ln → L1 → Ln - 1 → L2 → Ln - 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* reverse(ListNode* head) {
if (head == nullptr || head->next == nullptr)
return head;
ListNode* newhead = reverse(head->next);
head->next->next = head;
head->next = nullptr;
return newhead;
}
void reorderList(ListNode* head) {
ListNode *slow = head, *fast = head;
while (fast->next != nullptr && fast->next->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}
slow = reverse(slow->next);
fast = head;
while (fast != nullptr && slow != nullptr) {
ListNode* temp1 = fast->next;
ListNode* temp2 = slow->next;
fast->next = slow;
slow->next = temp1;
slow = temp2;
fast = fast->next->next;
}
if (fast != nullptr)
fast->next = nullptr;
}
};
代码思路
一、整体方法概述
这段代码的目的是对给定的单链表进行重排,使其满足特定的顺序要求。主要通过找到链表的中间节点,将后半部分链表反转,然后交替合并前半部分和反转后的后半部分来实现重排。
二、函数分析
-
reverse
函数- 这个函数用于反转单链表。
- 首先判断链表为空或者只有一个节点的情况,直接返回该链表。
- 然后递归调用自身,传入当前节点的下一个节点,获取反转后的链表头节点
newhead
。 - 接着,将当前节点的下一个节点的
next
指针指向当前节点,实现反转。 - 最后,将当前节点的
next
指针置为nullptr
,返回反转后的链表头节点。
-
reorderList
函数- 首先,使用快慢指针法找到链表的中间节点。慢指针
slow
每次移动一步,快指针fast
每次移动两步。当快指针到达链表末尾或者下一个节点为nullptr
时,慢指针就位于链表的中间位置。 - 接着,将慢指针的下一个节点作为后半部分链表的头节点,调用
reverse
函数将后半部分链表反转,得到反转后的链表头节点slow
。 - 然后,将前半部分链表的头节点
fast
和反转后的后半部分链表的头节点slow
进行交替合并。 - 在合并过程中,使用临时变量
temp1
和temp2
分别保存当前前半部分节点的下一个节点和当前后半部分节点的下一个节点。 - 将当前前半部分节点的
next
指针指向当前后半部分节点,将当前后半部分节点的next
指针指向保存的前半部分节点的下一个节点。 - 然后移动前半部分和后半部分的指针到下一个位置,继续进行合并操作。
- 最后,如果前半部分链表还有剩余节点,将其
next
指针置为nullptr
。
- 首先,使用快慢指针法找到链表的中间节点。慢指针
三、算法思路总结
该算法首先找到链表的中间节点,将链表分为前半部分和后半部分。然后反转后半部分链表,最后交替合并前半部分和反转后的后半部分链表,实现了对单链表的重排操作。整个过程通过巧妙地运用指针操作和递归实现了链表的反转和合并,时间复杂度为O(n) ,其中 是链表的长度,因为需要遍历链表两次(一次找到中间节点,一次进行合并)。空间复杂度主要取决于递归调用栈的空间,在最坏情况下为O(n/2) ,因为递归反转后半部分链表时,递归栈的深度最多为链表长度的一半。
25. K 个一组翻转链表
题目描述
给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。k
是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 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* reverseKGroup(ListNode* head, int k) {
int n=0;
for(ListNode *cur=head;cur;cur=cur->next)
++n;
ListNode *dummy=new ListNode(0,head),*p0=dummy;
ListNode *pre=nullptr,*cur=head;
for(;n>=k;n-=k){
for(int i=0;i<k;++i){
ListNode *nxt=cur->next;
cur->next=pre;
pre=cur;
cur=nxt;
}
ListNode *nxt=p0->next;
p0->next->next=cur;
p0->next=pre;
p0=nxt;
}
return dummy->next;
}
};
代码思路
一、整体方法概述
这段代码的目的是将给定的链表按照每 k
个节点一组进行翻转。如果节点总数不是 k
的整数倍,则将最后剩余的节点保持原有顺序。
二、函数分析
- 首先,通过遍历链表计算链表的节点总数
n
。 - 创建一个虚拟头节点
dummy
,并将其next
指针指向原始链表的头节点head
。同时,定义指针p0
指向dummy
,用于在后续的操作中追踪每一组的前一个节点。 - 定义指针
pre
和cur
,分别初始化为nullptr
和head
,用于在翻转每一组节点时使用。 - 进入一个循环,只要剩余节点数量
n
大于等于k
,就继续进行分组翻转操作。 - 在每一组的翻转操作中,进行
k
次循环。在每次循环中:- 更新
pre
为当前节点cur
,cur
为保存的下一个节点nxt
。 - 然后,将当前节点
cur
的next
指针指向前一个节点pre
,实现翻转操作。 - 首先,保存当前节点
cur
的下一个节点为nxt
。
- 更新
- 当一组节点翻转完成后,进行以下操作:
- 保存
p0
的下一个节点为nxt
。 - 将
p0
的下一个节点(即当前组的第一个节点)的next
指针指向剩余未翻转的部分(即cur
)。 - 将
p0
的next
指针指向翻转后的组的新头节点(即pre
)。 - 更新
p0
为nxt
,准备处理下一组节点。
- 保存
- 循环结束后,返回虚拟头节点
dummy
的下一个节点,即为翻转后的链表的头节点。
三、算法思路总结
该算法通过计算链表长度确定需要翻转的组数,然后对每一组节点进行翻转操作。在翻转过程中,使用临时指针保存节点的关系,确保正确地翻转每一组节点,并将翻转后的组连接到正确的位置。最后,通过虚拟头节点返回翻转后的链表。整个过程的时间复杂度为O(n) ,其中 是链表的长度,因为需要遍历链表两次(一次计算长度,一次进行翻转操作)。空间复杂度为O(1) ,因为只使用了有限的额外指针变量,不随输入规模增长。