力扣排序算法

力扣排序算法

  • Kth Element 问题
  • 215. 数组中的第K个最大元素
    • 1.要求
    • 2. 思路
  • 347. 前 K 个高频元素
    • 1.要求
    • 2。思路
  • 451. 根据字符出现频率排序
    • 1.要求
    • 思路
  • 75. 颜色分类
    • 1.要求
    • 思想

Kth Element 问题

用于求解 Kth Element 问题,也就是第 K 个元素的问题
快速排序的 partition() 进行实现。需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)

用于求解 TopK Elements 问题,也就是 K 个最小元素的问题。可以维护一个大小为 K 的最小堆,最小堆中的元素就是最小元素。最小堆需要使用大顶堆来实现,大顶堆表示堆顶元素是堆中最大元素。这是因为我们要得到 k 个最小的元素,因此当遍历到一个新的元素时,需要知道这个新元素是否比堆中最大的元素更小,更小的话就把堆中最大元素去除,并将新元素添加到堆中。所以我们需要很容易得到最大元素并移除最大元素,大顶堆就能很好满足这个要求。
堆也可以用于求解 Kth Element 问题,得到了大小为 k 的最小堆之后,因为使用了大顶堆来实现,因此堆顶元素就是第 k 大的元素。
快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。
可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。

215. 数组中的第K个最大元素

1.要求

在未排序的数组中找到第 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 ≤ 数组的长度。

2. 思路

1.将数组排序然后出去第k个元素
2.使用堆,思路是创建一个大顶堆,将所有数组中的元素加入堆中,并保持堆的大小小于等于 k。这样,堆中就保留了前 k 个最大的元素。这样,堆顶的元素就是正确答案
java 最小堆参考:https://blog.csdn.net/u013309870/article/details/71189189
排序 :时间复杂度 O(NlogN),空间复杂度 O(1)
堆 :时间复杂度 O(NlogK),空间复杂度 O(K)。
快速选择 :时间复杂度 O(N),空间复杂度 O(1)

java堆

class Solution {
    public int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> pq = new PriorityQueue<>(); //最小堆
        for(int val:nums){
            pq.add(val);
            if(pq.size()>k){
                pq.poll();
            }
        }
        return pq.peek();
        
    }
}

python 排序

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        nums.sort(reverse=True)
        return nums[k-1]
        

python 堆

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        # nums.sort(reverse=True)
        # return nums[k-1]
        return heapq.nlargest(k, nums)[-1]
        
        

java 快速

class Solution {
    private int partition(int[] a, int l, int h) {
        int i = l, j = h + 1;
        while (true) {
            while (a[++i] < a[l] && i < h) ;
            while (a[--j] > a[l] && j > l) ;
            if (i >= j) {
                break;
            }
            swap(a, i, j);
        }
        swap(a, l, j);
        return j;
    }

    private void swap(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

  
    public int findKthLargest(int[] nums, int k) {
            k = nums.length - k;
            int l = 0, h = nums.length - 1;
            while (l < h) {
                int j = partition(nums, l, h);
                if (j == k) {
                    break;
                } else if (j < k) {
                    l = j + 1;
                } else {
                    h = j - 1;
                }
            }
            return nums[k];
}
  
        
        
    }  
}

347. 前 K 个高频元素

1.要求

给定一个非空的整数数组,返回其中出现频率前 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 是数组的大小。

2。思路

1.先统计频率,然后再排序;
2.使用堆。借助 哈希表 来建立数字和其出现次数的映射,遍历一遍数组统计元素的频率
维护一个元素数目为 kk 的最小堆
每次都将新的元素与堆顶元素(堆中频率最小的元素)进行比较
如果新的元素的频率比堆顶端的元素大,则弹出堆顶端的元素,将新的元素添加进堆中
最终,堆中的 kk 个元素即为前 kk 个高频元素;
3设置若干个桶,每个桶存储出现频率相同的数。桶的下标表示数出现的频率,即第 i 个桶中存储的数出现的频率为 i。
把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数;

