LC 链表反转-回文

经典模型

原理

反转一个链表:按原始顺序迭代结点,并将他们逐个移动到列表头部

如:
初始链表
初始链表

  1. 将黑色结点的下一个结点移动到列表头部(则23从指向6变为指向15)
    第一次变化
  2. 再将黑色结点的下一个结点移动到头部:
    重复过程
  3. 直到黑色结点的下一个结点为空,此时得到反转后的链表

实现

/**
 * 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;
    }
};

应用

回文链表

题目描述

题目描述
这里涉及的倒序不需要存储下来,只需要当下的判断

  1. 递归
    比较高级的就是利用递归、栈的特点,找到最后然后从最后开始输出

这里递归写法:

/**
 * 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;
    }
};

  1. 双指针
    之前用快慢指针,都知道一个走一步一个走两步,但是这存在一个小技巧:

快指针速度是慢指针的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分点,调整步长比例即可

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值