经典模型
原理
反转一个链表:按原始顺序迭代结点,并将他们逐个移动到列表头部
如:
初始链表
- 将黑色结点的下一个结点移动到列表头部(则23从指向6变为指向15)
- 再将黑色结点的下一个结点移动到头部:
- 直到黑色结点的下一个结点为空,此时得到反转后的链表
实现
/**
* 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 head;
head=head->next;
ListNode *s=head->next;
ListNode *newHead=head;
while(head->next)
{
head->next=head->next->next;
s->next=newHead;
newHead=s;
s=head->next;
}
return newHead;
}
};
应用
回文链表
题目描述
这里涉及的倒序不需要存储下来,只需要当下的判断
- 递归
比较高级的就是利用递归、栈的特点,找到最后然后从最后开始输出
这里递归写法:
/**
* 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 {
ListNode *t;
bool res=true;
public:
bool recursivelyCheck(ListNode *head)
{
if(head==nullptr) return true;
res=recursivelyCheck(head->next)&&(t->val==head->val);
t=t->next;
return res;
}
bool isPalindrome(ListNode* head) {
t=head;
return recursivelyCheck(head);
}
};
说实话效率不是很高
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:
bool isPalindrome(ListNode* head)
{
if(head==nullptr||head->next==nullptr)
return true;
deque<int> d;
ListNode *ptr=head;
while(ptr!=nullptr)
{
d.push_back(ptr->val);
ptr=ptr->next;
}
while(!d.empty())
{
int p1=d.front();
d.pop_front();
if(d.empty()) //防止奇数的情况
return true;
int p2=d.back();
d.pop_back();
if(p1!=p2)
return false;
}
return true;
}
};
- 双指针
之前用快慢指针,都知道一个走一步一个走两步,但是这存在一个小技巧:
快指针速度是慢指针的2倍,当快指针走到尾结点时,慢指针刚好在链表中间
这就可以先找到链表的中间结点,将前面部分反转,这就可以前面和后面比较啦~
class Solution {
public:
bool isPalindrome(ListNode* head) {
if(!head||!head->next) return true;
ListNode *s=head;
ListNode *f=head;
ListNode *pre=head,*prepre=nullptr;
while(f&&f->next) //让快指针走到结尾,找慢指针指向的中间结点
{
pre=s; //先存储慢指针走过的结点
//快慢指针移动
s=s->next;
f=f->next->next;
//慢指针走过一个反转一个
pre->next=prepre; //不用关心连不到下一个,有s在
prepre=pre;
}
//若是奇数个结点,此时fast恰好是不为空的,此时s恰好是中间位置,需要往后移动一下
if(f)
s=s->next;
//前后都处理好了,就可以开始比较啦!
//前半部分是pre指向,后半部分是从s指向开始
//若不一样,就不是回文
while(pre&&s)
{
if(pre->val!=s->val) return false;
pre = pre->next;
s=s->next;
}
return true;
}
};
延伸:
快慢指针可以用来找中点,也就可以用来找n分点,调整步长比例即可