算法课课后习题对深化理解某一算法确实很有帮助.. 这一次课程学习了快速排序,每一次排序都涉及一个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
起初一看这和partition有什么关系?确实,如果坚定这一信念会得到一个很简单的基于moore投票的算法,稳定简单(两次遍历即可):
⌊ n/3 ⌋ times.
The algorithm should run in linear time and in O(1) space.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];
}
}

通过算法课程学习,深入理解了快速排序中的partition操作。该操作将数组划分为两部分,一部分元素大于pivot,另一部分小于pivot。一道习题要求在线性时间内找出数组中超过N/3比例的元素,这可以通过partition策略和Moore投票法相结合来解决,首先找到三等分点,然后遍历确认主成分,整体时间复杂度为线性。
926

被折叠的 条评论
为什么被折叠?



