主成分个数 - 快排中partition的深入理解

来源:互联网 发布:守望先锋 网络同步 编辑:程序博客网 时间:2024/05/26 20:21

算法课课后习题对深化理解某一算法确实很有帮助.. 这一次课程学习了快速排序,每一次排序都涉及一个partition操作,也就是把数组分为比pivot大的部分,和比pivot小的部分。

这个题目是在线性时间内找到某一长N的数组中出现次数超过某一比例,如N/3的全部元素。

https://leetcode.com/problems/majority-element-ii/

Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times. The algorithm should run in linear time and in O(1) space.
起初一看这和partition有什么关系?确实,如果坚定这一信念会得到一个很简单的基于moore投票的算法,稳定简单(两次遍历即可):

import java.util.List;import java.util.ArrayList;public class MajorityNumber2 {    public List<Integer> majorityElement(int[] nums) {        int n = nums.length;        Integer n1 = null, n2 = null;        int c1 = 0, c2 = 0;        for (int id = 0; id < n; id++) {            if(n1 != null && nums[id] == n1.intValue()) c1++;            else if (n2 != null && nums[id] == n2.intValue()) c2++;            else if (c1 == 0) {                n1 = nums[id];                c1 = 1;            }            else if (c2 == 0) {                n2 = nums[id];                c2 = 1;            }            else {                c1--;                c2--;            }        }        c1 = c2 = 0;        for (int id = 0; id < n; id++) {            if (nums[id] == n1.intValue()) c1++;            else if (nums[id] == n2.intValue()) c2++;        }        List<Integer> rst = new ArrayList<>();        if (c1 > n/3) rst.add(n1);        if (c2 > n/3) rst.add(n2);        return rst;    }}

但是,其实这个问题还有一种思考方法。对于某一个元素,如果超过一定比例出现,比如说一半,那它在排好序的数组中一定会处在中间点上。

类似地,超过三分之一,那一定会出现在某一个三等分点上...

那么,类似于moore投票中找candidate的方法,我可以直接用partition找到排名在某等分点上的数,从而把所有的candidate找出来,然后再遍历一次确认这些candidate中有哪些是真的主成分。partition耗时最差是线性,总共来说时间同样是线性的

import java.util.List;import java.util.ArrayList;public class Solution {    public List<Integer> majorityElement(int[] nums) {        int n = nums.length;        List<Integer> rst = new ArrayList<>();        if (n <= 1) {            for (int i = 0; i < n; i++)                rst.add(nums[i]);            return rst;        }        if (n == 2) {            rst.add(nums[0]);            if (nums[0] != nums[1])                rst.add(nums[1]);            return rst;        }        int n3thc = 0; // the n/3-th counter        int n2thc = 0; // the 2n/3-th counter        int n3th = select(nums, n/3);        int n2th = select(nums, 2*n/3);        for (int i = 0; i < n; i++) {            if (nums[i] == n3th) n3thc++;            else if (nums[i] == n2th) n2thc++;        }        if (n3thc > n/3) rst.add(n3th);        if (n2thc > n/3) rst.add(n2th);        return rst;    }    private static void swap(int[] a, int p, int q) {        int tmp = a[p];        a[p] = a[q];        a[q] = tmp;    }    private static int partition(int[] nums, int lo, int hi) { // descending partition        int i = lo + 1, j = hi;        for (;;) {            while (nums[lo] < nums[i] && i < j) i++;            while (nums[lo] > nums[j] && i < j) j--;            if (i >= j) break;            swap(nums, i, j);        }        swap(nums, lo, j);        return j;    }    private static int select(int[] nums, int k) { // find the k-th largest in nums        int lo = 0, hi = nums.length - 1;        while (lo < hi) {            int j = partition(nums, lo, hi);            if (j < k) lo = j + 1;            else if (j > k) hi = j - 1;            else return nums[k];        }        return nums[k];    }}



0 0
原创粉丝点击