Leetcode题目总结-Linkedlist-题目206和234

本文介绍了LeetCode上的两道链表题目206(Reverse a singly linked list)和234(Palindrome Linked List)。讨论了如何在O(n)时间和O(1)空间复杂度内解决这两个问题,包括迭代和递归方法。对于234题,特别强调了如何确定链表中间位置,并处理奇数和偶数长度链表的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  今天做了两道非常有意思的题目,leetcode上的206和234。这两道题目都是和链表有关的,并且都会运用的reverse list这个思想。


  pro(206): Reverse a singly linked list.


 My solution1: 

如果是数组实现回非常简单,但是linkedlist如何实现?


第一个想法是,新建一个数组,遍历linkedlist中的所有元素,并记录在数组中。再遍历一次,此时修改在linked list中的每一个元素的值. 这个方法需要三次遍历.

public class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null) return null;
        
        //第一次遍历,是用来计算链表中有多少个元素,即n
        ListNode Num = head;
        int n = 1;
        while(Num.next != null){ //判断链表是否到头
            Num = Num.next;
            n++;
        }
        
        //第二次遍历,是用来在数组中纪录链表中的元素
        ListNode Record = head;
        int[] Val = new int[n];
        for(int i = 0; i < n; i++){
            Val[i] = Record.val;
            Record = Record.next;
        }
        
        //第三次遍历,是用来反转的
        ListNode Reverse = head;
        for(int j = n-1; j >= 0; j--){
            Reverse.val = Val[j] ;
            Reverse = Reverse.next;
        }
        
        return head;
    }
}

  这是完全利用数组,那么如何治只利用链表呢?


  My solution(2): 迭代

   如果是双链表,进行reverse是非常简单的,因此我们可以借鉴双链表的方法来反转链表。这里我们需要三个node, pre , cur, next 分别代表当前,之前和之后的node.

  

public class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) return head;
        
        ListNode pre = null;
        ListNode next = null;
        
        
        while(head != null){
            next = head.next;
            head.next = pre;
            pre = head;
            head = next;
        }
        
        return pre;
        
    }
}

 

  My solution(3): 递归

  利用递归!

public class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) return head;
        
        ListNode cur = head.next;
        ListNode res = reverseList(cur); //res是得到head之后的反转
        
        head.next = null;
        cur.next = head;
        return res;      
        
    }
}


  接下来我们看看leetcode的234题。


  Pro:

Given a singly linked list, determine if it is a palindrome.

Follow up:Could you do it in O(n) time and O(1) space?


  My solution(1):

  建立了一个数组,如果是回行文,顺着的和逆的应该相等。

  

public class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) return true;
        
        ListNode countNum = head;
        int count = 1; 
        while(countNum.next != null){
            countNum = countNum.next;
            count++;
        }
        
        ListNode copyVal = head;
        int[] copy = new int[count];
        for(int j = 0; j < count; j++){
            copy[j] = copyVal.val;
            copyVal = copyVal.next;
        }
        
        int i = count-1;
        while(i >= 0){
            if(copy[i] != head.val) return false;
            
            head = head.next;
            i--;
        }

       //这里可以稍微改进一下
      /*
        int i = 0;
        while(i < count/2){
            if(copy[i] != copy[count-1-i]) return false;
            i++;
        }

      */
        return true;
        
    }
}

但是,这其实是不符合题目要求的,因为题目要求Could you do it in O(n) time and O(1) space?,因为建立了一个数组,空间复杂度是 O(n).


My solution(2): 


这是我想出来的第二个方法,但是是错误的!源于我对链表没有更深刻的理解。我先上代码,大家可以看一下!


public class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) return true;
        
        ListNode cur = head;
        ListNode reverse = reverseNode(cur);
        while(head.next != null){
            if(head.val != reverse.val) return false;
            
            head = head.next;
            reverse = reverse.next;
        }
        if(head.val != reverse.val) return false;
        else{
            return true;
        } 
            
        
    }
    private ListNode reverseNode(ListNode cur){
        if(cur == null || cur.next == null ) return cur;
        
         
        ListNode next = cur.next;
        ListNode res = reverseNode(next);
        
        cur.next = null;
        next.next = cur;
        return res;   
    }
}

  高手肯定立马看出来了!!我至始至终都是在对这一个链表操作。比如,刚开始链表时[1,2,3,1], head指着第一个元素,reverse变成[1,3,2,1], head指向最后一个元素,reverse指向第一个元素.....所以不对。


  My solution(3):


To think about :Could you do it in O(n) time and O(1) space?如何不使用额外的空间,在链表本身上操作,来判断是否相等

反转链表法,链表前半段原地翻转,再将前半段和后半段依次比较,判断是否相等。


public class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) return true;
        
        ListNode countNum = head;
        int count = 0;
        while(countNum != null){
            countNum = countNum.next;
            count++;
        }
        
        int middle = count/2;
        ListNode cur = head;
        ListNode pre = null;
        ListNode next = null;
        
        for(int i = 0; i < middle; i++){
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        
        if(count% 2 == 1){
            cur = cur.next;
        }
        
        for(int j = 0; j < middle; j++){
            if(pre.val != cur.val) return false;
            
            else{
                pre = pre.next;
                cur = cur.next;
            }
        }
        return true;
    }
}

  我对这个解决方案不太满意,因为感觉还是用了跟数组有关的东西,并没有直接在链表上下手,比如如何得到中间的node? 我们需要知道整个链表中的元素,然后在判断中间的元素在哪里。


   My solution(4):

  

   先看一下代码,然后在一一分析。


   

public class Solution {
    public boolean isPalindrome(ListNode head) {
       if(head == null || head.next == null) return true;
        
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        if(fast != null){
            slow = slow.next;
        }
        
        slow = reverse(slow);
        fast = head;
        
        while(slow != null){
            if(slow.val != fast.val) return false;
            
            slow = slow.next;
            fast = fast.next;
        }
        
        return true;
    }
    
    private ListNode reverse(ListNode rev){
        ListNode pre = null;
        ListNode next = null;
        
        while(rev != null){
            next = rev.next;
            rev.next = pre;
            pre = rev;
            rev = next;
        }
        
        return pre;
    }
}

  在这里说明一下:


1 怎么样确定slow已经到来的middle的位置?

 while(fast != null && fast.next != null){ 

//为什么要判断fast.next, 如果fast.next = null, fast.next.next就没有意义了。

            fast = fast.next.next;

            slow = slow.next;

        }


2  我们会发现,链表中元素可能为奇数个也可能为偶数个

如果是偶数个:

1221 > 1      2    2     1   null

                      (s)          (f)

我们把slow之后的反转,就变成两个链表

  null < 2 < 1

               (s 头指针)

  1 > 2

(head 头指针) 


如果是奇数个:

12321 > 1    2   3    2    1

                      (s)       (f)

此时如果把slow之后的反转,会变成:

3   <   2<   1

                (s)

和 1    >     2

    (h)


这样就没办法比较head.val 和s.val

如果加上这个:

if(fast != null){

            slow = slow.next;

        }

如果是奇数个:

12321 > 1    2   3    2    1

                           (s)       

此时如果把slow之后的反转,会变成:

   2<   1

         (s)

和 1    >     2

    (h)



  






  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值