LeetCode链表集锦

LeetCode链表集锦_第1张图片

⭐️前言⭐️

本篇文章是博主按照代码随想录的链表部分力扣题的一些见解,有一些自己的想法与代码随想录题解结合,能使这部分题理解的更加透彻。

博客主页: 【如风暖阳】
精品Java专栏【JavaSE】、【备战蓝桥】、【JavaEE初阶】、【MySQL】、【数据结构】
欢迎点赞 收藏留言评论 私信必回哟

本文由 【如风暖阳】 原创,首发于 CSDN

博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言

博客中涉及源码及博主日常练习代码均已上传码云(gitee)、GitHub


内容导读

  • 203.移除链表元素
  • 707. 设计链表
  • 206.反转链表
  • 24. 两两交换链表中的节点
  • 19. 删除链表的倒数第 N 个结点
  • 面试题 02.07. 链表相交
  • 142. 环形链表 II
  • 导图总结

203.移除链表元素

力扣题目链接

题意:删除链表中等于给定值 val 的所有节点。

示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:
输入:head = [], val = 1
输出:[]

示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]

思路:

LeetCode链表集锦_第2张图片
设置虚拟头节点,让del节点从头开始遍历,找到相应定值对应的点后完成删除。
完成删除操作使用双指针的方法,前后两个节点紧挨着一步一步往后走,如果找到对应删除节点就让删除的节点的前一个节点的next域跳两步,最后完成遍历后返回虚拟头节点的next域(这才是新的头节点)

代码:

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head==null) {
            return null;
        }
        ListNode dummyNode=new ListNode();
        dummyNode.next=head;
        ListNode ahead=dummyNode;
        ListNode cur=head;
        while(cur!=null) {
            if(cur.val==val) {
                ahead.next=cur.next;
                cur=cur.next;
            }else {
                cur=cur.next;
                ahead=ahead.next;
            }
        }
        return dummyNode.next;
    }
}

707. 设计链表

力扣题目链接

题意:

在链表类中实现这些功能:

get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

思路:
具体思路在代码注释中体现。

代码:

//定义节点
class Node {
    int val;
    Node next;
    public Node(int val) {
        this.val=val;
    }
}
class MyLinkedList {
    int val;
    Node head;
    public MyLinkedList() {
    }
    
    //该方法用于返回链表长度
  	private int size() {
        int count=0;
        Node cur=head;
        while(cur!=null) {
            count++;
            cur=cur.next;
        }
        return count;
    }
    //get(index)
    public int get(int index) {
    //如果下标不合法返回-1
        if(index<0||index>=size())
            return -1;
        Node cur=this.head;
        int count=0;
        while(count!=index) {
            cur=cur.next;
            count++;
        }
        return cur.val;
    }
    //头插
    public void addAtHead(int val) {
        Node node=new Node(val);
        if(head==null) {
            head=node;
            return;
        }
        node.next=this.head;
        this.head=node;
    }
    //尾插
    public void addAtTail(int val) {
        Node node=new Node(val);
        if(head==null) {
            head=node;
            return;
        }
        Node cur=this.head;
        while(cur.next!=null) {
            cur=cur.next;
        }
        cur.next=node;
    }
    //返回对应下标的前一个节点
    private Node searchIndex(int index) {
        Node cur=this.head;
        int count=0;
        while (count!=index-1) {
            count++;
            cur=cur.next;
        }
        return cur;
    }
    //指定下标插入
    public void addAtIndex(int index, int val) {
   		 //下标<=0 头插
        if(index<=0) {
            addAtHead(val);
            return;
        }
        //下标等于链表长度 尾插
        if(index==size()) {
            addAtTail(val);
            return;
        }
        //下标大于链表长度 直接返回
        if(index>size())
            return;
        //找到指定下标的前一个节点,插入新的节点
        Node cur=searchIndex(index);
        Node node=new Node(val);
        //新节点后后边节点连接
        node.next=cur.next;
        //前一个节点和新节点连接
        cur.next=node;
    }
    //删除指定位置节点
    public void deleteAtIndex(int index) {
    	//不合法直接返回	
        if (index < 0 || index >= size()) {
            return;
        }
        //下标为0删除头节点
        if(index==0) {
            head=head.next;
        }else {
        //其他情况还是找到删除节点的前一个节点,让其next域跳两次完成删除
            Node cur=searchIndex(index);
            cur.next=cur.next.next;
        }
    }
}

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */

206.反转链表

力扣题目链接
题意:反转一个单链表。

示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL

思路:

LeetCode链表集锦_第3张图片
完成倒置,就需要将每个节点的next域指向前一个节点,但如果发生改变就找不到后一个节点了,所以需要找一个临时节点进行存储,然后再进行修改。

代码:
双指针:

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null||head.next==null)
        return head;
        //前一个节点
        ListNode prev=null;
        //反转节点
        ListNode cur=head;
        //临时节点,用于存储cur节点的下一个节点
        ListNode curNext=null;
        while(cur!=null) {
        	//临时节点先进行存储
            curNext=cur.next;
            //当前节点的next指向前一个节点
            cur.next=prev;
            //前一个节点后移
            prev=cur;
            //当前节点后移
            cur=curNext;
        }
        //最后全部反转完成后,prev节点就是反转后链表的头节点
        return prev;
    }
}

