震惊!双指针解决链表百分之百的问题!

本文详细介绍了多种链表操作的解法,包括合并两个有序链表、删除有序链表的重复元素、判断链表是否存在环以及找到环的起点、寻找两个链表的相交点、链表反转和从尾到头打印链表等。解法涵盖了循环、递归和双指针等技巧。
摘要由CSDN通过智能技术生成

一.合并两个有序链表【力扣21】

题目表述

在这里插入图片描述

解法1、循环+双指针

  • new一个结果链表,一个p节点
  • 链表1、2谁小,p.next节点指向谁
  • 谁空,p.next就指向另一个链表
class Solution {
    	public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
		if (l1 == null) return l2;
        if (l2 == null) return l1;
        

        ListNode result=new ListNode(0);
        ListNode p=result;
        while(l1!=null&& l2!=null){
            if(l1.val<l2.val){
                p.next=l1;
                l1=l1.next;
            }else{
                p.next=l2;
                l2=l2.next;
            }
            p=p.next;
        }
        if(l1==null) p.next=l2;
        if(l2==null) p.next=l1;

        return result.next;
}

解法2、递归

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
		if (l1 == null) {
			return l2;
		} else if (l2 == null) {
			return l1;
		} else if (l1.val < l2.val) {
			l1.next = mergeTwoLists(l1.next, l2);
			return l1;
		} else {
			l2.next = mergeTwoLists(l1, l2.next);
			return l2;
		}
	
    }

二、删除有序列表的重复元素【力扣83】

题目表述

在这里插入图片描述

解法1、常规解法

  • 创建一个p节点,让p=head.
  • 如果p等于p.next,跳过p.next,让p.next=p.next.next
  • 如果p不等于p.next,符合题意,p节点移动,p=p.next
  • 返回head
    public ListNode deleteDuplicates(ListNode head) {
        if(head==null) return head;

        ListNode p= head;
        while(p.next!=null){
        if(p.next.val==p.val){
            p.next=p.next.next;         
        }else{
            p=p.next;
        }
        }
    return head;
        // return p.next;//[1,2]
        // return p.next.next;//[2]
        // return p.next.next.next;//[]
    }

解法2、递归

        if(head == null||head.next==null) return head;
        head.next=deleteDuplicates(head.next);
        return head.val==head.next.val?head.next:head;

三、环形链表【力扣141】

题目表述

  • 抓住主要题干:题目给出链表的头节点,判断链表是否有环

在这里插入图片描述

解法:快慢指针相遇问题

  • 注意健壮性,除了head可能为空,还有构成环的因素
  • 慢指针一步走一个节点,快指针一步走两个节点
  • 如果相遇,就有环的存在,返回true
  • 如果没有,跳出循环,返回false
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head==null) return false;
        ListNode slow=head;
        ListNode quick=head;
        // 一个或者两个节点构不成环
        while(quick.next!=null&&quick.next.next!=null){
            slow=slow.next;
            quick=quick.next.next;
            if(slow==quick){
                return true;
            }
        }
        return false;
}
}

四、环形链表返回头节点【力扣142】

题目表述

  • 在141的基础上,返回环的开头节点

解法

如果有环,就让慢指针重新回到开始位置,快慢指针速度均为1,快慢指针再次相遇就是环开头的位置

  • 快慢指针第一次相遇是确定环
  • 快慢指针第二次相遇是确定环开头的位置
public class Solution {
    public ListNode detectCycle(ListNode head) {
        Boolean logo=false;
        if(head==null) return head;
        ListNode slow=head;
        ListNode quick=head;
        while(quick.next!=null&&quick.next.next!=null){
            slow=slow.next;
            quick=quick.next.next;
            if(slow==quick){
                logo=true;
                break;
            }
        }
        if(logo==true){
            slow=head;
            while(slow!=quick){
                slow=slow.next;
                quick=quick.next;
            }
            return slow;
        }
        return null;
    }
}

五、相交链表【力扣160】

题目表述

在这里插入图片描述

解法:相遇

  • 长的链表,节点先移动到与短链表相同长度的位置
  • 两个链表的节点在同时移动,如果相同,则相遇
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode ha=headA;
        ListNode hb=headB;
        int la=0;
        int lb=0;
        int cha=0;
    // while循环,遍历出a,b链表的长度
        while(ha.next!=null){
            ha=ha.next;
            la++;
        }
        while(hb.next!=null){
            hb=hb.next;
            lb++;
        }
    // 对比长度,长度长的链表,统一赋值给ha
        if(la<lb){
            ha=headB;
            hb=headA;
            cha=lb-la;
        }else{
            ha=headA;
            hb=headB;
            cha=la-lb;
        }
    // ha先移动到相同长度的位置
        for(int i=0;i<cha;i++){
            ha=ha.next;
        }

        while(ha!=null&&hb!=null){
            // 如果移动到相同节点,也就是相交节点,就返回该节点
            if(ha==hb){
                return ha;
            }
            ha=ha.next;
            hb=hb.next;
        }
        // 没有相遇,就返回空
        return null;
    }
}

大佬算法

作者:jyd
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/solution/mian-shi-ti-0207-lian-biao-xiang-jiao-sh-b8hn/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 双指针,A指针遍历A链表,再遍历链表B,B指针则反
        ListNode A = headA, B = headB;
        // 当相遇的时候,返回该时结点
        while (A != B) {
            // 如果已经遍历完毕,就去遍历另一个链表
            A = A != null ? A.next : headB;
            B = B != null ? B.next : headA;
        }
        return A;
    }
}

