Leetcode排序相关问题
215.数组中第K大元素
思路
【最终位置】求数组中第K大元素,与排序相关,快排每次都能找到一个元素在排序好的数组中的最终位置(且左边的都比该数小,右边的都比该数大),借鉴快排思想,利用下标优化查找过程。每次找一个元素最终位置,如果等于K-1,将该元素返回;否则,若该元素位置小于K,在其右边子数组快排,否则在左边快排。
代码
public int findKthLargest0(int[] nums, int k){
int left=0,right=nums.length-1;
while(left<right){
int mid=findIndex(nums,left,right); //调用此方法已经将一个元素排序了(mid处
if (mid==nums.length-k) return nums[mid]; //当mid为所指下标
else if (mid>nums.length-k) //mid靠右了,去左边找
right=mid-1;
else
left=mid+1;
}
return nums[left];
}
public int findIndex(int[] nums,int begin,int end){ //指定区间内将begin下标元素最终位置找到
int value=nums[begin];
int left=begin;
int right=end;
while(left<right){
while (left<right&&nums[right]>value)
right--;
while(left<right&&nums[left]<=value)
left++;
int temp=nums[left];
nums[left]=nums[right];
nums[right]=temp;
}
nums[begin]=nums[left];
nums[left]=value;
return left;
}
347.前K个高频元素
思路
【元素频率顺序】因为不同元素个数统计排序,借鉴桶排序,先统计元素个数(HashMap),再将个数相同的元素放到一个桶里(二维List),元素从多到少输出。
代码
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//统计元素个数相关或者元素某个属性——用HashMap键值对
//整数不好作为下标,就用次数作为下标
//初始化HashMap
Map<Integer,Integer> map=new HashMap<>();//键代表数,值代表该数出现次数
for (int key:nums){
//是否包含该键,没有的话初始化值1
if (map.containsKey(key))
map.put(key,map.get(key)+1);
else
map.put(key,1);
}
//将相同次数的放在一个List里
List<Integer>[] list=new ArrayList[nums.length+1];//以防都是相同元素,这时候就要放在下标为nums.length处
for (int key:map.keySet()){ //对于每个键——map.keySet()获取键集合
int value=map.get(key); //获取该键的值,表示键数字出现次数
if(list[value] == null){
list[value] = new ArrayList();
}
list[value].add(key); //加到对应次数下标里
}
//倒序遍历List输出——因为每个子list下不知道有几个,用int[]得遍历,所以还是用list的addAll
List<Integer> output=new ArrayList<>();
for (int i = nums.length; i >0 && output.size()<k; i--) {
if (list[i]==null) continue;;
output.addAll(list[i]);
}
return output.stream().mapToInt(Integer::valueOf).toArray();
}
}
451.根据字符出现频率排序
思路
【元素频率顺序】依旧是元素频率/个数相关的题目,而且范围未知,不是下面的三个元素的问题,借鉴桶排序。统计每个字符出现次数,然后按个数装桶,最后倒序取。
代码
- 与347相似版
class Solution {
public String frequencySort(String s) {
Map<Character,Integer> map=new HashMap<>();
for (int i = 0; i < s.length(); i++) {
if (map.get(s.charAt(i))==null) map.put(s.charAt(i),1);
else map.put(s.charAt(i),map.get(s.charAt(i))+1);
}
List<Character>[] list=new ArrayList[s.length()+1]; //注意
for (Character key:map.keySet()){
int value=map.get(key);
if (list[value]==null) list[value]=new ArrayList<>();
list[value].add(key);
}
List<Character> listOut=new ArrayList<>();
for (int i = list.length-1; i >0 ; i--) {
if (list[i]==null) continue; //注意
for (int j = 0; j < list[i].size(); j++) {
for (int k = 0; k < i; k++) {
listOut.add(list[i].get(j));
}
}
}
StringBuilder str = new StringBuilder();
for (Character character : listOut) {// 对ArrayList进行遍历,将字符放入StringBuilder中
str.append(character);
}
return str.toString();
}
}
- List存字符,按照每种字符出现次数从小到大排列,最后倒序输出(因为List存的去重后的,所以要取map里的value,输出value个字符)
class Solution {
public String frequencySort(String s) {
Map<Character,Integer> map=new HashMap<>();
for (int i = 0; i < s.length(); i++) {
if (map.get(s.charAt(i))==null) map.put(s.charAt(i),1);
else map.put(s.charAt(i),map.get(s.charAt(i))+1);
}
List<Character> listOut = new ArrayList<Character>(map.keySet());
Collections.sort(listOut, (a, b) -> map.get(b) - map.get(a));
StringBuilder str = new StringBuilder();
int size = listOut.size();
for (int i = 0; i < size; i++) {
char c = listOut.get(i);
int frequency = map.get(c);
for (int j = 0; j < frequency; j++) {
str.append(c);
}
}
return str.toString();
}
}
75.颜色分类
思路
【3个元素整体排序】若使用上面多个元素出现频率的方法,会比较麻烦。三个元素则可以遇到0挪前面,遇到2挪后面,遇到1不变。使用双指针法(一个left指向排序好的0下一位,一个right指向排序好的2上一位),同时再多一个指针i依次遍历元素,满足条件与相应指针元素交换,然后再进行不同的步进。特别的,如果right处交换完,i处不知道交换的是啥元素,不能直接步进,需要再次处理i,而right正常–。
代码
class Solution {
public void sortColors(int[] nums){
//左右指针指向待放0的位置和待放2的位置;i步进处理元素,看是否交换——思想是0放前面,2放后面
int left=0,right=nums.length-1,i=0;
while(i<=right){ //注意right后面都是2,不用处理,right处可能为任意值,需要处理,如果为0,就需要换到前面去
if (nums[i]==0)
swap(nums,i++,left++);
else if (nums[i]==2)
swap(nums,i,right--); //换后面去的是2,换前面来的可能0/1/2,需要再次处理
else
i++;
}
}
public void swap(int[] nums,int index1,int index2){
int temp=nums[index1];
nums[index1]=nums[index2];
nums[index2]=temp;
}
}
技巧总结
排序相关思路
① 某个元素的位置/分水岭——参考快排(快排每次把一个元素放到最终位置,且左边都小,右边都大)
② 多个元素个数相关——参考桶排序(按照出现个数元素放到相应桶里)
③ 三种元素排序——一种放前,一种放后,一种不处理
元素出现个数统计HashMap
Map<Character,Integer> map=new HashMap<>();
for (int i = 0; i < s.length(); i++) {
if (map.get(s.charAt(i))==null) map.put(s.charAt(i),1);
else map.put(s.charAt(i),map.get(s.charAt(i))+1);
}
List相关(二维List,Integer的List转数组)
① 当List指定长度,且每个元素存的也是List(二维List):
List<Integer>[] list=new ArrayList[nums.length+1];//以防都是相同元素,这时候就要放在下标为nums.length处
for (int key:map.keySet()){ //对于每个键——map.keySet()获取键集合
int value=map.get(key); //获取该键的值,表示键数字出现次数
if(list[value] == null){
list[value] = new ArrayList();
}
list[value].add(key); //加到对应次数下标里
}
② 二维List中的每个List第一次用都要声明:if (list[value]==null) list[value]=new ArrayList<>();
③ List中加许多键值对addAll:output.addAll(list[i]);
list[i]为一个List。
④ Integer类型的List转数组:return output.stream().mapToInt(Integer::valueOf).toArray();
map操作(键集合、按键排序、某键值+1)
① 获取键集合:map.keySet()
,一般用于遍历每个key:for (int key:map.keySet()){}
,也可以直接创建含有每个键的List:List<Character> listOut = new ArrayList<Character>(map.keySet());
。
② 某键的值+:map.put(s.charAt(i),map.get(s.charAt(i))+1);
③ 按照map里的value排序键(存在了List里):Collections.sort(listOut, (a, b) -> map.get(b) - map.get(a));
字符List转String(Character数组同理)
StringBuilder str = new StringBuilder();
for (Character character : listOut) {// 对ArrayList进行遍历,将字符放入StringBuilder中
str.append(character);
}
return str.toString();