递归:

class Solution {
    public ListNode reverseList(ListNode head) {
    	//传递的两个参数就是需要反转的两个节点
        return reverse(null,head);
    }
    private ListNode reverse(ListNode prev,ListNode cur) {
        if(cur==null)
        return prev;
        ListNode curNext=cur.next;
        cur.next=prev;
        prev=cur;
        cur=curNext;
        //完成后移以后递归新的反转节点
        return reverse(prev,cur);
    }
}

24. 两两交换链表中的节点

力扣题目链接
题意
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

LeetCode链表集锦_第4张图片
思路:
LeetCode链表集锦_第5张图片

代码:

class Solution {
    public ListNode swapPairs(ListNode head) {
        ListNode dummyNode=new ListNode(0);
        dummyNode.next=head;
        ListNode cur=dummyNode;
        //需要交换这两个节点的顺序,所以这两个节点都不能为空
        while(cur.next!=null&&cur.next.next!=null) {
        	//先对这两个节点地址进行存储
            ListNode node1=cur.next;
            ListNode node2=cur.next.next;
            cur.next=node2;
            node1.next=node2.next;
            node2.next=node1;
            //最后把cur节点移到下一次两个交换节点的前一个节点
            cur=node1;
        }
        return dummyNode.next;
    }
}

19. 删除链表的倒数第 N 个结点

力扣题目链接
题意
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
LeetCode链表集锦_第6张图片
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

思路:
快慢指针,快指针先走一定步数,然后快慢指针一起走,当快指针走出链表为null时,慢指针走到倒数第n+1个节点的位置,让其next域跳两步完成删除

代码:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy=new ListNode(-1);
        dummy.next=head;
        ListNode slow=dummy;
        ListNode fast=dummy;
        while(n+1>0) {
            fast=fast.next;
            n--;
        }
        while(fast!=null) {
            slow=slow.next;
            fast=fast.next;
        }
        slow.next=slow.next.next;
        return dummy.next;
    }
}

面试题 02.07. 链表相交

力扣题目链接
题意
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:
LeetCode链表集锦_第7张图片

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1:
LeetCode链表集锦_第8张图片

示例 2:
LeetCode链表集锦_第9张图片

示例 3:

LeetCode链表集锦_第10张图片

思路:
先对两个链表都完成一次遍历,计算其链表长度,让长的链表头先走两链表之差步,然后两个指针再一起走,第一次next域相同时就是相交处,如果没有相同一直走到空,说明不相交。

代码:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int countA=count(headA);
        int countB=count(headB);
        int a=countA-countB;
        ListNode curA=new ListNode(0);
        curA.next=headA;
        ListNode curB=new ListNode(-1);
        curB.next=headB;
        if(a>0) {
            while(a-->0)
            curA=curA.next;
        }else {
            a=-a;
            while(a-->0)
            curB=curB.next;
        }
        while(curA!=null&&curB!=null) {
            if(curA.next==curB.next) {
                return curA.next;
            }
            curA=curA.next;
            curB=curB.next;
        }
        return null;
    }
    private int count(ListNode head) {
        ListNode cur=head;
        int count=0;
        while(cur!=null) {
            count++;
            cur=cur.next;
        }
        return count;
    }
}

142. 环形链表 II

力扣题目链接
题意
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

LeetCode链表集锦_第11张图片
思路:

做这道题需要考虑两个事情:

  • 链表是否有环
  • 环的入口在哪里

1、判断有环
定义快慢指针从链表头开始走,快指针每次走两步,慢指针每次走一步,如果有指针走出去为null则一定无环,如果有环则快慢指针一定会相遇。
为什么如果有环快慢指针一定会相遇呢?
快指针先入环,无论慢指针入环时快指针在环的哪个位置,两者的相对速度差一,快指针每次都会靠近慢指针一步,一定会相遇。

2.寻找环的入口
LeetCode链表集锦_第12张图片
快指针是慢指针速度的两倍,所以慢指针走一圈快指针就会走两圈,又因为由1中分析,两者以相对速度为一的速度靠近,则一定不会错过(一定相遇),所以慢指针入环后,走不完第一圈就一定会被快指针赶上(快指针也走不完两圈)。
slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A,由此可以得出两者的路程关系等式。

2*(x+y)=x+y+n(y+z)

又由前边分析得,慢指针走不完一圈会被快指针追上,则快指针也走不完两圈,所以n为1
由此得x=z。
这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

代码:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head==null||head.next==null)
        return null;
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null) {
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow) {
                fast=head;
                while(fast!=slow) {
                    fast=fast.next;
                    slow=slow.next;
                }
                return fast;
            }
        }
        return null;
    }
}

导图总结

LeetCode链表集锦_第13张图片


⚡️最后的话⚡️

总结不易,希望uu们不要吝啬你们的哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正
在这里插入图片描述

你可能感兴趣的:(LeetCode刷题集锦,链表,leetcode,数据结构)