diff --git a/Leecode/src/main/java/com/markilue/leecode/listnode/swapPairs.java b/Leecode/src/main/java/com/markilue/leecode/listnode/T04_swapPairs.java similarity index 98% rename from Leecode/src/main/java/com/markilue/leecode/listnode/swapPairs.java rename to Leecode/src/main/java/com/markilue/leecode/listnode/T04_swapPairs.java index 8515530..9af3e90 100644 --- a/Leecode/src/main/java/com/markilue/leecode/listnode/swapPairs.java +++ b/Leecode/src/main/java/com/markilue/leecode/listnode/T04_swapPairs.java @@ -2,7 +2,7 @@ package com.markilue.leecode.listnode; -public class swapPairs { +public class T04_swapPairs { public static void main(String[] args) { diff --git a/Leecode/src/main/java/com/markilue/leecode/listnode/removeNthFromEnd.java b/Leecode/src/main/java/com/markilue/leecode/listnode/T05_removeNthFromEnd.java similarity index 99% rename from Leecode/src/main/java/com/markilue/leecode/listnode/removeNthFromEnd.java rename to Leecode/src/main/java/com/markilue/leecode/listnode/T05_removeNthFromEnd.java index 72f4376..f00e456 100644 --- a/Leecode/src/main/java/com/markilue/leecode/listnode/removeNthFromEnd.java +++ b/Leecode/src/main/java/com/markilue/leecode/listnode/T05_removeNthFromEnd.java @@ -3,7 +3,7 @@ package com.markilue.leecode.listnode; /** * 删除链表的倒数第N个节点 */ -public class removeNthFromEnd { +public class T05_removeNthFromEnd { public static void main(String[] args) { diff --git a/Leecode/src/main/java/com/markilue/leecode/listnode/T06_GetIntersectionNode.java b/Leecode/src/main/java/com/markilue/leecode/listnode/T06_GetIntersectionNode.java new file mode 100644 index 0000000..74cbb85 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/listnode/T06_GetIntersectionNode.java @@ -0,0 +1,217 @@ +package com.markilue.leecode.listnode; + +import java.util.HashSet; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.listnode + *@Author: dingjiawen + *@CreateTime: 2022-12-26 11:09 + *@Description: + * TODO 力扣160题 相交链表: + * 给你两个单链表的头节点 headA 和 headB , + * 请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。 + *@Version: 1.0 + */ +public class T06_GetIntersectionNode { + + /** + * 思路:核心在于如何判断两个链表相交了,显然不能通过两个数的值是否相等 + * 核心是内存,所以用== ? + * 使用一个hashset装headA,遍历headB? 时间复杂度O(n) + * 速度击败6.13%,内存击败96.59% 8ms + * @param headA + * @param headB + * @return + */ + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + ListNode tempA = headA; + ListNode tempB = headB; + HashSet set = new HashSet<>(); + while (tempA != null) { + set.add(tempA); + tempA = tempA.next; + } + while (tempB != null) { + boolean flag = set.contains(tempB); + if (flag) { + return tempB; + } + tempB = tempB.next; + } + return null; + } + + + /** + * 代码随想录思路:核心在于如何判断两个链表相交了,显然不能通过两个数的值是否相等 + * 分别计算出两个链表的长度,如果两个链表相同那么他们的长度应该相同 + * 因此把两个链表的尾端对齐,从短链表头开始遍历 + * 速度击败97.96%,内存击败22.84% 1ms + * @param headA + * @param headB + * @return + */ + public ListNode getIntersectionNode1(ListNode headA, ListNode headB) { + ListNode tempA = headA; + ListNode tempB = headB; + int lengthA = 0; + int lengthB = 0; + + //计算A长度 + while (tempA != null) { + lengthA++; + tempA = tempA.next; + } + //计算B长度 + while (tempB != null) { + lengthB++; + tempB = tempB.next; + } + + //让A成为最长的 + ListNode curA = headA; + ListNode curB = headB; + if (lengthA < lengthB) { + ListNode temp = curA; + curA = curB; + curB = temp; + int len = lengthA; + lengthA = lengthB; + lengthB = len; + } + + //遍历到两个头 + int len = lengthA - lengthB; + while (len-- > 0) { + curA = curA.next; + } + + //开始查找 + while (curA != null && curA != curB) { + curA = curA.next; + curB = curB.next; + } + return curA; + } + + + /** + * 官方题解中最快:快在不用交换 + * 先求俩链表的长度,然后将指针pA和pB移动到相同长度的位置 + * 判断两个指针的节点是否相同,如果相同则返回; + * 否则从此相同起点往后移动并判断俩几点是否相同 + * 速度击败100% 0ms + */ + public ListNode getIntersectionNode2(ListNode headA, ListNode headB) { + int m = 0, n = 0; + ListNode pA = headA, pB = headB; + for(; pA != null; m++){ + pA = pA.next; + } + for(; pB != null; n++){ + pB = pB.next; + } + + pA = headA; pB = headB; + // 如果是链表A长了就往后移动m-n个位置 + if(m > n){ + for(int i = 0; i < m - n; i++){ + pA = pA.next; + } + // 如果链表B长了就往后移动n-m个位置 + }else if(m < n){ + for(int i = 0; i < n - m; i++){ + pB = pB.next; + } + } + + // 判断相同起点的俩节点是否相同 + if(pA == pB){ + return pA; + } + + // 否则同时向后移动并判断 + ListNode ret = null; + while(pA != null && pB != null){ + pA = pA.next; + pB = pB.next; + if(pA == pB){ + ret = pA; + break; + } + + } + + return ret; + } + + + public ListNode getIntersectionNode3(ListNode headA, ListNode headB) { + ListNode tempA = headA; + ListNode tempB = headB; + int lengthA = 0; + int lengthB = 0; + + //计算A长度 + while (tempA != null) { + lengthA++; + tempA = tempA.next; + } + //计算B长度 + while (tempB != null) { + lengthB++; + tempB = tempB.next; + } + + //让A成为最长的 + ListNode curA = headA; + ListNode curB = headB; + if (lengthA < lengthB) { + int len=lengthB-lengthA; + for (int i = 0; i < len; i++) { + curB=curB.next; + } + }else { + int len=lengthA-lengthB; + for (int i = 0; i < len; i++) { + curA=curA.next; + } + } + //开始查找 + while (curA != null && curA != curB) { + curA = curA.next; + curB = curB.next; + } + return curA; + } + + + /** + * 官方题解法:双指针法,本质上和代码随想录方法一致 + * 只有当链表 headA和 headB都不为空时,两个链表才可能相交。 + * 因此首先判断链表 headA和 headB是否为空,如果其中至少有一个链表为空,则两个链表一定不相交,返回 null + * 当链表 headA和 headB都不为空时,创建两个指针 pA和 pB,初始时分别指向两个链表的头节点 headA和 headB,然后将两个指针依次遍历两个链表的每个节点。具体做法如下: + * 每步操作需要同时更新指针 pA{pA}pA 和 pB{pB}pB。 + * 如果指针 pA 不为空,则将指针 pA移到下一个节点;如果指针 pB不为空,则将指针 pB移到下一个节点。 + * 如果指针 pA为空,则将指针 pA移到链表 headB的头节点;如果指针 pB为空,则将指针 pB移到链表 headA的头节点。 + * 当指针 pA和 pB指向同一个节点或者都为空时,返回它们指向的节点或者 null。 + * 速度击败97.96% 内存击败9.31% 1ms + * @param headA + * @param headB + * @return + */ + public ListNode getIntersectionNode4(ListNode headA, ListNode headB) { + if (headA == null || headB == null) { + return null; + } + ListNode pA = headA, pB = headB; + //本质上和代码随想录是一样的,先求长度再遍历到差值,然后从短链表头开始遍历 + while (pA != pB) { + pA = pA == null ? headB : pA.next; + pB = pB == null ? headA : pB.next; + } + return pA; + } + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/listnode/DetectCycle.java b/Leecode/src/main/java/com/markilue/leecode/listnode/T07_DetectCycle.java similarity index 98% rename from Leecode/src/main/java/com/markilue/leecode/listnode/DetectCycle.java rename to Leecode/src/main/java/com/markilue/leecode/listnode/T07_DetectCycle.java index 640cba7..f107d07 100644 --- a/Leecode/src/main/java/com/markilue/leecode/listnode/DetectCycle.java +++ b/Leecode/src/main/java/com/markilue/leecode/listnode/T07_DetectCycle.java @@ -19,7 +19,7 @@ import java.util.HashSet; * @Version: 1.0 */ -public class DetectCycle { +public class T07_DetectCycle { @Test diff --git a/Leecode/src/main/java/com/markilue/leecode/listnode/second/T04_SwapPairs.java b/Leecode/src/main/java/com/markilue/leecode/listnode/second/T04_SwapPairs.java new file mode 100644 index 0000000..a26fb75 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/listnode/second/T04_SwapPairs.java @@ -0,0 +1,112 @@ +package com.markilue.leecode.listnode.second; + +import com.markilue.leecode.listnode.ListNode; +import com.markilue.leecode.listnode.ListNodeUtils; +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.listnode.second + *@Author: dingjiawen + *@CreateTime: 2022-12-26 09:50 + *@Description: + * TODO 二刷力扣24题 两两交换链表中的节点: + * 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。 + * 你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 + *@Version: 1.0 + */ +public class T04_SwapPairs { + + @Test + public void test(){ + int[] head={1,2,3,4}; + ListNode root = ListNodeUtils.build(head); + ListNodeUtils.print(swapPairs(root)); + } + + @Test + public void tes1t(){ + int[] head={1}; + ListNode root = ListNodeUtils.build(head); + ListNodeUtils.print(swapPairs(root)); + } + + /** + * 自己迭代法思路:速度击败100%,内存击败53.44% + * @param head + * @return + */ + public ListNode swapPairs(ListNode head) { + + if (head == null || head.next == null) { + return head; + } + + ListNode root = new ListNode(); + root.next = head; + ListNode temp = root; + while (temp.next != null) { + //以1,2,3,4为例 + if(temp.next.next!=null){ + //记录2,3,4 + ListNode temp1=temp.next.next; + //1,3,4 + temp.next.next=temp.next.next.next; + //2,1,3,4 + temp1.next=temp.next; + //root,2,1,3,4 + temp.next=temp1; + //后移继续遍历 + temp=temp.next.next; + }else { + temp=temp.next; + } + } + return root.next; + } + + + /** + * 官方迭代法 + * @param head + * @return + */ + public ListNode swapPairs1(ListNode head) { + + ListNode dummyNode = new ListNode(0); + dummyNode.next = head; + ListNode prev = dummyNode; + + while (prev.next != null && prev.next.next != null) { + ListNode temp = head.next.next; // 缓存 next + prev.next = head.next; // 将 prev 的 next 改为 head 的 next + head.next.next = head; // 将 head.next(prev.next) 的next,指向 head + head.next = temp; // 将head 的 next 接上缓存的temp + prev = head; // 步进1位 + head = head.next; // 步进1位 + } + return dummyNode.next; + } + + + /** + * 官方递归法 + * @param head + * @return + */ + public ListNode swapPairs2(ListNode head) { + // base case 退出提交 + if(head == null || head.next == null) return head; + // 获取当前节点的下一个节点 + ListNode next = head.next; + // 进行递归,先解决后面的 + ListNode newNode = swapPairs(next.next); + // 这里进行交换 + next.next = head; + head.next = newNode; + + return next; + } + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/listnode/second/T05_RemoveNthFromEnd.java b/Leecode/src/main/java/com/markilue/leecode/listnode/second/T05_RemoveNthFromEnd.java new file mode 100644 index 0000000..68ad784 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/listnode/second/T05_RemoveNthFromEnd.java @@ -0,0 +1,66 @@ +package com.markilue.leecode.listnode.second; + +import com.markilue.leecode.listnode.ListNode; +import com.markilue.leecode.listnode.ListNodeUtils; +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.listnode.second + *@Author: dingjiawen + *@CreateTime: 2022-12-26 10:33 + *@Description: + * TODO 二刷19题 删除链表的倒数第 N 个结点: + * 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点 + *@Version: 1.0 + */ +public class T05_RemoveNthFromEnd { + + @Test + public void test(){ + int[] head={1,2,3,4,5}; + int n = 5; + ListNode root = ListNodeUtils.build(head); + ListNodeUtils.print(removeNthFromEnd(root,n)); + } + + @Test + public void test1(){ + int[] head={1}; + int n = 1; + ListNode root = ListNodeUtils.build(head); + ListNodeUtils.print(removeNthFromEnd(root,n)); + } + + /** + * 自己思路迭代法: + * 双指针:一个比另一个多走n,然后另一个到最末尾的时候,这个刚好到要删的地方 + * 速度击败100%,内存击败95.42% + * @param head + * @param n + * @return + */ + public ListNode removeNthFromEnd(ListNode head, int n) { + //弄个虚拟头结点,方便删除头结点 + ListNode root = new ListNode(); + root.next=head; + ListNode fast=root; + ListNode slow=root; + //往前置一位,找到要删除的前一个 + for (int i = 0; i < n; i++) { + fast=fast.next; + } + + //让快指针走到头 + while (fast.next!=null){ + fast=fast.next; + slow=slow.next; + } + + //慢指针进行删除 + if(slow.next!=null){ + slow.next=slow.next.next; + } + return root.next; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/listnode/second/T07_DetectCycle.java b/Leecode/src/main/java/com/markilue/leecode/listnode/second/T07_DetectCycle.java new file mode 100644 index 0000000..964f7e0 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/listnode/second/T07_DetectCycle.java @@ -0,0 +1,54 @@ +package com.markilue.leecode.listnode.second; + +import com.markilue.leecode.listnode.ListNode; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.listnode.second + *@Author: dingjiawen + *@CreateTime: 2022-12-26 12:36 + *@Description: + * TODO 二刷 力扣142题 环形链表 II: + * 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 + * 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 + * 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 + * 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。 + * 不允许修改 链表。 + *@Version: 1.0 + */ +public class T07_DetectCycle { + + /** + * 思路:快慢指针:一个一次走一步,一个一次走两步,如果有环那么他们一定能相遇: + * 通过推导可知:从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点 + * @param head + * @return + */ + public ListNode detectCycle(ListNode head) { + if(head==null||head.next==null){ + return null; + } + ListNode root = new ListNode(); + root.next=head; + ListNode fast=root; + ListNode slow=root; + + while(fast!=null&&fast.next!=null){ + fast=fast.next.next; + slow=slow.next; + //让两者相遇 + if(fast==slow){ + //从头结点出发一个,再从相遇节点出发一个 + ListNode temp=root; + while (temp!=slow){ + temp=temp.next; + slow=slow.next; + } + return slow; + } + } + //找不到相遇的 + return null; + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/listnode/selftry/reverseKGroup.java b/Leecode/src/main/java/com/markilue/leecode/listnode/selftry/reverseKGroup.java index c05f7d3..d0a52df 100644 --- a/Leecode/src/main/java/com/markilue/leecode/listnode/selftry/reverseKGroup.java +++ b/Leecode/src/main/java/com/markilue/leecode/listnode/selftry/reverseKGroup.java @@ -1,6 +1,6 @@ package com.markilue.leecode.listnode.selftry; -import com.markilue.leecode.listnode.swapPairs; +import com.markilue.leecode.listnode.T04_swapPairs; public class reverseKGroup { @@ -16,7 +16,7 @@ public class reverseKGroup { } public static class ListNode { int val; - swapPairs.ListNode next; + T04_swapPairs.ListNode next; ListNode() { } @@ -25,7 +25,7 @@ public class reverseKGroup { this.val = val; } - ListNode(int val, swapPairs.ListNode next) { + ListNode(int val, T04_swapPairs.ListNode next) { this.val = val; this.next = next; }