力扣刷题指南
(若有侵权会及时删除,请联系告知!)
第一题(知识点:链表的旋转和反转)
题目描述
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例
示例1:
输入:head = [1,2,3,4]
输出:[2,1,4,3]
示例2:
输入:head = []
输出:[]
示例3:
输入:head = [1]
输出:[1]
思路分析
- 知识点:链表翻转、递归——参考题解画手大鹏
- 本题的递归和非递归解法其实原理类似,都是更新每两个节点的链表形态来完成整个链表的调整。
方法一:递归算法
递归需要注意以下三个条件,分别是:
- 返回值
- 调用单元做了什么
- 终止条件
在本题中:
1.返回值:交换完成的子链表的头结点,即
L
2
L2
L2 。
2.调用单元:设需要交换的两个点为
h
e
a
d
head
head 和
n
e
x
t
next
next,
h
e
a
d
head
head 连接后面交换完成的子链表,
n
e
x
t
next
next 连接
h
e
a
d
head
head ,完成交换。
3.终止条件:
h
e
a
d
head
head 为空指针或者
n
e
x
t
next
next 为空指针,也就是当前无节点或者只有一个节点,无法进行交换。
示例代码
/**
* 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 * p1 = head;
ListNode * p2 = p1->next;
ListNode * p3 = p2->next;
p1->next = swapPairs(p3);
p2->next = p1;
return p2;
}
};
复杂度分析
- 时间复杂度: O ( n ) , O(n), O(n),其中 n n n 是链表的节点数量。需要对每个节点进行更新指针的操作。
- 空间复杂度: O ( n ) , O(n), O(n),其中 n n n 是链表的节点数量。空间复杂度主要取决于递归调用的栈空间。
方法二:非递归算法,注意L0/L1/L2/L3的指针交换过程,分析同方法一
示例代码
/**
* 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(-1);
dummy->next = head;
ListNode * p0 = dummy;
ListNode * p1 = head;
while(p1 && p1->next)
{
ListNode * p2 = p1 -> next;
ListNode * p3 = p2 -> next;
p0->next = p2;
p2->next = p1;
p1->next = p3;
p0 = p1;
p1 = p3;
}
return dummy->next;
}
};
复杂度分析
- 时间复杂度: O ( n ) , O(n), O(n),其中 n n n 是链表的节点数量。需要对每个节点进行更新指针的操作。
- 空间复杂度: O ( 1 ) O(1) O(1)
第二题(知识点:链表的旋转和反转/快慢指针)
题目描述
给你一个链表的头节点 h e a d head 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* rotateRight(ListNode* head, int k) {
if(head == nullptr) return nullptr;
ListNode *fast = head;
ListNode *slow = head;
int n = 0;
//计算链表中节点个数
while(fast != nullptr)
{
n++;
fast = fast->next;
}
k = k % n;
if(k == 0) return head;
fast = head;
int ans = 0;
//双指针--快指针先向前移动k步
while(ans < k)
{
fast = fast->next;
ans++;
}
//快慢指针同时向前移动,直到快指针指向节点的下一个节点为空指针
while(fast->next)
{
fast = fast->next;
slow = slow->next;
}
//开始调整位置
ListNode * newHead = nullptr;
newHead = slow->next;
fast->next = head;
slow->next = nullptr;
return newHead;
}
};
复杂度分析
- 时间复杂度: O ( n ) , O(n), O(n),其中 n n n 是链表的节点数量。
- 空间复杂度: O ( 1 ) O(1) O(1)
第三题(知识点:链表的旋转和反转)
题目描述
给你单链表的头节点 h e a d head head ,请你反转链表,并返回反转后的链表。
示例
示例代码一:迭代法
示例一: 1->2->3->4->5->null
翻转后: 5->4->3->2->1->null
我们需要存储节点cur以及它的前驱节点pre,同时在交换的过程中节点的next指针位置会发生改变
,因此交换之前需要存储其节点的下一个位置,代码如下
/**
* 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) {
if(head == nullptr) return nullptr;
ListNode* pre = nullptr;
ListNode* cur = head;
while(cur != nullptr)
{
ListNode * temp = cur -> next;
cur -> next = pre;
pre = cur;
cur = temp;
}
return pre;
}
};
复杂度分析
- 时间复杂度: O ( n ) , O(n), O(n),其中 n n n 是链表的节点数量,需要遍历链表一次。
- 空间复杂度: O ( 1 ) O(1) O(1)
示例代码二:递归法——参考官方题解评论区大佬NoColor96的代码
/**
* 以链表1->2->3->4->5举例
* @param head
* @return
*/
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
/*
直到当前节点的下一个节点为空时返回当前节点
由于5没有下一个节点了,所以此处返回节点5
*/
return head;
}
//递归传入下一个节点,目的是为了到达最后一个节点
ListNode newHead = reverseList(head.next);
/*
第一轮出栈,head为5,head.next为空,返回5
第二轮出栈,head为4,head.next为5,执行head.next.next=head也就是5.next=4,
把当前节点的子节点的子节点指向当前节点
此时链表为1->2->3->4<->5,由于4与5互相指向,所以此处要断开4.next=null
此时链表为1->2->3->4<-5
返回节点5
第三轮出栈,head为3,head.next为4,执行head.next.next=head也就是4.next=3,
此时链表为1->2->3<->4<-5,由于3与4互相指向,所以此处要断开3.next=null
此时链表为1->2->3<-4<-5
返回节点5
第四轮出栈,head为2,head.next为3,执行head.next.next=head也就是3.next=2,
此时链表为1->2<->3<-4<-5,由于2与3互相指向,所以此处要断开2.next=null
此时链表为1->2<-3<-4<-5
返回节点5
第五轮出栈,head为1,head.next为2,执行head.next.next=head也就是2.next=1,
此时链表为1<->2<-3<-4<-5,由于1与2互相指向,所以此处要断开1.next=null
此时链表为1<-2<-3<-4<-5
返回节点5
出栈完成,最终头节点5->4->3->2->1
*/
head.next.next = head;
head.next = null;
return newHead;
}
复杂度分析
- 时间复杂度: O ( n ) , O(n), O(n),其中 n n n 是链表的节点数量。
- 空间复杂度: O ( n ) , O(n), O(n), n是链表的长度。空间复杂度主要取决于递归调用的栈空间,最多为n层