题目链接:24.两两交换链表中的结点
思路:此题通过观看卡老师的视频讲解,理解的很透彻,直接上链接,下面我把观看视频做的笔记放出来
笔记:
代码如下:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyhead=new ListNode(0);//设置一个虚拟头结点
dummyhead.next=head;//将虚拟头结点指向head,这样方面后面做的删除操作
ListNode cur=dummyhead;//初始化
//临时结点,保存两个结点后面的结点(例如2、4……)
ListNode temp;
ListNode temp1;
//循环条件里面的先后顺序不能变,否则会发生空指针异常
while(cur.next!=null&&cur.next.next!=null){
temp=cur.next;
temp1=cur.next.next.next;
cur.next=cur.next.next;
cur.next.next=temp;
temp.next=temp1;
cur=cur.next.next;
}
return dummyhead.next;
}
}
时间复杂度:O(n)
空间复杂度:O(1)
题目链接:19.删除链表的倒数第N个节点
思路:本题的难点在于如何找到倒数第n个节点(待删除元素) 的前一个节点的位置,通过观看卡老师的视频,可以:
1、首先定义虚拟头结点,然后定义fast指针和slow指针指向虚拟头结点
2、fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作)
3、fast和slow同时移动,直到fast指向null。此时慢指针所在的位置便是待删除元素的前一个
4、删除slow指向的下一个节点
代码如下:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//首先定义一个虚拟头节点
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
//然后给其初始化,定义两个快慢指针指向虚拟头节点,定义其目的是为了方便找到倒数第n个节点的前一个节点的那个位置,因为前一个节点才能对后面要删除的节点进行操作
ListNode fastIndex = dummyHead;
ListNode slowIndex = dummyHead;
//核心思路就是题目给定倒数第n个节点,然后让快指针先移动n+1个节点,然后再同时移动快慢指针直到快指针指向Null,此时,慢指针所在的位置就是倒数第n个节点前一个节点的位置
for(int i=0; i<=n; i++){
fastIndex=fastIndex.next;
}
//上一步快指针已经到了n+1的位置了,下一步就需要同时移动快慢指针了,将慢指针移动到指定位置
while(fastIndex!=null){
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}
//此时slowIndex的位置就是待删除元素的前一个位置,接下来进行对倒数第n个元素删除的操作
slowIndex.next = slowIndex.next.next;
return dummyHead.next;
}
}
题目链接:力扣面试题02.07.链表相交
思路:简单来说,就是求两个链表交点节点的指针, 这里要注意,交点不是数值相等,而是指针相等。 因为一个节点不可能有两个next指针。这里和val值没啥直接关系,两个val相同不一定就相交,但是如果相交,那么从这个相交节包括之后的所有节点val值都是相同的。
1、首先有两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点
2、我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 对齐的位置
3、此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB(⚠️注意,实际比较的是指针相同,不是val),则找到交点
代码如下:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
int lenA = 0, lenB = 0;
while (curA != null) {
// 求链表A的长度
lenA++;
curA = curA.next;
}
while (curB != null) {
// 求链表B的长度
lenB++;
curB = curB.next;
}
//为什么还要初始化一次?因为前面让curA和B去遍历了一遍链表统计长度 结束之后它们走到空指针去了,所以要把它们放回链表头去执行后面的下一次遍历
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
//1. swap (lenA, lenB);
int tmpLen = lenA;
lenA = lenB;
lenB = tmpLen;
//2. swap (curA, curB);
ListNode tmpNode = curA;
curA = curB;
curB = tmpNode;
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap-- > 0) {
curA = curA.next;
}
// 遍历curA 和 curB,遇到相同则直接返回(首先要知道如果相交,一个节点不可能有两个next指针。这里和val值没啥直接关系,两个val相同不一定就相交,但是如果相交,那么从这个相交节包括之后的所有节点val值都是相同的)
while (curA != null) {
if (curA == curB) {
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
}
题目链接:142.环形链表II
思路:建议观看卡老师的视频,下面是观看视频做的笔记:
主要考察两知识点:
- 判断链表是否环
- 如果有环,如何找到这个环的入口
1、判断链表是否有环
可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环
2、如果有环,如何找到这个环的入口
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。正如上图笔记中:
因为上述推断出x=z, 这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
代码如下:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast=head;
ListNode slow=head;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
//如果相等,证明有环,接下来要判断入环的第一个节点,因为在卡老师的视频里讲过(看个人笔记),x=z,即从相遇的那个节点的位置到环入口位置的这段距离和从头节点head到环入口的这段距离是相等的,接下来创建两个指针,一个指向头节点,一个指向相遇的位置(此时fast/slow的位置),让这两个指针不断向后移动,直到两者相等,也就是入环的那个节点的位置(此时index1/index2的位置)
ListNode index1=fast;
ListNode index2=head;
// 两个指针,从头节点和相遇节点,各走一步,直到相遇。相遇点即为环入口
while(index1!=index2){
index1=index1.next;
index2=index2.next;
}
return index1;
}
}
return null;
}
}
时间复杂度: O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n
空间复杂度: O(1)