赞
踩
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。(难度中等)
示例 1:输入: [3,2,1,5,6,4] 和 k = 2
输出: 5示例 2:输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
- package Sort;
-
- import java.util.Arrays;
-
- /**
- * @author : lkw
- * @data : 2021/3/11 10:32
- * @description :215. 数组中的第K个最大元素(难度中等)
- * 方法一:暴力解法
- * 时间复杂度:O(nlogn),此解法时间消耗为排序,jdk默认的是快速排序,
- * 空间复杂度:O(1),使用的是原地排序
- *
- **/
- public class findKthLargest_01 {
- public int findKthLargest(int[] nums, int k) {
- Arrays.sort(nums);
- return nums[nums.length-k];
- //若有5个元素,第二大,就是nums[5-2]
- //若有5个元素,第四大就是nums[5-4],即nums[nums.length-k]
-
- }
- }
- package Sort;
-
- /**
- * @author : lkw
- * @data : 2021/3/11 11:54
- * @description :215. 数组中的第K个最大元素(难度中等)
- * 方法二:快速排序
- **/
- /*在程序运行开始前就知道数组的元素(所有的元素都是机前确定,并且不变),可以使用快速选择排序,
- // 若是动态数据流的情况,则只能使用堆排序
- //快速选择:即快排中轴值计算的过程,小于轴值的放在左侧,大于的放于右侧
- 一轮排序后,通过排序后轴值下标与k之间的关系,选定轴值左侧或右侧,进行下一步操作
- **/
- public class findKthLargest_02 {
- public static void main(String[] args) {
- int[] nums = {23,46,20,8,11,18};
- System.out.println(findKthLargest(nums, 2));
-
- }
- public static int findKthLargest(int[] nums, int k){
- int left= 0;
- int right= nums.length-1;
- int target = nums.length-k;
- while (true){
- int index = partition(nums,left,right);
-
- if (index==target){
- return nums[index];
- }
- else if(index<target){
- left=index+1;
- }
- else right=index-1;
- }
-
-
- }
-
- public static int partition(int[] nums,int left,int right){
- int pivot = nums[left];
- int j=left;
- for (int i = left+1; i <= right; i++) {
- if (nums[i]<pivot){
- j++;
- swap(nums,i,j);
- }
- }
- swap(nums,j,left);
- return j;
-
- }
- public static void swap(int[] nums,int i,int j){
- int temp = nums[i];
- nums[i]= nums[j];
- nums[j] = temp;
- }
- }
补充知识:Lambda表达式
Lambda表达式:接受2个参数(数字),并返回他们的差值 (x, y) -> x – y
Java Lambda 表达式:Java Lambda 表达式 | 菜鸟教程
小顶堆:每个结点的值都小于或等于其左右孩子结点的值
小顶堆的比较器:(a, b) -> a - b
大顶堆的比较器:(a, b) -> b - a
- poll:将首个元素从队列中弹出,如果队列是空的,就返回null
- peek:查看首个元素,不会移除首个元素,如果队列是空的就返回null
思路:创建一个为len的小顶堆,输出len-k个最小元素后,堆中剩余k个最大元素,则堆顶是则是第k大元素。
小顶堆:每个结点的值都小于或等于其左右孩子结点的值
- package Sort;
-
- /**
- * @author : lkw
- * @data : 2021/3/11 12:14
- * @description :优先队列(1)
- **/
-
- import java.util.PriorityQueue;
- import java.util.Random;
-
- /**
- * 创建一个为len的小顶堆,输出len-k个元素后,堆中剩余k个最大元素,则堆顶是则是第k大元素
- */
- public class findKthLargest_03 {
- public static void main(String[] args) {
- int[] nums = {23,20,0,8,11,18};
- System.out.println(findKthLargest(nums, 2));
- }
-
-
-
- public static int findKthLargest(int[] nums, int k) {
- int len = nums.length;
- PriorityQueue<Integer> priorityQueue= new PriorityQueue<>(len,(a,b)->(a-b));
- for (int i = 0; i <len ; i++) {
- priorityQueue.add(nums[i]);
-
- }
- for (int i = 0; i < len-k; i++) {
- priorityQueue.poll();
- }
- return priorityQueue.peek();
- }
- }
-
-
若我们创建一个k+1的小顶堆,先放入k个元素
再将待插入的元素放入小顶堆,此时弹出一个堆顶元素,一直保持堆内还是k个最小元素,程序最后的栈顶就是最k大元素。
可以参考:力扣
- package Sort;
-
- import java.util.PriorityQueue;
-
- /**
- * @author : lkw
- * @data : 2021/3/12 10:11
- * @description :
- **/
- public class findKthLargest_03 {
- public static void main(String[] args) {
- //int[] nums = {23,46,20,8,11,18};
- int[] nums = {3,2,1,5,6,4};
- System.out.println(findKthLargest(nums, 2));
-
- }
- public static int findKthLargest(int[] nums, int k) {
- /*优先队列思想:数组长度为len,输出第k大元素,即数组排序后[0...len-k-1][len-k...len-1],后半段k个元素中最小的元素
- 后半段k个元素中最小的元素,可以创建一个容量为k的小顶堆,最后输出堆顶元素。
- 但是现在数组时乱序:我们可以创建一个k的小顶堆,始终保持有k个
- 则先放入k个元素,堆满之后若此时元素若大于堆顶则先弹出在放入
- 若我们创建一个k+1的小顶堆,先放入k个元素
- 再将待插入的元素放入小顶堆,此时弹出一个堆顶元素,此时堆内还是k个最小元素
- */
- PriorityQueue<Integer> priorityQueue= new PriorityQueue<>(k+1,(a,b)->(a-b));
- //先放入k个元素
- for (int i = 0; i < k; i++) {
- priorityQueue.add(nums[i]);
-
- }
- for (int i = k; i < nums.length; i++) {
- priorityQueue.add(nums[i]);
- priorityQueue.poll();
-
- }
- return priorityQueue.peek();
-
- }
- }
- //大顶堆
- public static int findKthLargest(int[] nums, int k) {
- int len = nums.length;
- PriorityQueue<Integer> priorityQueue= new PriorityQueue<>(len,(a,b)->(b-a));//大顶堆
- for (int i = 0; i < len; i++) {
- priorityQueue.add(nums[i]);
- }
- for (int i = 0; i < k-1; i++) {
- priorityQueue.poll();
- }
- return priorityQueue.peek();
- }
以下为桶排序的三个题
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。(难度中等)
示例 1:输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]示例 2:输入: nums = [1], k = 1
输出: [1]
提示:你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
你可以按任意顺序返回答案。
补充:理解List集合add()和addAll()的区别可参考博文:
简单易懂List集合add()和addAll()的区别_IX的博客-CSDN博客
Map.get() 方法返回指定键所映射的值 。
Map.ketSet() 方法将获取 Map 集合的所有键名。
Add方法是将传入的参数作为当前List中的一个item存储,即使你传入一个List也只会令当前的List增加1个元素 。
AddAll是传入一个List,将此List中的所有元素加入到当前List中,也就是当前List会增加的元素个数为传入的List的大小。
具体题解力扣
补充:关于Map的博文:Map 综述(一):彻头彻尾理解 HashMap_Rico's Blogs-CSDN博客_hashmap
Map 是 Key-Value 对映射的抽象接口,该映射不包括重复的键,即一个键对应一个值。HashMap 是 Java Collection Framework 的重要成员,也是Map族(如下图所示)中我们最为常用的一种。简单地说,HashMap 是基于哈希表的 Map 接口的实现,以 Key-Value 的形式存在,即存储的对象是 Entry (同时包含了 Key 和 Value) 。
同样地,HashSet 也是 Java Collection Framework 的重要成员,是 Set 接口的常用实现类,但其与 HashMap 有很多相似之处。对于 HashSet 而言,其采用 Hash 算法决定元素在Set中的存储位置,这样可以保证元素的快速存取;
HashMap首先会调用Key的hashCode方法,然后基于此获取Key哈希码,通过哈希码快速找到某个桶,这个位置可以被称之为 bucketIndex。如果两个对象的hashCode不同,那么equals一定为 false;否则,如果其hashCode相同,equals也不一定为 true。所以,理论上,hashCode 可能存在碰撞的情况。
hashCode : Object 的 native方法 , 获取对象的哈希值,用于确定该对象在哈希表中的索引位置,它实际上是一个int型整数。
List、ArrayList和LinkedList
- ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)的数据结构。
- List是一个接口,而ArrayList是List的实现类。因此List不能作为定义一个实例对象,只能作为引用。
当数列范围太大,或者不是整数时,我们可以使用桶排序,类似于计数排序。
所有的桶保存在ArrayList集合当中,每一个桶被定义成一个ArrayList。
桶排序的博文:漫画:什么是桶排序?_程序人生的博客-CSDN博客
for (int num:nums)中的num为nums中元素的值。
String reverse = new StringBuffer(a).reverse().toString(); //翻转字符a
- package Sort;
-
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
-
- /**
- * @author : lkw
- * @data : 2021/3/15 9:06
- * @description :347. 前 K 个高频元素
- **/
- public class topKFrequent_01 {
- //思路一:拿一个数组n[k+1]记录0-k的次数,然后比较数组n[k+1]的大小
-
- /*
- Map.get() 方法返回指定键所映射的值
- Map.keySet() 方法将获取 Map 集合的所有键名
- Add方法是将传入的参数作为当前List中的一个item存储,即使你传入一个List也只会令当前的List增加1个元素
- AddAll是传入一个List,将此List中的所有元素加入到当前List中,也就是当前List会增加的元素个数为传入的List的大小
- */
- //1.先统计频率
- public static void main(String[] args) {
- int[] nums = {1,1,1,2,2,3};
- System.out.println(topKFrequent(nums, 2));
-
-
- }
- public static int[] topKFrequent(int[] nums, int k) {
- List<Integer> reslist = new ArrayList();
- //使用map存入的是键值对,统计每个元素出现的频率,元素为键,频率为值
- HashMap<Integer,Integer> map = new HashMap();
- for (int num:nums){
- if (map.containsKey(num)) {
- map.put((Integer)num,map.get(num) + 1);
- }
- else
- map.put((Integer)num,1);
- }
- //桶排序,频率作为数组下标,对于出现频率不同的元素,存入对于的数组下标
- //有n+1个桶,因为元素出现的频率可能有0次,1次,2次.....n次 , 一共有n+1种可能
- //
- List<Integer>[] list = new List[nums.length+1];//创建了一个元素都为List型的数组
- for (int key: map.keySet()){
- //i为出现的频率
- int i = map.get(key);
- if (list[i]==null){
- //防止空指针,防止i第一次进来的时候报错
- //因为频率相同也可放入,因为array[i]也是一个数组列表
- list[i]=new ArrayList<>();
- }
- //创建好数组列表后,再将数值放入
- list[i].add((Integer)key);//注意:这里不是else
- }
- //倒序遍历数组,获得频率由大到小的次序
- for (int i = list.length-1;i>=0 && reslist.size()<k;i--){
- if (list[i]==null) continue;
- reslist.addAll(list[i]);//为啥不是else?
- }
- System.out.println(reslist);
- int[] resArray = new int[reslist.size()];
- // int[] res = new int[k];
- for (int i = 0; i < k; i++) {
- resArray[i] = reslist.get(i);
- }
-
- return resArray;
-
- }
-
- }
时间复杂度:遍历数组,统计频率的时复为O(n),桶排序的时复为O(n+1),list转数组时复为O(n),因此最后的时间复杂度为O(n).
空间复杂度:O(n), map,list,relist,reArray所占用的内存。
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。(中等难度)
示例 1:
输入:"tree"
输出:"eert"
解释: 'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。
此题和上一题347题为一个思路
先统计频率,在桶排序,在组合字符串。
- package Sort;
-
- import java.util.ArrayList;
- import java.util.HashMap;
-
- /**
- * @author : lkw
- * @data : 2021/3/21 17:20
- * @description :451. 根据字符出现频率排序(中等)
- **/
- public class frequencySort_01 {
- public static void main(String[] args) {
- String s = "cccaaaabcdw";
- frequencySort(s);
- }
- public static String frequencySort(String s) {
- char array[] = s.toCharArray();
- //键为:char,值为频率
- HashMap<Character, Integer> map = new HashMap();
- for (int i = 0; i < array.length; i++) {
- char key = array[i];
- if (map.containsKey(key)) {
- map.put(key, map.get(key) + 1);
- } else
- map.put(key, 1);
- }
-
- //System.out.println(map);//{r=1, t=1, e=2}
-
- //创建桶
- //有n+1个桶,因为元素出现的频率可能有0次,1次,2次.....n次 , 一共有n+1种可能
- //第i个桶存出现频率为i的字符
- ArrayList<Character>[] list = new ArrayList[array.length + 1];
- for (char key : map.keySet()) {
- int index = map.get(key);
- if (list[index] == null) {
- list[index] = new ArrayList();
- }
- list[index].add(key);
- }
- System.out.println(list[3]);
-
- //组合字符
- //建议使用StringBuilder,执行效率高
- // String a = "";
- //倒序扫描桶,因为频率高的在最后面
- StringBuilder res = new StringBuilder();
- for (int i=list.length-1;i>=0;i--) {
- if (list[i] != null) {
- for (char ch:list[i]){
- for (int j = 0; j < i; j++) {
- //a=a+ch;
- res.append(ch);
- }
- }
- }
-
- }
-
- //System.out.println(a);
- //return a;
- System.out.println(res.toString());
- return res.toString();
- }
-
- }
-
-
时间复杂度:O(n). 统计频率需要 O(n);创建桶遍历 HashMap 需要 O(n);遍历桶的集合需要 O(n) *两个常数,依然是 O(n)。
空间复杂度:O(n). 建立 HashMap 需要 O(n);建立 n+1个桶需要 O(n)。
最后组合字符串用的string:
最后输出组合字符串用的StringBuilder:
补充:StringBuffer和StringBuilder的运行效率和内存占用几乎一致,这是因为二者都是对对象本身进行操作,不会生成新的字符串对象。二者的区别主要是StringBuffer是线程安全的,而StringBuilder是线程不安全的。
String是不可变的对象,每次对String进行操作的时候,都会生成一个新的对象,这会对系统的性能造成影响。
执行效率竟然如此之大,因此以后建议使用StringBuilder。
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。(难度中等)
示例 1:输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]示例 2:输入:nums = [2,0,1]
输出:[0,1,2]示例 3:输入:nums = [0]
输出:[0]示例 4:输入:nums = [1]
输出:[1]
- package Sort;
-
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.HashMap;
-
- /**
- * @author : lkw
- * @data : 2021/3/21 20:36
- * @description :75. 颜色分类
- * 桶排序
- **/
- public class sortColors_01 {
- public static void main(String[] args) {
- int nums[] = {1,1,0,2,0,1,2,2,2};
- sortColors(nums);
- }
-
- public static void sortColors(int[] nums) {
- //统计频率,hashmap,key:数值,value:频率
- HashMap<Integer,Integer> map = new HashMap();
- for (int num:nums){
- if (map.containsKey(num)){
- map.put(num,map.get(num)+1);
- }
- else
- map.put(num,1);
-
- }
- //System.out.println(map);
-
-
- int t=0;
- for (int key: map.keySet()){
- for (int i =0;i<map.get(key);i++){
- nums[t++]=key;
- }
- }
- //System.out.println(Arrays.toString(nums));
-
- }
- }
时间复杂度:O(n), 遍历数组nums时构造HashMap为O(n),最后构建nums,遍历map用时O(n),因此为O(n)。
空间复杂度:O(n), map的大小为O(n)
此代码中: 0 的优先级> 1 的优先级 >2 的优先级
即时2先被填入,但是若最后值不为2,会被覆盖掉。
- package Sort;
-
- import java.util.Arrays;
-
- /**
- * @author : lkw
- * @data : 2021/3/21 21:28
- * @description :数组
- **/
- public class sortColors_02 {
- public static void main(String[] args) {
- int nums[] = {1, 1, 0, 2, 0, 1, 2, 2, 2};
- sortColors(nums);
- }
-
- public static void sortColors(int[] nums) {
- //zero表示数字0的右侧边界,one、two分别表示1 和 2 的右侧边界
- int zero = 0;
- int one = 0;
- int two = 0;
-
-
- int length = nums.length;
-
- for(int i = 0; i < length; i++)
- {
- //记录下待处理的值
- int temp = nums[i];
-
- //不管怎样,我先给你填个2
- nums[two] = 2;
- two ++;
-
- // <=1的话 1的右侧边界one要向右挪一格
- if(temp <= 1)
- {
- nums[one] = 1;
- one ++;
- }
-
- //为0的话 0的右侧边界zero要向右挪一格
- if(temp == 0)
- {
- nums[zero] = 0;
- zero ++;
- }
- }
- //System.out.println(Arrays.toString(nums));
- }
- }
时间复杂度:O(n)
空间复杂度: O(1)
化简以上代码:
- package Sort;
-
- import java.util.Arrays;
-
- /**
- * @author : lkw
- * @data : 2021/3/22 9:21
- * @description :
- **/
- public class sortColors_03 {
- public static void main(String[] args) {
- int nums[] = {1, 1, 0, 2, 0, 1, 2, 2, 2};
- sortColors(nums);
- }
-
- public static void sortColors(int[] nums) {
- int zeao=0,one=0;
- for (int i=0;i< nums.length;i++){
- int tem=nums[i];
- nums[i]=2;
-
- if (tem<=1){
- nums[one++]=1;
- }
- if (tem==0){
- nums[zeao++]=0;
- }
-
- }
- //System.out.println(Arrays.toString(nums));
-
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。