diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T56_SortList.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T56_SortList.java new file mode 100644 index 0000000..188243e --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T56_SortList.java @@ -0,0 +1,200 @@ +package com.markilue.leecode.hot100; + +import com.markilue.leecode.listnode.ListNode; +import com.markilue.leecode.listnode.ListNodeUtils; +import org.junit.Test; + +import java.util.List; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-14 09:59 + *@Description: + * TODO 力扣148题 排序链表: + * 给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 + *@Version: 1.0 + */ +public class T56_SortList { + + @Test + public void test() { + int[] head = {4, 2, 1, 3}; + ListNode root = ListNodeUtils.build(head); + ListNode listNode = sortList(root); + ListNodeUtils.print(listNode); + + } + + + /** + * 思路:尝试归并排序 + * 速度击败21.59% 内存击败11.8% 16ms + * @param head + * @return + */ + public ListNode sortList(ListNode head) { + if(head==null){ + return head; + } + + ListNode temp = head; + int length = 0; + while (temp != null) { + length++; + temp = temp.next; + } + return divide(head, 0, length - 1); + } + + public ListNode divide(ListNode root, int start, int end) { + if (start >= end) { + return new ListNode(root.val); + } + int mid = start + ((end - start) >> 1); + int temp = mid - start; + ListNode midNode = root; + //root移动mid位 + while (temp-- >= 0) { + midNode = midNode.next; + } + ListNode node1 = divide(root, start, mid); + ListNode node2 = divide(midNode, mid + 1, end); + return MergeSort(node1, node2); + + + } + + public ListNode MergeSort(ListNode root1, ListNode root2) { + + //两个都不是null,开始归并 + ListNode head = new ListNode(); + ListNode temp = head; + + while (root1 != null && root2 != null) { + if (root1.val < root2.val) { + temp.next = new ListNode(root1.val); + root1 = root1.next; + } else { + temp.next = new ListNode(root2.val); + root2 = root2.next; + } + temp = temp.next; + } + + if (root1 == null) { + temp.next = root2; + } else { + temp.next = root1; + } + + + return head.next; + } + + + /** + * 官方归并排序:使用快慢指针遍历到中间位置 + * 速度击败46.29% 内存击败88.11% 12ms + * @param head + * @return + */ + public ListNode sortList1(ListNode head) { + return sortList(head, null); + } + + public ListNode sortList(ListNode head, ListNode tail) { + if (head == null) { + return head; + } + if (head.next == tail) { + head.next = null; + return head; + } + ListNode slow = head, fast = head; + while (fast != tail) { + slow = slow.next; + fast = fast.next; + if (fast != tail) { + fast = fast.next; + } + } + ListNode mid = slow; + ListNode list1 = sortList(head, mid); + ListNode list2 = sortList(mid, tail); + ListNode sorted = merge(list1, list2); + return sorted; + } + + public ListNode merge(ListNode head1, ListNode head2) { + ListNode dummyHead = new ListNode(0); + ListNode temp = dummyHead, temp1 = head1, temp2 = head2; + while (temp1 != null && temp2 != null) { + if (temp1.val <= temp2.val) { + temp.next = temp1; + temp1 = temp1.next; + } else { + temp.next = temp2; + temp2 = temp2.next; + } + temp = temp.next; + } + if (temp1 != null) { + temp.next = temp1; + } else if (temp2 != null) { + temp.next = temp2; + } + return dummyHead.next; + } + + + /** + * 官方最快:时间复杂度O(N),直接记录各个值的数量,直接根据数量进行构造 + * 速度击败99.74% 内存击败11.48% 3ms + * @param head + * @return + */ + public ListNode sortList2(ListNode head) { + if (head == null) { + return null; + } + int max = head.val; + int min = head.val; + ListNode node = head.next; + + //记录链表中的最大值最小值 + while (node != null) { + int val = node.val; + if (val > max) { + max = val; + } + if (val < min) { + min = val; + } + node = node.next; + } + + //记录每个值的数量 + int[] count = new int[max - min + 1]; + + node = head; + while (node != null) { + count[node.val - min]++; + node = node.next; + } + + //直接根据每个值的数量进行构造 + node = head; + max -= min; + for (int i = 0; i <= max; i++) { + int val = min + i; + while (count[i]-- > 0) { + node.val = val; + node = node.next; + } + } + return head; + } + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T57_MaxProduct.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T57_MaxProduct.java new file mode 100644 index 0000000..f20bba1 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T57_MaxProduct.java @@ -0,0 +1,118 @@ +package com.markilue.leecode.hot100; + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-14 10:59 + *@Description: + * TODO 力扣152题 乘积最大子数组: + * 给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 + * 测试用例的答案是一个 32-位 整数。 + * 子数组 是数组的连续子序列。 + *@Version: 1.0 + */ +public class T57_MaxProduct { + + + @Test + public void test() { + int[] nums = {-2, 3, -4}; + System.out.println(maxProduct(nums)); + } + + /** + * 思路:动态规划法: + * TODO DP五部曲: + * 1.dp定义: dp[i][0]表示不要当前数的最大值 ; dp[i][1]表示要当前数的最大值 + * 2.dp状态转移方程: + * 1.dp[i][0] 不要当前的数 + * dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]) + * 2.dp[i][1] 要当前的数 + * dp[i][1]=nums[i] + * dp[i][2]=dp[i-1][1]*nums[i] + * 3.dp初始化:dp[i][0]=Integer.Min dp[i][1]=nums[i] + * 4.dp遍历顺序: + * 5.dp举例推导:以nums=[2,3,-2,4]为例 + * [2 3 -2 4] + * dp0: m 2 6 6 + * dp1: 2 6 -2 4 + * 尚且存在问题: 当前位置的最优解未必是前一个位置的最优解得出来的 + * 只能过150个用例 [2,-5,-2,-4,3]无法通过 输出20 真实结果24 + * @param nums + * @return + */ + public int maxProduct(int[] nums) { + + int[][] dp = new int[nums.length][3]; + dp[0][0] = Integer.MIN_VALUE; + dp[0][1] = nums[0]; + dp[0][2] = nums[0]; + int result = nums[0]; + + for (int i = 1; i < nums.length; i++) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]); + dp[i][1] = Math.max(dp[i - 1][1] * nums[i], nums[i]); + dp[i][2] = dp[i - 1][2] * nums[i]; + if (result < dp[i][0]) result = dp[i][0]; + if (result < dp[i][1]) result = dp[i][1]; + if (result < dp[i][2]) result = dp[i][2]; + } + + return result; + } + + + /** + * 官方题解: 也是动态规划法 + * 1.根据当前位置的数是整数还是负数来进行判断 + * 如果当前位置为正数 则希望前一个数尽可能的大 + * 如果当前位置为负数 则希望前一个数尽可能的小 + * 所以记录前一个数最大值和最小值 + * @param nums + * @return + */ + public int maxProduct1(int[] nums) { + + int length = nums.length; + int[] maxF = new int[length]; + int[] minF = new int[length]; + System.arraycopy(nums, 0, maxF, 0, length); + System.arraycopy(nums, 0, minF, 0, length); + int result=maxF[0]; + for (int i = 1; i < length; ++i) { + maxF[i] = Math.max(maxF[i - 1] * nums[i], Math.max(nums[i], minF[i - 1] * nums[i])); + minF[i] = Math.min(minF[i - 1] * nums[i], Math.min(nums[i], maxF[i - 1] * nums[i])); + if(result queue; + + public Stack stack; + + + //核心:又要有栈结构,又要在常数时间检索最小 ->一个栈 一个优先队列? + //通过 速度击败29.61% 内存击败41.67% 5ms + public T58_MinStack() { + queue=new PriorityQueue<>(); + stack=new Stack<>(); + } + + public void push(int val) { + stack.push(val); + queue.offer(val); + } + + public void pop() { + Integer pop = stack.pop(); + queue.remove(pop); + } + + public int top() { + return stack.peek(); + + } + + public int getMin() { + return queue.peek(); + } +} + + +/** + * 官方解法:使用minStack来记录最小值,这时只要不pop那个最小值,stack就pop出的永远是那个最小值 + * 速度击败94.9% 内存击败23.54% 4ms + */ +class MinStack { + private Deque stack; + private Deque minStack; + + public MinStack() { + stack = new ArrayDeque<>(); + minStack = new ArrayDeque<>(); + minStack.push(Integer.MAX_VALUE); + } + + public void push(int val) { + stack.push(val); + minStack.push(Math.min(minStack.peek(), val)); + } + + public void pop() { + stack.pop(); + minStack.pop(); + } + + public int top() { + return stack.peek(); + } + + public int getMin() { + return minStack.peek(); + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T59_GetIntersectionNode.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T59_GetIntersectionNode.java new file mode 100644 index 0000000..075ebad --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T59_GetIntersectionNode.java @@ -0,0 +1,66 @@ +package com.markilue.leecode.hot100; + +import com.markilue.leecode.listnode.ListNode; + +import java.util.HashSet; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-14 11:55 + *@Description: + * TODO 力扣160题 相交链表: + * 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。 + * + *@Version: 1.0 + */ +public class T59_GetIntersectionNode { + + + /** + * 思路1:hashset记录法 + * 速度击败21.37% 内存击败87.13% + * @param headA + * @param headB + * @return + */ + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + + HashSet set = new HashSet<>(); + + while (headA != null) { + set.add(headA); + headA = headA.next; + } + + while (headB != null) { + if (set.contains(headB)) { + return headB; + } + headB = headB.next; + } + + return null; + + } + + + /** + * 两个分别走 + * 速度击败97.91% 内存击败56.97% 1ms + */ + public ListNode getIntersectionNode1(ListNode headA, ListNode headB) { + + ListNode tempA = headA; + ListNode tempB = headB; + + while (tempA!= tempB) { + //如果不相交也会出去,因为两个最后都会遍历完A和B,然后都为null出去 + tempA = tempA == null ? headB : tempA.next; + tempB = tempB == null ? headA : tempB.next; + } + + return tempA; + } +}