六、反转链表【力扣206】

题目表述

在这里插入图片描述

解法

  • 临时存储:先创建一个临时节点,用于存储p.next
  • 改变指针方向:p的下一个节点方向指向前一个节点
  • 改变右内容:代表前节点的pre,继续移动到p的位置
  • 改变左内容:此时的p向后移动,等于原先p.next的值
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null) return head;
        ListNode pre=null;
        ListNode p=head;

        while(p!=null){
            // 1.先创建一个临时节点,用于存储p.next
            ListNode temp=p.next;
            // 2.p的下一个节点应该指向前一个节点
            p.next=pre;
            // 3.代表前节点的pre,继续移动到p的位置
            pre=p;
            // 4.此时的p向后移动,等于原先p.next的值
            p=temp;
        }
        return pre;
    }
}

六-1、从尾到头打印链表【剑指offer06】

题目描述

  • 注意:返回的是整型数组
  • 数组的长度是固定的,所以设置长度变量
    在这里插入图片描述

解法

  • 注意数组没有add语法
  • temp2[i]=pre.val;
class Solution {
    public int[] reversePrint(ListNode head) {
        int[] temp1=new int[0];
        int size=0;
        if(head==null) return temp1;
        ListNode pre=null;
        ListNode p=head;
        while(p!=null){
            size++;
            ListNode tt=p.next;
            p.next=pre;
            pre=p;
            p=tt;
        }
        int[] temp2=new int[size];
        for(int i=0;i<size;i++){

            temp2[i]=pre.val;
            pre=pre.next;

        }
        return temp2;
        
    }
}

七、链表的中间结点【力扣876】

题目描述

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

在这里插入图片描述

解法

  • 快慢指针,快指针速度为2,慢指针速度为1
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode slow=head;
        ListNode quick=head;
        while(quick!=null&&quick.next!=null){
            slow=slow.next;
            quick=quick.next.next;   
        }
        return slow;
    }
}

八、链表中倒数第k个结点【剑指offer22】

题目描述

  • 注意:此题返回的是k以后的链表
    在这里插入图片描述

解法

  • 快慢指针,快指针和慢指针速度一致
  • 快指针先走k步,这样慢指针就慢走K步
  • 慢指针达到的位置就是倒数第k个结点
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode slow=head;
        ListNode quick=head;
        for(int i=0;i<k;i++){
                quick=quick.next;
        }
        while(quick!=null){
            quick=quick.next;
            slow=slow.next;
        }
        return slow;
    }
}

八-1、删除链表的倒数第k个结点,八的升级【力扣19】

题目描述

  • 给定一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点

在这里插入图片描述

解法

  • 思路与上面一致
  • 最后的连接,要跳过删除的结点
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head.next==null) return null;
        ListNode slow=head;
        ListNode quick=head;
        for(int i=0;i<n;i++){
            quick=quick.next;
        }
        if(quick==null) return head.next;

        while(quick.next!=null){
            quick=quick.next;
            slow=slow.next;
        }
        slow.next=slow.next.next;
        return head;

    }
      
}

八-2、返回倒数第k个结点 八的升级【力扣面试题02.02】

题目表述

在这里插入图片描述

解法

  • 思路和八一样,快慢指针
  • 直接返回值即可,.val
class Solution {
    public int kthToLast(ListNode head, int k) {
        ListNode slow=head;
        ListNode quick=head;
        for(int i=0;i<k;i++){
            quick=quick.next;
        }
        while(quick!=null){
            slow=slow.next;
            quick=quick.next;
        }
        return slow.val;
    }
}

九、链表排序【力扣148】

题目表述

在这里插入图片描述

解法

  • 归排或者快排,数组是元素组成,而链表是元素+方向,所以要把链表进行拆分
  • 只要不断的比较链表的头结点的值,将较短的放入合并后的链表中,并更新头结点就可以了。
  • 有位大佬提出虚拟结点,在此引用
public class Offer077 {
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        // list 为链表后半段的头结点,在 splitList 函数中完成了将链表从中间截断的处理
        ListNode list = splitList(head);
        head = sortList(head);
        list = sortList(list);
        return mergeList(head, list);
    }

    private ListNode splitList(ListNode head) {
        // 为链表增加一个虚拟节点
        // 当 fast 达到尾端时,slow 刚好指向前半段链表的最后一个节点
        ListNode virtualHead = new ListNode(-1, head);
        ListNode fast = virtualHead, slow = virtualHead;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode result = slow.next;
        slow.next = null;
        return result;
    }

    private ListNode mergeList(ListNode head1, ListNode head2) {
        // 增加虚拟头结点,可以统一操作
        // 不用刻意考虑链表中存在空节点的情况,也不用特意增加判断合并后的链表的头结点是什么
        ListNode virtualHead = new ListNode();
        ListNode node = virtualHead;
        ListNode node1 = head1, node2 = head2;
        while (node1 != null && node2 != null) {
            // 将两个链表中较小的加入到合并后的链表中
            if (node1.val <= node2.val) {
                node.next = node1;
                node1 = node1.next;
            } else {
                node.next = node2;
                node2 = node2.next;
            }
            node = node.next;
        }
        if (node1 != null) {
            node.next = node1;
        } else if (node2 != null) {
            node.next = node2;
        }
        return virtualHead.next;
    }
}


作者:huaLuoYueQue
链接:https://leetcode.cn/problems/7WHec2/solution/hua-luo-yue-que-fen-er-zhi-zhi-tu-jie-li-95mj/
来源:力扣(LeetCode
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值