每日一算法题-回文链表

71 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

一、题目

image.png

struct ListNode {
    int val;
    ListNode *next = nullptr;
    inline ListNode(int x, ListNode* next = nullptr) : val(x), next(next) { }
};

bool isPalindrome(ListNode* pHead) {
}

int main(int, char**)
{
    ListNode p1(1);
    ListNode p2(2);
    ListNode p3(3);
    ListNode p4(2);
    ListNode p5(1);

    p1.next = &p2;
    p2.next = &p3;
    p3.next = &p4;
    p4.next = &p5;
    p5.next = nullptr;


    cout << (isPalindrome(&p1) ? "是回文" : "不是回文") << endl;

    return 0;
}

二、分析

根据题意可知,我们需要判断字符串是否是回文,
回文的特点是从左右两头往中间遍历,结果相同,
但是从尾部出发,势必要先遍历一遍,效率太低。

难点一:
由特点可知,从中间向左右出发遍历,也可判断,
那关键点就在于获取中间点,此时需要分两种情况,
一种总数是偶数,一种总数是奇数。

难点二:
由于链表是单向,不能实现从右到左的方向,所以,
我们还需要对左边一半部分进行逆序处理。

三、模拟

  1. 创建两个遍历节点,速度差是两倍,这样一个跑完,另一个刚好跑一半 节点一 跑得快A
    节点二 跑得慢B

  2. 创建新的节点,链接跑的慢的节点跑过的路 节点三 新表头
    老表头指向新表头的子节点
    新表头指向老表头
    老表头也走一步

  3. 判断总数的奇偶性 新表头和跑得慢都是一步一步的跑,所以会指向同一节点
    因为跑得快是两步两步的跑,所以奇数肯定会跑出界,此时,跑得慢就刚好指向中间节点,新表头也会链到中间节点,偶数就会指向中间偏左,就需要再走一步

如果 跑得快存在 跑得慢再跑一步

  1. 循环迭代比较新表头和跑得慢的值是否相等

四、实现

bool isPalindrome(ListNode* pHead) {
    if(pHead == nullptr || pHead->next == nullptr) return true;
    ListNode* rabbit = pHead;
    ListNode* tortoise = pHead;
    ListNode* head = pHead;
    ListNode cur(0);
    while(rabbit != nullptr && rabbit->next != nullptr){
        rabbit = rabbit->next->next;
        tortoise = tortoise->next;
        ListNode* temp = head->next;
        head->next = cur.next;
        cur.next = head;
        head = temp;
    }
    if(rabbit){
        tortoise = tortoise->next;
    }
    while(tortoise){
        if(tortoise->val != cur.next->val){
            return false;
        }
        tortoise = tortoise->next;
        cur.next = cur.next->next;
    }
    return true;
}

五、结言

利用了前面做过的逆序,总体难度不高

创作不易,留个赞再走吧!如果对文章内容有任何指正,欢迎评论!