常规
题目来源:LeetCode-25-K个一组翻转链表
思路:
- 给已知链表加一个头节点 H a i r Hair Hair(官方的代码还挺有趣)
- 找出需要翻转的链表的头指针和尾指针(尾指针需要从头指针向后扫描K个,若不满足,则不翻转)
- 保存尾指针的下一个节点地址 n e x nex nex,不然等这一段翻转结束后链表就串不起来了
- 进入翻转函数进行翻转,这里使用了C++17的一个新功能,返回了一个用 t i e ( ) tie() tie()函数组合的 p a i r pair pair(一般函数的返回值只能是一个值,而这里同时返回翻转后链表的头指针和尾指针)
- 翻转结束后将链表重新链接, h e a d head head是头指针的后继,而 t a i l tail tail是 n e x nex nex的前驱
- 更新 p r e pre pre和 h e a d head head( p r e pre pre为 h e a d head head的前驱, h e a d head head为待翻转的第一个元素)
- 翻转函数种所做的事情其实就是对传入的链表进行翻转,很像循环链表的操作过程,在这里需要注意要修改指针值时,必须先将指针进行保存,不然该元素就永远找不见了。
代码:
/**
* 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) {
ListNode* hair = new ListNode(0);
hair->next = head;
ListNode* pre = hair;
while(head){
ListNode* tail = pre;
for(int i = 0;i < k;i++){
tail = tail->next;
if(tail == NULL)
return hair->next;
}
ListNode* nex = tail->next;
tie(head,tail) = MyReverse(head,tail);
pre->next = head;
tail->next = nex;
pre = tail;
head = tail->next;
}
return hair->next;
}
pair<ListNode*,ListNode*> MyReverse(ListNode* head,ListNode* tail){
ListNode* prev = tail->next;
ListNode* p = head;
while(prev != tail){
ListNode* nex = p->next;
p->next = prev;
prev = p;
p = nex;
}
return {tail,head};
}
};
静态链表
题目来源:PTA-Reversing Linked List
分析:其实思路和常规链表一样,只是数据是用数组保存的
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int MAXSIZE = 1e5 + 1;
struct Node{
int Data;
int Next;
};
vector<Node> List;
int ADDRESS,N,K;
void Init(){
cin >> ADDRESS >> N >> K;
List.resize(MAXSIZE);
for(int i = 0;i < N;i++){
int Index,Data,Add;
cin >> Index >> Data >> Add;
List[Index].Data = Data;
List[Index].Next = Add;
}
}
pair<int,int> MyReverse(int head,int tail){
int prev = List[tail].Next;
int p = head;
while(prev != tail){
int nex = List[p].Next;
List[p].Next = prev;
prev = p;
p = nex;
}
return {tail,head};
}
void ReverseKGroup(){
int pre = 1e5;
List[pre].Next = ADDRESS;
int head = ADDRESS;
while(head != -1){
int tail = pre;
for(int i = 0;i < K;i++){
tail = List[tail].Next;
if(tail == -1)
return;
}
int nex = List[tail].Next;
tie(head,tail) = MyReverse(head,tail);
List[pre].Next = head;
List[tail].Next = nex;
pre = tail;
head = List[tail].Next;
}
}
void Print(){
int Pos = List[1e5].Next;
while(Pos != -1){
cout << setw(5) << setfill('0') << Pos << " " ;
cout << List[Pos].Data << " " ;
if(List[Pos].Next == -1)
cout << List[Pos].Next << endl;
else
cout << setw(5) << setfill('0') << List[Pos].Next << endl;
Pos = List[Pos].Next;
}
}
int main(){
Init();
ReverseKGroup();
Print();
}
将程序中的
v
e
c
t
o
r
vector
vector容器改为
N
o
d
e
Node
Node类型的数组,其空间复杂度会有显著降低,时间复杂度却会上升。由于题目允许的空间复杂度较高,而时间复杂度较低,所以可以使用容器来用空间换时间。
另一种方式是将各个节点的地址进行保存,使用
r
e
v
e
r
s
e
(
)
reverse()
reverse()函数翻转地址。其中
r
e
v
e
r
s
e
(
)
reverse()
reverse()函数的两个参数为
[
f
i
r
s
t
,
l
a
s
t
)
[first,last)
[first,last),左闭右开区间
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define MAXSIZE 1000010
struct node //使用顺序表存储data和下一个地址next
{
int data;
int next;
}node[MAXSIZE];
int List[MAXSIZE];
int main()
{
int first, n, K; //输入头地址 和 n,k;
int Address, Data, Next;
cin >> first >> n >> K;
for (int i=0; i < n; i++)
{
cin >> Address >> Data >> Next;
node[Address].data = Data;
node[Address].next = Next;
}
int j = 0; //j用来存储能够首尾相连的节点数
int p = first; //p指示当前结点
while (p != -1) //保存所有节点的地址,便于后边翻转
{
List[j++] = p;
p = node[p].next;
}
int i = 0;
while (i + K <= j)
{
reverse(&List[i],&List[i+K]);
i=i+K;
}
for (i = 0; i < j - 1; i++)
{
printf("%05d %d %05d\n",List[i],node[List[i]].data,List[i+1]);
}
printf("%05d %d -1\n",List[i],node[List[i]].data);
return 0;
}