LeetCode刷题之链表

本文介绍了LeetCode中关于链表的几个经典题目,包括环形链表的检测与入口查找,合并排序链表,反转链表以及删除链表倒数第k个元素的方法。通过快慢指针、递归等技巧进行解题,详细阐述了解题思路和代码实现。

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

  • Linked List Cycle(环形链表)-Number141

思路:

    利用快慢指针,快指针每次走两步,慢指针每次走一步,如果链表中存在环,那么快慢指针总会相遇

代码:

    

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast!=null && fast.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast)
                return true;
        }
        return false;
    }
}
  • Linked List Cycle II (求环的入口)-Number142

思路:

    在判断出环之后,将快指针指向链表头,慢指针指向相遇点

   快慢指针每次移动一个指针,再次相遇点就是环的入口处,设链表头部到环的入口长度为a,环的长度为b,环的入口到相遇点的长度为c:则f=2s=s+nbs=nb=a+c+(n-1)bb=a+c,即慢指针从相遇点继续前进a步就是环的入口

代码:

 

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while(fast!=null && fast.next!=null){
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){
                fast = head;
                while(fast!=slow){
                    fast = fast.next;
                    slow = slow.next;
                }
                return fast;      
            }
        }
        return null;
    }
}
  • Merge Two Sorted Lists-Number21

思路:

    利用递归的思想,当某个链表为空了,就返回另一个。然后核心还是比较当前两个节点值大小,如果 l1 的小,那么对于 l1 的下一个节点和 l2 调用递归函数,将返回值赋值给 l1.next,然后返回 l1;否则就对于 l2 的下一个节点和 l1 调用递归函数,将返回值赋值给 l2.next,然后返回 l2

代码:

    

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
            if(l1==null)
                return l2;
            if(l2==null)
                return l1;
            if(l1.val<l2.val){
                l1.next=mergeTwoLists(l1.next,l2);
                return l1;
            }else{
                l2.next=mergeTwoLists(l1,l2.next);
                return l2;
            }
            
        }
    }
  • Merge k Sorted Lists-Number23

思路:

    如果利用Merge Two Sorted Lists的思路来解决,先两两合并再与下一个链表合并,这样的话时间复杂度是很高的,所以在这里我们利用分治法来解决。

    不断的对k进行划分,k划分称k/2,再不停的划分,直到划分成只有一个链表或两个链表的任务。代码中的k是通过 (n+1)/2 计算的,这里为啥要加1呢,这是为了当n为奇数的时候,k能始终从后半段开始,比如当 n=5 时,那么此时 k=3,则0和3合并,1和4合并,最中间的2空出来。当n是偶数的时候,加1也不会有影响,比如当 n=4 时,此时 k=2,那么0和2合并,1和3合并。

代码:

    

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        if(lists.length == 0)
            return null;
        int length = lists.length; //链表长度
        while(length>1){
            int k=(length+1)/2; 
            for(int i=0;i<length/2;i++){
                lists[i] = mergeTwoLists(lists[i],lists[i+k]);
            }
            length = k;
        }
        return lists[0];
    }
    public ListNode mergeTwoLists(ListNode lists1, ListNode lists2){
        ListNode temp = new ListNode(-1);
        ListNode current = temp;
        while(lists1!=null && lists2!=null){
            if(lists1.val<lists2.val){
                current.next = lists1;
                lists1 = lists1.next;
            }else{
                current.next = lists2;
                lists2=  lists2.next;
            }
            current = current.next;
        }
        if(lists1==null)
            current.next = lists2;
        if(lists2==null)
            current.next = lists1;
        return temp.next;
    }
}
  • Reverse Linked List(反转链表)-Number 206

思路:

    链表中的主要考点在于指针,所以当需要反转链表的时候,我们只需要改变指针的指向。

    迭代法:引入三个指针,prev记录上一个节点,next记录下一个节点,head表示当前指针

    递归法:head.next.next = head

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null; // 记录前一个节点
        ListNode next = null; //记录后一个节点
        while(head!=null){
            next = head.next;
            head.next = pre; //改变指针的方向
            pre = head;
            head = next;
        }
        return pre;
    }
}
  • Remove-nth Node From End of List(删除链表的倒数第k个元素)-number19

思路:

    利用经典的双指针,定义两个指针,第一个指针从链表的头指针开始遍历向前走k-1步,第二个指针保持不动,从第k步开始,第二个指针也从链表的头指针开始遍历,由于两个指针的距离保持在k-1,当第一个指针到达链表的尾节点时,第二个指针刚好指向倒数第k个节点。

    利用快慢指针的步数差来进行巧妙的计算

代码:

  

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode fast = head;
        ListNode slow = head;
        ListNode back = null; //用来记录被删除的节点的前一个节点
        if(head == null)
            return null;
        for(int i=0;i<n-1;i++){ //快指针先走到第k-1的位置
            fast = fast.next;
        }
        while(fast!=null && fast.next!=null){
            fast = fast.next;
            back = slow;
            slow = slow.next;
        }
        if(back == null)
            return slow.next;
        else{
            back.next = back.next.next;
            return head;
        }
    }
}

在此题的基础上,进行变形,删除链表的中间节点,则可以设置快慢指针,快指针每次走两步,慢指针每次走一步,当快指针到达链尾的时候,慢指针到达中点位置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值