24. 两两交换链表中的节点
思路:
- 头节点要更改,加入虚拟头节点
- 在变换中,三个节点参与变化,第一个节点位置不变,指向第三个节点,第三个节点指向第二个节点,第二个节点指向原第三个节点的下个节点,~~交换完成后三个节点整体右移,~~第一个节点变为第二个节点所在位置(只需要移动第一个节点即可,在循环体前面得到下两个节点),
后面两个节点为新的节点 - 循环结束条件(不太清楚):第一个节点的下一个节点和下下个节点为空(fristNode.next!=null && fristNode.next.next!=null)
-
Java代码
- 三个节点参与
class Solution { public ListNode swapPairs(ListNode head) { if (head==null || head.next==null) return head; ListNode dummy = new ListNode(); dummy.next=head; // ListNode cur=dummy, fristNode=head, secondNode=head.next; ListNode cur=dummy, fristNode, secondNode; // while (secondNode!=null && secondNode.next!=null && secondNode.next.next!=null) { while (cur.next!=null && cur.next.next!=null) { fristNode = cur.next; secondNode = cur.next.next; ListNode temp = secondNode.next; cur.next=secondNode; secondNode.next=fristNode; fristNode.next=temp; // 要管单次循环体中的完整交换,不然无法处理循环结束时的情况 cur=fristNode; /*fristNode=temp; secondNode=temp.next;*/ // 只需要移动第一个节点即可(不然可能会计算空节点的下一个节点),在前面得到相邻的下两个节点 } return dummy.next; } }
时间复杂度:O(n)
空间复杂度:O(1)- 完整代码
package topic2ListNode; /*24. 两两交换链表中的节点 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 示例 1: 输入:head = [1,2,3,4] 输出:[2,1,4,3] 链接:https://leetcode.cn/problems/swap-nodes-in-pairs/*/ public class topic4_24 { public static void main(String[] args) { Solution solution = new topic4_24().new Solution(); int[] arr = {1,2,3,4}; ListNode head = new ListNode(arr); head.print(); ListNode newHead = solution.swapPairs(head); newHead.print(); } class Solution { public ListNode swapPairs(ListNode head) { if (head==null || head.next==null) return head; ListNode dummy = new ListNode(); dummy.next=head; // ListNode cur=dummy, fristNode=head, secondNode=head.next; ListNode cur=dummy, fristNode, secondNode; // while (secondNode!=null && secondNode.next!=null && secondNode.next.next!=null) { while (cur.next!=null && cur.next.next!=null) { fristNode = cur.next; secondNode = cur.next.next; ListNode temp = secondNode.next; cur.next=secondNode; secondNode.next=fristNode; fristNode.next=temp; // 要管单次循环体中的完整交换,不然无法处理循环结束时的情况 cur=fristNode; /*fristNode=temp; secondNode=temp.next;*/ // 只需要移动第一个节点即可(不然可能会计算空节点的下一个节点),在前面得到相邻的下两个节点 } return dummy.next; } } }
19.删除链表的倒数第N个节点
思路:
- 找到要删除节点(cur)的前一个节点(pre),前一个节点指向下下个节点(pre.next=cur.next)
- 循环终止条件:前一个节点的下 n+1 个节点为空(找到要删除节点的前一个节点,要删除节点的下n个节点为空)
额外的补充: - 添加虚拟头节点(因为可以对头节点进行操作)
-
Java代码
- 双指针法
class Solution { public ListNode removeNthFromEnd(ListNode head, int n) { ListNode dummy = new ListNode(); dummy.next = head; ListNode cur = dummy, pre = dummy; while (n-- >= 0){ cur=cur.next; } while (cur!=null) { cur=cur.next; pre=pre.next; } pre.next=pre.next.next; return dummy.next; } }
时间复杂度:O(n) 实际是O(2n-index)
空间复杂度:O(1)- 完整代码
package topic2ListNode; /*19. 删除链表的倒数第 N 个结点 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 示例 1: 输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5] 链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/*/ public class topic5_19 { public static void main(String[] args) { Solution solution = new topic5_19().new Solution(); int[] arr = {1, 2, 3, 4, 5}; int n = 2; ListNode head = new ListNode(arr); head.print(); System.out.println("要删除倒数第" + n + "个节点"); ListNode newHead = solution.removeNthFromEnd(head, n); newHead.print(); } class Solution { public ListNode removeNthFromEnd(ListNode head, int n) { ListNode dummy = new ListNode(); dummy.next = head; ListNode cur = dummy, pre = dummy; while (n-- >= 0) { cur = cur.next; } while (cur != null) { cur = cur.next; pre = pre.next; } pre.next = pre.next.next; return dummy.next; } } }
面试题 02.07. 链表相交
思路:
- 定义两个从头开始的节点(curA和curB),分别沿着各自的链表进行遍历,并进行判断是否相同
- 当两个运动的节点到达各自的终点后(不同时为null),分别从对方的起点开始运动
- 循环终止条件:两个运动节点相同(同时为空或者是达到相交节点,curA!=curB)
原理:
A链表长度为n,B链表长度为m,curA节点运动长度为 n+m,curB节点运动长度为 m+n
-
Java代码
- 双指针
public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { ListNode curA=headA, curB=headB; while (curA!=curB) { curA = curA!=null ? curA.next : headB; curB = curB!=null ? curB.next : headA; } return curA; } }
时间复杂度:O(n) 实际是O(2(m+n))
空间复杂度:O(1)- 完整代码
package topic2ListNode; /*面试题 02.07. 链表相交 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。 图示两个链表在节点 c1 开始相交: 题目数据 保证 整个链式结构中不存在环。 注意,函数返回结果后,链表必须 保持其原始结构 。 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 示例 1: 输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Intersected at '8' 解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。 从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。 在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。*/ public class topic6_0207 { public static void main(String[] args) { Solution solution = new topic6_0207().new Solution(); int[] arrA = {4,1}; int[] arrB = {5,0}; int[] arrIntersect = {8,4,5}; ListNode headA = new ListNode(arrA); ListNode headB = new ListNode(arrB); ListNode headIntersect = new ListNode(arrIntersect); headA.addListNode(headIntersect); headB.addListNode(headIntersect); headA.print(); headB.print(); ListNode intersectNode = solution.getIntersectionNode(headA, headB); intersectNode.print(); } public class Solution { public ListNode getIntersectionNode(ListNode headA, ListNode headB) { ListNode curA=headA, curB=headB; while (curA!=curB) { curA = curA!=null ? curA.next : headB; curB = curB!=null ? curB.next : headA; } return curA; } } }
142.环形链表II
思路:
- 使用快慢指针找到链表是否相交(快指针每次移动2个长度,慢指针每次移动1个长度)
- 若未找到(快指针为null),循环结束条件:快指针为空,并且快指针的下个节点为空(fast!=null && fast.next!=null)
- 找到后根据移动关系(x=y+(n-1)(y+z)),重新制定两个指定,相交处即是链表的环入口
具体细节过程细节
-
Java代码
- 双指针
public class Solution { public ListNode detectCycle(ListNode head) { if (head==null) {return null;} ListNode dummy = new ListNode(); dummy.next = head; ListNode fast=dummy, slow=dummy; while (fast!=null && fast.next!=null) { slow=slow.next; fast=fast.next.next; if (slow==fast) { ListNode index1 = fast; ListNode index2 = dummy; while (index1!=index2) { index1=index1.next; index2=index2.next; } return index1; } } return null; } }
时间复杂度:O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n
空间复杂度:O(1)- 完整代码
package topic2ListNode; /*142. 环形链表 II 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。 不允许修改 链表。 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/linked-list-cycle-ii 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 示例 1: 输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。*/ public class topic7_142 { public static void main(String[] args) { Solution solution = new topic7_142().new Solution(); int[] arr = {3,2,0,-4}; int index = 1; ListNode head = new ListNode(arr); head.print(); System.out.println("index: "+index); head.createCircle(1); ListNode cycleNode = solution.detectCycle(head); System.out.println(cycleNode.val); } public class Solution { public ListNode detectCycle(ListNode head) { if (head==null) {return null;} ListNode dummy = new ListNode(); dummy.next = head; ListNode fast=dummy, slow=dummy; while (fast!=null && fast.next!=null) { slow=slow.next; fast=fast.next.next; if (slow==fast) { ListNode index1 = fast; ListNode index2 = dummy; while (index1!=index2) { index1=index1.next; index2=index2.next; } return index1; } } return null; } } }