class Solution {
    public List<Integer> topKFrequent(int[] nums, int k) {
        HashMap<Integer,Integer> map = new HashMap();
    for (int key: nums) {
        map.put(key,map.getOrDefault(key,0)+1);

    }

                // System.out.println(map);
    // 遍历map,用最小堆保存频率最大的k个元素
    PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
        @Override
        public int compare(Integer a, Integer b) {
            return map.get(a) - map.get(b);
        }
    });
    for (Integer key : map.keySet()) {
        if (pq.size() < k) {
            pq.add(key);
        } else if (map.get(key) > map.get(pq.peek())) {
            pq.remove();
            pq.add(key);
        }
    }
    // 取出最小堆中的元素
    List<Integer> res = new ArrayList<>();
    while (!pq.isEmpty()) {
        res.add(pq.remove());
    }
    return res;


    }
}
class Solution {
    public List<Integer> topKFrequent(int[] nums, int k) {
        Map<Integer,Integer> frequency=new HashMap<>();
        //统计词频
        for(int num:nums){
            frequency.put(num,frequency.getOrDefault(num,0)+1);
        }
        
        List<Integer> [] buckets=new ArrayList[nums.length+1];
        
        for(int key:frequency.keySet()){
            int frequ=frequency.get(key); //获取词频
            
            if(buckets[frequ]==null){
                buckets[frequ]= new ArrayList<>();
            }
            buckets[frequ].add(key);  //将相同词频的词存入相同的下标位置;
        }
        //取出
        List<Integer> topk=new ArrayList<>();
        for(int i=buckets.length-1;i>=0&&topk.size()<k;i--){
            if(buckets[i]==null){
                continue;
            }
            if(buckets[i].size()<=(k-topk.size())){
                topk.addAll(buckets[i]);
            }else{
                topk.addAll(buckets[i].subList(0,k-topk.size()));
            }
        }
        return topk;
    }
}

451. 根据字符出现频率排序

1.要求

给定一个字符串,请将字符串里的字符按照出现的频率降序排列。

示例 1:

输入:
“tree”

输出:
“eert”

解释:
'e’出现两次,'r’和’t’都只出现一次。
因此’e’必须出现在’r’和’t’之前。此外,"eetr"也是一个有效的答案。
示例 2:

输入:
“cccaaa”

输出:
“cccaaa”

解释:
'c’和’a’都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。
示例 3:

输入:
“Aabb”

输出:
“bbAa”

解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意’A’和’a’被认为是两种不同的字符。

思路

1.统计后再输出;
2. 利用桶排序,先统计字符频率,然后利用桶排序的思想,将字符放入它的频次对应的桶中。最后输出即可;

class Solution {
    public String frequencySort(String s) {
        
        Map<Character,Integer> frequencyNum=new HashMap<>();
        for(char c:s.toCharArray()){
            frequencyNum.put(c,frequencyNum.getOrDefault(c,0)+1);
        }
        List<Character> [] frequencybuckets=new ArrayList[s.length()+1];
        for(char c:frequencyNum.keySet()){
            int f=frequencyNum.get(c);
            if(frequencybuckets[f]==null){
                frequencybuckets[f]=new ArrayList();
            }
            frequencybuckets[f].add(c);
        }
        StringBuilder str=new StringBuilder();
        for(int i=frequencybuckets.length-1;i>=0;i--){
            if(frequencybuckets[i]==null){
                continue;
            }
            for(char c:frequencybuckets[i]){
                for(int j=0;j<i;j++){ //重复写几次
                    str.append(c);
                }
            }
        }
        return str.toString();
        
    }
}

75. 颜色分类

1.要求

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

注意:
不能使用代码库中的排序函数来解决这道题。

示例:

输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
进阶:

一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?

思想

1.暴力破解两个for循环;
2.荷兰国旗问题,算法的目标是将这三种颜色顺序正确地排列。它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。
这里使用的是三指针问题,index 当前指针,p1 指向最左边,p2,指向最右边,只有三种情况,当前的指针,等于0,那么当前指针index和与p1指向的数交换,然后都自增1,如果等于2,那么index 与p2指向的数交换,p2自减,对后就是等于1,index 自增1。感觉就是一个代表一个颜色。一轮遍历就完成了。

// An highlighted block
class Solution {
    public void sortColors(int[] nums) {
        
        int index=0;
        int  p1=0,p2=nums.length-1;
        while(index<=p2){
            if(nums[index]==0){
               int temp=nums[index];
                nums[index]=nums[p1];
                nums[p1]=temp;
                p1++;
                index++;
                
            }else if(nums[index]==2){
                int temp=nums[index];
              nums[index]=nums[p2];
                nums[p2]=temp;
                p2--;
            }else{
                index++;
            }
        }
        
    }
    
}

链接: 参考1
参考2

你可能感兴趣的:(领扣刷题)