【LeetCode】链表

这篇博客详细探讨了LeetCode中与链表相关的算法问题,包括如何反转链表的后半段以检查回文,从尾到头打印链表的三种方法,删除链表中的特定节点,找到链表倒数第k个节点,删除倒数第k个节点,链表逆序,找到两个链表的第一个公共节点以及合并两个升序链表。解题思路涵盖了快慢指针、递归、栈和双指针等技巧。

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

【回文链表】反转后半段,依次比较

解题思路

  1. 快慢指针二分链表
  2. 通过fast是否为空,判断链表节点数量是奇数还是偶数,并找到中心点
  3. 让slow指向后半段的初始节点,反转后半段链表,fast指向头结点
  4. 依次循环比较
class Solution {
    public boolean isPalindrome(ListNode head) {
    ListNode fast, slow;
    slow = fast = head;

    //通过快慢指针二分链表,并且找到中点
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
    }

    //注意:slow和fast都是从head开始走的,slow一次走一步,fast一次走两步
    //所以如果链表节点数量是奇数,应该是fast正好是最后一个,slow正好指向中间轴
    //如果链表节点数量是偶数,跳出循环时,fast为空,slow指向后半段第一个
    if (fast != null) {
        slow = slow.next;
    }
    //反转后半部分链表,之后slow是反转后的链表的头结点,fast指向前半段头结点
    slow =  reverse(slow);
    fast = head;

    //比较值是否相等
    while(slow != null){
        if(slow.val != fast.val) return false;
        slow = slow.next;
        fast = fast.next;
    }
    return true;
    }

    //反转链表
    public ListNode reverse(ListNode head) {
        if(head == null) return head;

        ListNode pre,post;
        pre = head;
        post = null;

        while(pre != null){
            ListNode tmp = pre.next;
            pre.next = post;
            post = pre;
            pre = tmp;
        }
        return post;
    }
}

从尾到头打印链表

三种方法:逆序、递归、栈
  1. 原链表逆序——得到链表长度——初始化数组——遍历逆序后的链表,按位置插入结果数组
class Solution {
    public int[] reversePrint(ListNode head) {
        //逆序链表
        ListNode revListHead = reverse(head);
        
        //计算链表长度
        int size = 0;
        ListNode count = revListHead;
        while(count != null){
            count = count.next;
            size++;
        }

        //通过size初始化结果数组
        int[] res = new int[size];
        int index = 0;

        //遍历链表,往数组里插入数值
        while(revListHead != null){
            res[index] = revListHead.val;
            revListHead = revListHead.next;
            index++;
        }
        return res;

    }

    //逆序链表
    public ListNode reverse(ListNode head){
        ListNode pre,post;
        pre = head;
        post = null;

        while(pre != null){
            ListNode tmp = pre.next;
            pre.next = post;
            post = pre;
            pre = tmp;
        }

        return post;
    }
}
  1. 递归法
class Solution {
    //定义全局变量,i用来初始化数组容量
    //递归后半段,从后往前,j是元素插入数组的索引位置
    //res数组定义要放在全局,不能放在方法中
    int i = 0;
    int j = 0;
    int[] res;
    
    public int[] reversePrint(ListNode head) {
        recur(head);
        return res;
    }
    
    public void recur(ListNode head){
        if(head == null){
            res = new int[i];
            return;
        }
        
        i++;
        recur(head.next);
        res[j] = head.val;
        j++;        
    }
}

3.通过栈完成

class Solution {
    public int[] reversePrint(ListNode head) {
        LinkedList<ListNode> stack = new LinkedList<>();

        //把链表节点从头到尾放入栈中
        while(head != null){
            stack.addFirst(head);
            head = head.next;
        } 

        //通过栈大小得到结果数组大小
        int[] res = new int[stack.size()];
        int index = 0;
        
        //出栈,把值存入结果数组
        while(!stack.isEmpty()){
            res[index++] = stack.poll().val;
        }
        return res;
    }
}

删除链表中的特定节点

  1. 考虑特殊情况:head == nullhead.val == val
  2. 其他情况:设置当前节点cur检测,前置节点pre辅助删除
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if(head == null) return head;           //head为空
        if(head.val == val) return head.next;   //第一个元素就是val,特殊处理

        //除了第一个元素,其他任何元素的删除操作,必须存在一个前置节点
        ListNode pre,cur;
        pre = null;
        cur = head;

        while(cur != null){
            if(cur.val == val) break;

            pre = cur;
            cur = cur.next;
        }

        pre.next = pre.next.next;
        return head;
    }
}
  1. 递归
思想:

递归前半段是找到那个值为val的节点,后半段就是把节点接上

递归出口有两个:

(1)是head == null直接返回
(2)还有一个出口是head.val == val,当前的head即为要删除的节点

所以当head.val == val时,返回head.next即可


【返回链表倒数第k个节点】

解题思路

固定好双指针之间的距离
双指针向后滑动,滑到fast为空,返回slow即可

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        if(head == null) return head;
        
        ListNode slow,fast;
        slow = fast = head;
		//固定好slow和fast的距离
        while(k > 0){
            fast = fast.next;
            k--;
        }
		//一起向后滑动
        while(fast != null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

【删除链表倒数第k个节点】请与上一题作比较

思想:想删除倒数第k个,有一个指针在倒数第k+1的位置上即可;
  1. 滑动的游标卡尺的长度为k+1
  2. 为了统一头结点的操作,我们新建一个头结点nHead
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head == null || head.next == null)  return null;

        ListNode nHead = new ListNode(0);
        nHead.next = head;

        ListNode slow,fast;
        slow = fast = nHead;
        
        while(n > 0){
            fast = fast.next;
            n--;
        }

        while(fast.next != null){
            slow = slow.next;
            fast = fast.next;
        }

        slow.next = slow.next.next;

        return nHead.next;
    }
}


链表逆序

固定模板,熟记
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null) return head;

        ListNode pre,post;
        pre = head;
        post = null;

        while(pre != null){
            ListNode tmp = pre.next;
            pre.next = post;
            post = pre;
            pre = tmp;
        }
        return post;
    }
}

两个链表的第一个公共节点

思想:浪漫相遇,走过你来时的路,我就会和你相遇
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) return null;
        
        ListNode a,b;
        a = headA;
        b = headB;

        while(a != b){
            a = a==null?headB:a.next;
            b = b==null?headA:b.next;
        }

        return a;
    }
}

合并两个升序链表

  1. 递归
  2. 非递归
递归
在这里插入代码片
非递归
新建链表头结点,尾插法建立链表
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        /*
        非递归,新建链表头结点,尾插法建立链表
        */
        if(l1 == null && l2 == null) return null;
        if(l1 == null) return l2;
        if(l2 == null) return l1;

        ListNode newHead = new ListNode(0);
        ListNode tail = newHead;

        while(l1 != null && l2 != null){
            if(l1.val < l2.val){
                tail.next = l1; 
                l1 = l1.next; 
                tail = tail.next;
                continue;
            }else if(l1.val > l2.val){
                        tail.next = l2;
                        l2 = l2.next;
                        tail = tail.next;
                        continue;
            }else{
                tail.next = l1;
                l1 = l1.next;
                tail = tail.next;

                tail.next = l2;
                l2 = l2.next;
                tail = tail.next;
                continue;
            }
        }

        if(l1 != null) tail.next = l1;
        if(l2 != null) tail.next = l2;

        return newHead.next;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值