diff --git a/Leecode/src/main/java/com/markilue/leecode/stackAndDeque/MaxSlidingWindow.java b/Leecode/src/main/java/com/markilue/leecode/stackAndDeque/MaxSlidingWindow.java index 0b2144e..0573eae 100644 --- a/Leecode/src/main/java/com/markilue/leecode/stackAndDeque/MaxSlidingWindow.java +++ b/Leecode/src/main/java/com/markilue/leecode/stackAndDeque/MaxSlidingWindow.java @@ -165,7 +165,7 @@ public class MaxSlidingWindow { /** * 官方使用优先队列完成的做法:时间复杂度O(nlogn) * 对于「最大值」,我们可以想到一种非常合适的数据结构,那就是优先队列(堆),其中的大根堆可以帮助我们实时维护一系列元素中的最大值。 - * 对于本题而言,初始时,我们将数组 nums 的前 kk 个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。 + * 对于本题而言,初始时,我们将数组 nums 的前 k 个元素放入优先队列中。每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。 * 我们不断地移除堆顶的元素,直到其确实出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。为了方便判断堆顶元素与滑动窗口的位置关系,我们可以在优先队列中存储二元组(num,index),表示元素 num 在数组中的下标为 index。 *

* TODO 本质上就是维护一个可以比较大小的优先队列,队列中存放(值,索引) diff --git a/Leecode/src/main/java/com/markilue/leecode/stackAndDeque/TopKFrequent.java b/Leecode/src/main/java/com/markilue/leecode/stackAndDeque/TopKFrequent.java new file mode 100644 index 0000000..3eae9a6 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/stackAndDeque/TopKFrequent.java @@ -0,0 +1,129 @@ +package com.markilue.leecode.stackAndDeque; + +import org.junit.Test; + +import java.util.*; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.stackAndDeque + * @Author: dingjiawen + * @CreateTime: 2022-09-16 09:24 + * @Description: TODO 力扣347题 前k个高频元素 + * 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 + * @Version: 1.0 + */ +public class TopKFrequent { + + + @Test + public void test() { + int[] nums = {1, 1, 1, 2, 2, 3}; + int k = 2; + int[] result = topKFrequent1(nums, k); + System.out.println(Arrays.toString(result)); + } + + @Test + public void test1() { + int[] nums = {1}; + int k = 1; + int[] result = topKFrequent(nums, k); + System.out.println(Arrays.toString(result)); + } + + + /** + * 自己的思路一:先给nums排序,那么相同的数一定在临近,然后遍历一遍获取count, ->如果使用数组进行记录则数组大小跟nums里最大的数有关,这里使用优先队列进行记录 + * 整体时间复杂度O(nlogn),因为排序O(nlogn),遍历一遍O(n),放入优先队列O(logn) + * 速度击败99.59%,内存击败13.35% + * + * @param nums + * @param k + * @return + */ + public int[] topKFrequent(int[] nums, int k) { + + Arrays.sort(nums); + + int lastNum = nums[0]; + int count = 0; + //使用优先队列记录 + PriorityQueue deque = new PriorityQueue<>(new Comparator() { + @Override + public int compare(int[] nums1, int[] nums2) { + return nums1[1] != nums2[1] ? nums2[1] - nums1[1] : nums2[0] - nums1[0]; + } + }); + + for (int i = 0; i < nums.length; i++) { + if (nums[i] == lastNum) { + count += 1; + } + if (nums[i] != lastNum) { + deque.offer(new int[]{lastNum, count}); + count = 1; + lastNum = nums[i]; + } + //如果是最后一个数了,那就直接放 + if (i == nums.length - 1) { + deque.offer(new int[]{nums[i], count}); + } + } + int[] result = new int[k]; + for (int i = 0; i < k; i++) { + result[i] = deque.poll()[0]; + } + + return result; + + } + + + /** + * 官方思路:不用先排序,直接用map装,装完以后遍历map,用一个小顶堆维护一个大小为k的堆,少于k直接装,大于k就和堆顶元素比 + * map装复杂度O(n),取map O(1),堆大小只有k,所以堆化操作复杂度O(logk)个,所以最后时间复杂度O(nlogk) + * 速度超过94.94%,内存超过25.05% + */ + public int[] topKFrequent1(int[] nums, int k) { + + HashMap map = new HashMap<>(); + + for (int num : nums) { + map.put(num, map.getOrDefault(num, 0) + 1); + } + + //创建一个小顶堆 + PriorityQueue deque = new PriorityQueue<>(new Comparator() { + @Override + public int compare(int[] nums1, int[] nums2) { + return nums1[1] - nums2[1]; + } + }); + + for (Map.Entry entry : map.entrySet()) { + Integer num = entry.getKey(); + Integer count = entry.getValue(); + + if(deque.size()deque.peek()[1]){ + deque.poll(); + deque.offer(new int[]{num,count}); + } + } + } + + int[] result = new int[k]; + for (int i = 0; i < k; i++) { + result[i]=deque.poll()[0]; + } + + return result; + + + } + + +}