⭐️前言⭐️
本篇文章是博主按照代码随想录的链表部分力扣题的一些见解,有一些自己的想法与代码随想录题解结合,能使这部分题理解的更加透彻。
博客主页: 【如风暖阳】
精品Java专栏【JavaSE】、【备战蓝桥】、【JavaEE初阶】、【MySQL】、【数据结构】
欢迎点赞 收藏 ⭐留言评论 私信必回哟本文由 【如风暖阳】 原创,首发于 CSDN
博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言
博客中涉及源码及博主日常练习代码均已上传码云(gitee)、GitHub
力扣题目链接
题意:删除链表中等于给定值 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
输出:[]
思路:
设置虚拟头节点,让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;
}
}
力扣题目链接
题意:
在链表类中实现这些功能:
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);
*/
力扣题目链接
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
思路:
完成倒置,就需要将每个节点的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);
}
}
力扣题目链接
题意:
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
代码:
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;
}
}
力扣题目链接
题意:
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
输入: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;
}
}
力扣题目链接
题意:
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
示例 3:
思路:
先对两个链表都完成一次遍历,计算其链表长度,让长的链表头先走两链表之差步,然后两个指针再一起走,第一次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;
}
}
力扣题目链接
题意:
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
做这道题需要考虑两个事情:
1、判断有环
定义快慢指针从链表头开始走,快指针每次走两步,慢指针每次走一步,如果有指针走出去为null则一定无环,如果有环则快慢指针一定会相遇。
为什么如果有环快慢指针一定会相遇呢?
快指针先入环,无论慢指针入环时快指针在环的哪个位置,两者的相对速度差一,快指针每次都会靠近慢指针一步,一定会相遇。
2.寻找环的入口
快指针是慢指针速度的两倍,所以慢指针走一圈快指针就会走两圈,又因为由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;
}
}
⚡️最后的话⚡️
总结不易,希望uu们不要吝啬你们的哟(^U^)ノ~YO!!如有问题,欢迎评论区批评指正