LeetCode - 234. Palindrome Linked List(回文链表)

LeetCode - 234. Palindrome Linked List(回文链表)

  • 方法一 : 使用一个栈(O(n)的空间复杂度)
  • 方法二 : 使用快慢指针,以及O(n/2)的空间复杂度
  • 方法三 : 使用快慢指针并反转链表(不需要额外的空间复杂度)

题目链接
题目

在这里插入图片描述

方法一 : 使用一个栈(O(n)的空间复杂度)

这个方法很简单,先遍历一遍链表,把整个链表都压入栈中,然后再遍历一遍,每次从栈顶拿出一个元素进行比较,如果所有元素都相同,则返回true,否则只要有一个不同就返回false

class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null)
            return true;
        
        Stack<ListNode>stack = new Stack<>();
        ListNode cur = head;
        while(cur != null){
            stack.push(cur);
            cur = cur.next;
        }
        
        cur = head;
        while(!stack.isEmpty()){
            if(stack.pop().val != cur.val){
                return false;
            }
            cur = cur.next;
        }
        return true;
    }
}

方法二 : 使用快慢指针,以及O(n/2)的空间复杂度

这个就是在第一种方法的基础上进行简单的改进:

  • 首先定义两个指针fastslow,每次让fast指针走两步,slow指针走一步;
  • 然后当fast指针走完的时候,slow指针刚好来到中点,此时把链表的后半部分压入栈中,然后拿栈中的元素依次和链表的前半部分比较,就可以得到结果。
  • 在代码实现 的过程中,要注意链表长度奇偶的不同,奇数的时候,slow指针指到中间位置的下一个位置,偶数的时候也要slow指针知道中间位置的下一个位置,一开始的时候slow = head.next,还有就是要注意fast移动的过程中,要做两个判断,防止空指针异常,具体奇偶的过程看下图。
    在这里插入图片描述在这里插入图片描述

代码实现:

class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null)
            return true;
        ListNode fast = head;
        ListNode slow = head.next;
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        
        Stack<ListNode>stack = new Stack<>();
        //把链表的后半部分推入到栈中
        while(slow != null){ 
            stack.push(slow);
            slow = slow.next;
        }
        
        //check
        slow = head;
        while(!stack.isEmpty()){
            if(stack.pop().val != slow.val)
                return false;
            slow = slow.next;
        }
        return true;
    }
}

方法三 : 使用快慢指针并反转链表(不需要额外的空间复杂度)

方法三的思想也要使用快指针和慢指针:

  • 快指针一次走两步,慢指针一次走一步,当快指针走完的时候,慢指针来到中间的位置(或者中间的前一个位置(偶数的情况(和上面的方法是不同的(上面的是慢指针在中间的后一个位置)))) (因为一开始fastslow的起点和上面的不同);
  • 然后,要用到链表的反转(链表反转不清楚的可以看一下这篇博客),此时,我们将后半部分链表反转,然后使用两个指针分别从头部和尾部位置开始比较,直到其中一个为空。 当然,不管返回true还是false,我们最后都将反转的链表的结构反转回来;
  • 在代码实现的过程中,也要注意奇数和偶数的情况,奇数的时候,slow指针正好来到中间结点,偶数的时候来到中间结点的前一个节点。

具体奇偶的过程可以看下图
这里写图片描述
这里写图片描述

代码实现

class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null)
            return true;
        ListNode fast = head;
        ListNode slow = head; //注意这里起点和之前的不同
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        
        //开始反转
        ListNode next = null;
        ListNode pre = null;
        ListNode cur = slow;
        while(cur != null){
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        } 
        
        //记录最后一个结点 , 反转之后pre是最后一个结点,
        ListNode end = pre;
        
        //开始判断
        ListNode R = end; //R从后一个结点开始比较
        ListNode L = head;
        boolean res = true;
        while( L != null && R != null){
            if(L.val != R.val){
                res = false;
                break;
            }
            L = L.next;
            R = R.next;
        }
        
        //最后还原链表
        cur = end;
        pre = null;
        while(cur != null){
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }        
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,我来用中文回复这个接:https://leetcode-cn.com/tag/dynamic-programming/ 这个接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值