zoukankan      html  css  js  c++  java
  • 排序

    排序算法:

    常用的排序算法有:快排、归并、堆排序。

    该中题型只要是利用排序来解题。

    Kth Element问题,也就是第K个元素的问题。

    快速选择

    用于求解 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 问题。

    1.Kth Element

    215. Kth Largest Element in an Array (Medium)

    Input: [3,2,1,5,6,4] and k = 2
    Output: 5
    

    题目描述:找到倒数第 k 个的元素。

    (1)排序 :时间复杂度 O(NlogN),空间复杂度 O(1)

    public int findKthLargest(int[] nums, int k) {
        Arrays.sort(nums);
        return nums[nums.length - k];
    }
    

    (2)堆 :时间复杂度 O(NlogK),空间复杂度 O(K)。

    思路:

    小顶堆

    使用小顶堆存放元素,堆顶是所有元素中最小元素,那么当堆中超过了K个元素,则将堆顶元素删除,一直循环到所有元素遍历一遍,且保持小顶堆元素个数始终为K个元素,则堆顶元素即为所求元素。
    

    大顶堆

    使用大顶堆存放元素,堆定是序列中最大元素,那么当堆内元素超过了n-k个元素,则将堆定删除,一直循环到遍历完所有元素,且保持大顶堆中元素个数始终为n-k个,则堆顶元素即为所求元素。
    
    public int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> pq = new PriorityQueue<>(); // 小顶堆
        for (int val : nums) {
            pq.add(val);
            if (pq.size() > k)  // 维护堆的大小为 K
                pq.poll();
        }
        return pq.peek();
    }
    

    (3)快速选择 :时间复杂度 O(N),空间复杂度 O(1)

    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];
    }
    
    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;
    }
    

    桶排序

    1. 出现频率最多的 k 个元素

    347. Top K Frequent Elements (Medium)

    Given [1,1,1,2,2,3] and k = 2, return [1,2].
    

    思路:

    使用map存放,元素和元素对应个数,使用小顶堆:
    小顶堆中存放的是map的键,但是在建立小顶堆的时,使用键对应map的值进行建堆。还可以这样玩儿 骚操作。
    这就是所谓的桶排序,即根据map中的值来对键进行排序,本题使用相对简单的方法,常见的map根据值排序,相对复杂。
    

    代码实现:

    public List<Integer> topKFrequent(int[] nums, int k) {
    
            Map<Integer,Integer> map=new HashMap<>();
            for (int num : nums) {
                map.put(num,map.getOrDefault(num,0)+1);
            }
    
            PriorityQueue<Integer> heap =
                    new PriorityQueue<Integer>((n1, n2) -> map.get(n1) - map.get(n2));
    
            //进小顶堆,并保持小顶堆始终含有k个元素,若超过k个元素,则删除队首元素,则这k个元素是map的键中前k大的元素
            for (Integer integer : map.keySet()) {
                heap.add(integer);
                if(heap.size()>k){
                    heap.remove();
                }
            }
    
            List<Integer> list=new ArrayList<>();
            //当小顶堆不空时候,将数据输出到list
            while (!heap.isEmpty()){
                 list.add(heap.remove());
            }
            Collections.reverse(list);
            return list;
        }
    
    

    2.按照字符出现次数对字符串排序

    451. Sort Characters By Frequency (Medium)

    Input:
    "tree"
    
    Output:
    "eert"
    
    Explanation:
    'e' appears twice while 'r' and 't' both appear once.
    So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer.
    
    

    思路:

    使用map统计出字符和对应出现频次,key为字符,value为值,
    使用优先队列来根据value的值来对key进行构建大顶堆,
    大顶堆出队,并根据出现的频次来加入StringBuilder,
    最后返回sb.toString()
    
    

    代码实现:

    public static String frequencySort2(String s) {
    
            Map<Character, Integer> map = new HashMap<>();
            for (int i = 0; i < s.length(); i++) {
                char c = s.charAt(i);
                map.put( c, map.getOrDefault(c, 0) + 1);
            }
            PriorityQueue<Character> pq = new PriorityQueue(((o1, o2) -> map.get(o2) - map.get(o1)));
            for (Character character : map.keySet()) {
                pq.add(character);
            }
            //使用StringBuilder来进行添加字符,更加高效
            StringBuilder sb=new StringBuilder();
            while (!pq.isEmpty()) {
                Character key = pq.remove();
                for (Integer i = 0; i < map.get(key); i++) {
                    sb.append(key);
                }
    
            }
            return sb.toString();
        }
    
    

    荷兰国旗问题

    荷兰国旗包含三种颜色:红、白、蓝。

    有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。

    1.按颜色进行排序

    75. Sort Colors (Medium)

    思路:

    本问题被称为 荷兰国旗问题
    ,最初由 Edsger W. Dijkstra提出。
    其主要思想是给每个数字设定一种颜色,并按照荷兰国旗颜色的顺序进行调整。
    
    我们用三个指针(p0, p2 和curr)来分别追踪0的最右边界,2的最左边界和当前考虑的元素。
    本解法的思路是沿着数组移动 curr 指针,若nums[curr] = 0,则将其与 nums[p0]互换;若 nums[curr] = 2 ,则与 nums[p2]互换。
    
    

    流程:

    初始化0的最优边界: p0=0
    初始化2的最左边界: p2=n-1
    初始化当前考虑的元素符号: cuur=0
    while cuur <= p2
    	若 nums[cuur]=0 :交换第 curr个和第 p0个元素,并将指针都向右移。
    	若 nums[cuur]=2 :交换第 curr个和第p2个元素,并肩p2指针左移。
    	若 nums[cree]=1 :将指针 curr右移
    
    

    代码实现:

    /**
    * 荷兰国旗问题
    * @param nums
    */
    public void sortColors(int[] nums) {
    
        int curr=0,p0=0,p2=nums.length-1;
    
        while (curr<=p2){
            if (nums[curr]==0){
                //此处必须是curr++ p0++ ,curr>=p0 ,
                //将两坐标位置元素交换后,此时curr指向的位置元素不需排序,
                //因为是已经排过序的,即对于p0都是已经排过序的,     
                //可以画图模拟
                swap(nums,curr++,p0++);
            }else if (nums[curr]==2){
                //此处必须是curr不变,p2--,curr<=p0,
                //将两坐标元素交换后此时,此时curr指向的元素是未曾被排序的,
                //所以还需要继续排序,可以画图模拟
                swap(nums,curr,p2--);
            }else {
                curr++;
            }
        }
    }
    /**
    * 交换位置
    * @param nums
    * @param i
    * @param j
    */
    void swap(int nums[],int i,int j){
        int tem=nums[i];
        nums[i]=nums[j];
        nums[j]=tem;
    }
    
    
  • 相关阅读:
    jquery使用--常见前端效果实现
    Quartz —— Spring 环境下的使用
    java设计模式--外观模式(Facade)
    java设计模式--装饰模式(Decorator)
    Java开发中的23种设计模式详解(转)
    java设计模式--工厂模式
    选择排序
    序列化
    解析器
    版本控制
  • 原文地址:https://www.cnblogs.com/jimlau/p/11917842.html
Copyright © 2011-2022 走看看