最近在刷LeetCode的算法题,今天学习了排序的高效方法:最小堆和桶排序法。
题目描述:给定一个非空的整数数组,返回其中出现频率前 k高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
1.最小堆法
思路:①借助哈希表来建立数字及其出现频次的映射
②维护一个元素数目为k的最小堆
③每次都将新元素与堆顶元素(堆中频率最小的元素)比较
④若新的元素比堆顶端的元素大,则弹出堆顶元素,将新元素添加进去
⑤最终,堆中k个元素即为前k个高频元素
class Solution{
public List topKFrequent(int[] nums,int k){
//使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值
HashMap map=new HashMap();
for(int num:nums){
if(map.containsKey(num)){
//若已存在,值加一
map.put(num,map.get(num)+1);
}else{
//若不存在,创建新的键值对
map.put(num,1);
}
}
}
//遍历map,用优先队列最小堆保存频率最大的k个元素
//建立小顶堆,若想建立大顶堆,则return map.get(b)-map.get(a)
/*继承Comparator接口,必须重写compare 方法*/
PriorityQueue pq=new PriorityQueue<>(new Comparator(){
@Override
public int compare(Integer a,Integer b){
return map.get(a)-map.get(b);
}
)};
//保存频率最大的k个元素,peek()方法:查看堆顶元素值
for(Integer key:map.keySet()){
if(pq.size()
pq.add(key);
}else if(map.get(key)>map.get(pq.peek())){
pq.remove();
pq.add(key);
}
}
//取出最小堆的元素
List res=new ArrayList<>();
while(!pq.isEmpty()){
res.add(pq.remove);
}
return res;
}
tips:
PriorityQueue(优先级队列):按关键词有序排列,插入新数据会自动插入合适位置。默认小顶堆。重写可以改变其排列方式。
方法1:
PriorityQueue pq=new PriorityQueue<>(new Comparator(){
@Override
public int compare(Integer a,Integer b){
return map.get(b)-map.get(a);
}
)};
方法2:调用Comparator,reverseOrder()方法
PriorityQueue pq=new PriorityQueue<>(Comparator.reverseOrder());
方法3:Lambda表达式:允许把函数作为一个方法的参数传递进方法。
PriorityQueue pq=new PriorityQueue<>((a,b)->b-a);
时间复杂度:O(nlogk),n表示数组的长度,遍历一遍复杂度为o(n);接着,遍历用于存储元素频率的map,若元素频率大于最小堆中顶部的元素,则将顶部的元素删除并将该元素 加入堆中,这里维护堆的数目为k,因此时间复杂度为O(nlogk)。
2.桶排序法
//基于桶排序求解「前 K 个高频元素」
class Solution {
public List topKFrequent(int[] nums, int k) {
List res = new ArrayList();
// 使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值
HashMap map = new HashMap();
for(int num : nums){
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
//桶排序
//将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标
List[] list = new List[nums.length+1];
for(int key : map.keySet()){
// 获取出现的次数作为下标
int i = map.get(key);
if(list[i] == null){
list[i] = new ArrayList();
}
list[i].add(key);
}
// 倒序遍历数组获取出现顺序从大到小的排列
for(int i = list.length - 1;i >= 0 && res.size() < k;i--){
if(list[i] == null) continue;
res.addAll(list[i]);
}
return res;
}
}
LeetCode-75按颜色排序
题目:
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
学习来的思路:1.设置两个指针r1,r2,分别代表当前r1记录左边小于1的当前位置,r2记录左边小于2 的当前位置。
2.如果左边比2小,那么就r2++,且交换数组的顺序(r2和i),若仍然小于1,r1++,继续交换(r2,r1)
(这个思路太大神了orz)
class Solution {
public void sortColors(int[] nums) {
int r1=-1,r2=-1;
for(int i=0;i
if(nums[i]<2){
r2++;
swap(nums,i,r2);
if(nums[r2]<1){
r1++;
swap(nums,r1,r2);
}
}
}
}
void swap(int[] nums,int i,int j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}