数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
class Solution {
public int majorityElement(int[] nums) {
int[] bit = new int[32];
int n = nums.length;
for(int a : nums){
for(int i=0; i<32; i++){
//无符号右移; 负数的无符号右移移动的是补码还是原码?
if(((a>>>i) & 1) == 1)
bit[i]++;
}
}
int res=0;
for(int i=0; i<32; i++){
if(bit[i]>n/2)
res = res | (1<<i);
}
return res;
}
}
/*
分治算法; 归并排序就属于分治算法
本题中使用分治的方法得到的不是众数, 那得到的是什么?
这么说吧, 如果某个元素出现的次数超过了长度的一半, 那么使用分治的思想一定可以得到这个数
再次强调: 某个元素出现的次数超过了长度的一半, 该算法才有效
反例:
{2,2,3,3,2,2,5,5}, 使用下面的算法返回的是5; 虽然众数是2, 但是2的出现次数没有大于数组长度的一半,所以算法失效了
*/
class Solution {
public int majorityElement(int[] nums) {
return core(nums, 0, nums.length-1);
}
private int core(int[] nums, int left, int right){
//base case
if(left==right)
return nums[left];
//
int mid = left + ((right-left)>>1);
int leftRes = core(nums, left, mid);
int rightRes = core(nums, mid+1, right);
if(leftRes == rightRes)
return leftRes;
int leftCount = count(nums, left, mid, leftRes);
int rightCount = count(nums, mid+1, right, rightRes);
return leftCount>rightCount? leftRes:rightRes;
}
private int count(int[] arr, int left, int right, int target){
int count = 0;
for(int i=left; i<=right; i++){
if(arr[i] == target)
count++;
}
return count;
}
}
/*
核心:分解问题
原问题:找出出现的次数超过数组长度的一半的元素
分解为两个问题:1)找出出现次数最多的元素;2)检查这个元素的出现次数是否超过数组长度的一半
*/
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array==null || array.length==0)
return 0;
if(array.length==1)
return array[0];
//1)找出出现次数最多的元素
// 这个方法并不能一定找出出现次数最多的元素, 找出的元素有可能是出现次数超过一半的元素, 需要验证
int curr=array[0], count=1;
for(int i=1; i<array.length; i++){
if(array[i] != curr){
count--;
if(count==0){
curr=array[i];
count=1;
}
}
else
count++;
}
//2)检查这个元素的出现次数是否超过数组长度的一半
count=0;
for(int i=0; i<array.length; i++){
if(array[i]==curr)
count++;
}
return count > array.length/2 ? curr : 0;
}
}
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
/*
之前写的方案是使用了必要条件之一: 满足要求的元素是数组中出现次数最多的元素
现在用一用另一个必要条件:满足要求的元素一定出现在arr[N/2]处, 配合partition使用
*/
if(array==null || array.length < 1 )
return 0;
//
partition(array, 0, array.length-1);
int curr = array[(array.length-1)/2], count = 0;
for(int i=0; i<array.length; i++){
if(array[i] == curr)
count++;
}
return count > array.length/2 ? curr : 0;
}
public void partition(int[] arr, int left, int right){
int random = (int)(Math.random()*(right-left+1)+left);
swap(arr,random, right);
int small=left-1;//小于区
int big=right;//大于区
while(left<big){
if(arr[left] < arr[right])
swap(arr, left++, ++small);
else if(arr[left] > arr[right])
swap(arr, left, --big);
else
left++;
}
swap(arr, big, right);//将划分值放到大于区的边界
//return new int[]{small+1, big};//没用到快排,不用返回等于区边界
}
public void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array==null || array.length<1)
return 0;
int curr=array[0], count=1;
for(int i=1; i<array.length; i++){
if(array[i] == curr)
count++;
else{
count--;
if(count==0){
curr = array[i];
count = 1; //第一次忘记更新次数。 更新curr必须更新count
}
}
}
//count>0,说明对应的curr是数组中出现次数最多的元素,需要进一步检查是否满足出现次数大于数组长度一半的条件
if(count>0){
count=0;//记录出现次数最多的元素的出现次数
for(int i=0; i<array.length;i++)
if(array[i]==curr)
count++;
}
return count > array.length/2 ? curr : 0;
}
}
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
//input check
if(array.length<1)
return 0;
//execute
/*
出发点:数组中出现次数超过数组长度一半的元素, 出现的次数比其他元素加起来都多(实际上这只是必要条件,并不是充分条件! 反例见下面的分析), 所以可以这样做
用curr记录当前的元素, count表示当前元素出现的次数.
如果下一个元素和curr相等, 则count++
如果下一个元素和curr不等,则count--
如果count==0, 那么令curr等于下一个元素, count=1
循环结束后,如果,count>0, 那么curr就是结果. 不对. 因为如果
(有没有可能, count>0, curr不是正确的结果? 这就需要证明最开始的出发点是不是正确的. 看起来出发点不够完备, 反例:[1,2,3,2,4,2,5,2,3])
*/
int curr = array[0];
int count = 1;
for(int i=1; i<array.length; i++){
if(array[i] == curr)
count++;
else{
count--;
if(count==0){
curr = array[i];
count = 1;
}
}
}
//check result 检查curr是不是出现次数超过数组长度的一半
count = 0;
for(int i=0; i<array.length; i++){
if(array[i]==curr)
count++;
}
if(count > array.length/2)
return curr;
return 0;
}
}
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
//input check
if(array.length < 1)
return 0;
//execute
int index = partition(array, 0, array.length-1);
while(true){
if(index == array.length/2)
break;
else if(index < array.length/2)
index = partition(array, index+1, array.length-1);
else
index = partition(array, 0, index-1);
}
int curr = array[index];
int count = 0;
for(int i=0; i<array.length; i++)
if(array[i]==curr)
count++;
if(count > array.length/2)
return curr;
return 0;
}
public int partition(int[] array, int lo, int hi){
int pivot = array[lo];
int i=lo, j=hi+1;
while(true){
while(++i < array.length && array[i] < pivot)
if(i==hi)
break;
while(--j >= 0 && array[j] >= pivot) //这里一定要用>=,不能只用>
if(j==lo)
break;
if(i>=j)
break;
swap(array, i, j);
}
swap(array, lo, j);
return j;
}
public void swap(int[] array, int i, int j){
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
// 基于partition的方法
//input check
ArrayList<Integer> al = new ArrayList<Integer>();
if(input.length < k || k<=0)
return al;
//execute
int index = partition(input, 0, input.length-1);
while(index != k-1){
if(index > k-1)
index = partition(input, 0, index-1);
if(index < k-1)
index = partition(input, index+1, input.length-1);
}
for(int i=0; i<=index; i++)
al.add(input[i]);
return al;
}
public int partition(int[] input, int lo, int hi){
//注意input只有一个元素的情况, 此时要注意数组索引是否越界
int i=lo, j=hi+1, pivot=input[lo];
while(true){
while(++i < input.length && input[i] < pivot)
if(i==hi)
break;
while(--j >= 0 && input[j] >= pivot)
if(j==lo)
break;
if(i>=j)
break;
swap(input, i, j);
}
swap(input, lo, j);
return j;
}
public void swap(int[] input, int i, int j){
int temp = input[i];
input[i] = input[j];
input[j] = temp;
}
}