24.两两交换单链表节点(双指针)
package com.liqi.day.day4;
import com.liqi.day.day3.ListNode;
public class LeetCode_24 { //给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点 你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)
public static void main(String[] args) {
}
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode(0, head);
ListNode temp;
ListNode firstNode;
ListNode secondNode;
ListNode cur = dummy;// cur一定得是第一个节点的前一个节点,不然无法修改
while (cur.next != null && cur.next.next != null) {
// 循环终止条件为cur后面不在有两两成对的节点
temp = cur.next.next.next;
firstNode = cur.next;
secondNode = cur.next.next;
cur.next = secondNode;
secondNode.next = firstNode;
firstNode.next = temp;
cur = firstNode; //todo 第一次写的时候忘记每次循环的变量了,即cur要移动
}
return dummy.next; //这里不能return head 因为使用了虚拟头节点,虚拟头节点的next才是链表真正的head
//return head; 如果当我们的操作改变了原来的head的话得到的结果就是错误的
}
}
}
总结:使用虚拟头节点记得返回的是虚拟头节点的next而不是原来的head,原来的head可能会被修改,二刷要做一下递归的解法
19.删除单链表的倒数第N个节点
package com.liqi.day.day4;
import com.liqi.day.day3.ListNode;
public class LeetCode_19 { //19.删除链表的倒数第N个节点 todo 1 <= n <= size
public static void main(String[] args) {
}
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//给你一个单链表,要求删除链表中的倒数第N个节点并返回
//思路:双指针,快指针遍历到尾节点,慢指针比快指针慢走N个节点(因为删除倒数第N个节点要拿到它的前一个节点)
//这里涉及到删除节点,为了统一我们仍然是使用虚拟头节点
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode cur = dummy;
ListNode slow = dummy;
//因为题目已经给出条件 1<=n<=size 所以直接for循环先让快指针比慢指针多走n个节点
for (int i = 0; i < n; i++) { //这里是<n 不是<=n 因为<=n 意思就是快指针比慢指针多走n+1步了
cur = cur.next;
}
while (cur.next != null) {
cur = cur.next;
slow = slow.next;
}
//while循环退出时slow指向的下个节点就是我们要删除的节点
slow.next = slow.next.next;
return dummy.next;//返回链表的头节点
}
}
}
总结:判断清楚快指针多走的步数是n-1 还是n还是n+1即可
160.单链表相交,求起始相交节点
package com.liqi.day.day4;
import com.liqi.day.day3.ListNode;
public class LeetCode_160 { //160.链表相交, 给你两个单链表的头节点 headA 和 headB ,
// 请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
// 题目数据 保证 整个链式结构中不存在环。
//注意,函数返回结果后,链表必须 保持其原始结构
public static void main(String[] args) {
}
public static class Solution {
public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
//目标:时间复杂度o(n) 空间复杂度:o(1) 思路 使用双指针(一个指向链表A 一个指向链表B) 单循环遍历
//因为不涉及链表节点的位置或者结构变动,所以不需要使用虚拟头节点
ListNode curA = headA;
ListNode curB = headB;
//第一遍自己做的时候老是想着如果先分别遍历链表A和B求出长度然后再遍历寻找想交起始节点时间复杂度就不是o(n)了
//但其实没有嵌套在一起的多个循环最终的时间复杂度仍然是o(N)
//先分别求出链表A和B的长度
int sizeA = 1;
int sizeB = 1;
while (curA.next != null) {
curA=curA.next;
sizeA++;
}
while (curB.next != null) {
curB=curB.next;
sizeB++;
}
curA=headA;
curB=headB;
//让curA指向两个链表中长度大的那一个,
// todo 回归老韩过关斩将思想,所有符合条件的情况等于排除不符合条件的特殊情况(符合条件的情况不好找的时候就用过关斩将法)
if (sizeB > sizeA) {
int tempSize=sizeB;
sizeB=sizeA;
sizeA=tempSize;
ListNode temp=curB;
curB=curA;
curA=temp;
}
int gap=sizeA-sizeB;
for(int i=0;i<gap;i++){
curA=curA.next; //让两个链表的起点到终点的长度相等
}
while (curA!=null) {//同时移动两个指针并比较内存地址是否相等
if (curA == curB) {//注意这里的交点指的是节点的内存地址相同而不是单纯值相同
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
}