[labuladong算法小抄]如何判断回文链表

本文来自labaladong的算法小抄 , 使用GO语言重新描述代码部分

寻找回文串的核心思想是从中心向两端扩展

func  Palindrome( s string,l,r int)string {
    str:=[]rune(s)
    for l>=0 && r < len(str) && str[l]==str[r]{
        l--
        r++
    }
    return string(str[l:r])
}

因为回文串长度可能为奇数也可能是偶数,长度为奇数时只存在一个中心点,而长度为偶数时存在两个中心点,所以上面这个函数需要传入l和r。

判断一个字符串是不是回文串就简单很多,不需要考虑奇偶情况,只需要「双指针技巧」,从两端向中间逼近即可:

func  isPalindrome( s string)bool {
    str:=[]rune(s)
    left := 0
    right := len(str) - 1
    for left < right {
        if str[left] != str[right]{
            return false
        }
        left++
        right--
    }
    return true
}

以上代码很好理解吧,因为回文串是对称的,所以正着读和倒着读应该是一样的,这一特点是解决回文串问题的关键。

下面扩展这一最简单的情况,来解决:如何判断一个「单链表」是不是回文。

一、判断回文单链表

输入一个单链表的头结点,判断这个链表中的数字是不是回文:

/**
 * 单链表节点的定义:
type ListNode struct {
    val  int
    next *ListNode
}
func isPalindrome(head ListNode)bool;

输入: 1->2->null
输出: false

输入: 1->2->2->1->null
输出: true
 */

这道题的关键在于,单链表无法倒着遍历,无法使用双指针技巧。那么最简单的办法就是,把原始链表反转存入一条新的链表,然后比较这两条链表是否相同。关于如何反转链表,可以参见前文「递归操作链表」。

其实,借助二叉树后序遍历的思路,不需要显式反转原始链表也可以倒序遍历链表,下面来具体聊聊。

对于二叉树的几种遍历方式,我们再熟悉不过了:

func traverse(root *TreeNode) {
    // 前序遍历代码
    traverse(root.left)
    // 中序遍历代码
    traverse(root.right)
    // 后序遍历代码
}

在「学习数据结构的框架思维」中说过,链表兼具递归结构,树结构不过是链表的衍生。那么,链表其实也可以有前序遍历和后序遍历

func traverse2(head *ListNode) {
    // 前序遍历代码
    traverse2(head.next)
    // 后序遍历代码
}

这个框架有什么指导意义呢?如果我想正序打印链表中的val值,可以在前序遍历位置写代码;反之,如果想倒序遍历链表,就可以在后序遍历位置操作

func traverse3(head *ListNode) {
    if head == nil {
        return
    }
    // 前序遍历代码
    traverse3(head.next)
    // 后序遍历代码
    fmt.Println(head.val)
}

说到这了,其实可以稍作修改,模仿双指针实现回文判断的功能:

// 左侧指针
var left *ListNode

func isPalindromeList(head *ListNode)bool {
    left = head
    return traverse4(head)
}

func traverse4( right *ListNode)bool {
    if right == nil {
        return true
    }
    res:= traverse4(right.next)
    // 后序遍历代码
    res = res && (right.val == left.val)
    left = left.next
    return res
}

这么做的核心逻辑是什么呢?实际上就是把链表节点放入一个栈,然后再拿出来,这时候元素顺序就是反的,只不过我们利用的是递归函数的堆栈而已。

当然,无论造一条反转链表还是利用后序遍历,算法的时间和空间复杂度都是 O(N)。下面我们想想,能不能不用额外的空间,解决这个问题呢?

二、优化空间复杂度

更好的思路是这样的:

1、先通过「双指针技巧」中的快慢指针来找到链表的中点:

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值