刷题不是为了刷题,而是为了锻炼解题思路,因为题是刷不完的,但是解题思路又是在一定题量的基础上积累提炼的,所以现在就来看看其中的一个升华过程: 从
翻转链表
到
k个一组翻转链表
。
题目与判题环境:《
power by leetcode
》,感谢leetcode平台和上面的大佬们。
开始:
链表大伙都很熟悉了,一个结构体的事情(原理和实现不提),现在有这么个链表和一个需求如下:
将这个链表进行翻转,就是将链表的头变成尾,尾变成头,中间元素也一样进行翻转;
对于数组而言,这个很容易进行翻转,首尾两个指针,逐步向中间逼近的同时翻转即可。
但是链表只给你一个头指针,后面有啥,你并不知道,并且内存空间不连续,你只有遍历了一次,才知道链表的全貌。
翻转链表代码很简单,最主要是如何理解思路;
-
prelude
序曲,曲奏,序幕 所以你可以看到很多大佬们的代码里面有pre的,这就是prelude的简写。 -
cursor
游标,如果学过数据库之类的,相信都不陌生,大佬们都简写为 cur
这里只使用
迭代/双指针法
进行翻转,想要学习递归之类的,请转去相应题目看题解。
先创建个nullptr节点,然后让头节点指向它,然后像经典的swap函数一样做法,遍历一遍即可,最后返回pre节点。
(会动的)
相应代码:
/**
* 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* pre = nullptr ;
ListNode* cur = head ;
while(cur){
ListNode* next = cur->next; //只是为了暂时保存下个节点的信息
cur -> next = pre ;
pre = cur;
cur = next;
}
return pre;
}
};
很好,然后通过这道题目,可以有变形的k个一组翻转链表;
题目会给一个头节点,一个k值。要求每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) {
ListNode* roll = new ListNode(0);//创建虚拟节点
roll -> next = head;//虚拟节点的下一个节点为头节点
ListNode *start = roll ;
ListNode *end = roll ;
while(1){
//遍历循环探链表真相
//----------------------------------
for(int i=0;i<k&&end;i++)end=end->next;//遍历k个节点
//----------------------------------
//指针为空即退出循环
//----------------------------------
if(!end)break;//end指针遍历null即退出循环
//----------------------------------
//新建节点保节点信息
//----------------------------------
ListNode* startNext = start->next;//创建两个节点来保存原有的两个关键节点信息,不让二者在翻转子链表的过程中收到影响;
ListNode* endNext = end->next;
//-----------------------------------
//断开链表求单独翻转
//-----------------------------------
end -> next = nullptr; //断开链表,指向nullptr
start -> next = reverseList(start->next);//调用刚刚写的翻转链表的函数,进行切割好的子链表的翻转操作;
//-----------------------------------
//链表翻转后有所指向
//-----------------------------------
startNext -> next = endNext;//通过翻转前保存的关键节点的信息进行拼接
//-----------------------------------
//重新定位预备下次操作
//----------------------------------
start = end = startNext;//进行定位
//----------------------------------
}
return roll->next; //返回翻转后的头节点,就是虚拟节点的下一个节点
}
ListNode* reverseList(ListNode* head){ //翻转链表操作,返回头节点 pre
ListNode* pre = nullptr;
ListNode* cur = head;
while(cur){
ListNode* next = cur->next;
cur -> next = pre;
pre = cur;
cur = next;
}
return pre;
}
};
好了,到这里你就会了两道题,然后可以触类旁通、举一反三。不管链表题怎么变,
都不会做
。呃,都有点思路。
参考链接: