题目:
No.1 Majority Number
Given an array of integers, the majority number is the number that occurs more than half of the size of the array. Find it.
(给定一个整型数组,找到一个数,它在数组中的出现次数大于数组元素个数的二分之一)
样例For [1, 1, 1, 1, 2, 2, 2], return 1
挑战 Expand O(n) time and O(1) space
分析:我们也许会想到先进行排序,在(n+1)/2位置的数即为要找的数,这样最小的时间复杂度为O(Nlog2N);如果进行hash,数字的范围又未知。
思路:出现的次数超过数组长度的一半,表明这个数字出现的次数比其他数字出现的次数的总和还多。所以我们可以考虑每次删除两个不同的数,那么在剩下的数中,待找数字出现的次数仍然超过总数的一半。通过不断重复这个过程,不断排除掉其他数字,最终剩下的都为同一个数字,即为要找的数字。
package test;
import java.util.ArrayList;
/**
*
* @author 风的流向
*
*/
public class Solution {
/**
* @param nums
* : a list of integers
* @return: find a majority number
*/
public int majorityNumber(ArrayList<Integer> nums) {
// 出现次数超过数组长度
int end = nums.size() - 1;// 最后一个元素的下标
for (int i = 1; i <= end; i++) {
if (nums.get(0) != nums.get(i)) {// 如果找到和第一个元素不相等元素,则删除它们两个
nums.remove(i);
nums.remove(0);
end = end - 2;// 最后一个元素前进两位
i = 1;// 开始指针指向首元素
}
}
return nums.get(0);
}
}
【附:开课吧算法讨论组参考答案】
http://www.douban.com/group/topic/67690689/
解法一:O(n)时间复杂度,O(n)空间复杂度
第一种解法为采用Hash表的办法(Hash表在工作中出现的概率非常高!大家一定要熟练运用),Hash表的key是数组中的元素,而value就是这个元素对应的出现次数。
执行过程如下:利用循环依次读取数组中的数字,插入到Hash表中,插入过程中如果发现已存在该值,则对该数字对应的value进行累加1操作,如果为发现Hash表中不存在这个数字,则插入这个数字和它对应的value,value赋值1。而后判断Hash表中的计数器是否满足条件,此过程的时间复杂度为O(n),空间复杂度是O(n)。
提升:常见的Hash表的实现所占用的空间为原始数据的2倍左右,如将一个长度为n的int数组插入到Hash表中需要占用约2*n的单位空间。而Hash的实现往往是考虑冲突概率和空间占用率的一个折中,叫兽有一种Hash的简易实现方法能够在可接受的冲突率下达到1.2倍的空间占用,如有兴趣可以在后面的专题中安排讨论
参考代码:
def majorityNumber(self,nums):
n = round(len(nums)/2.0)
nd = {}
for x in nums:
if x in nd:
nd[x] += 1
else:
nd[x] = 1
if nd[x] >= n:
return x
解法二:O(nlogn)时间复杂度,O(1)空间复杂度
第二种解法为首先对数组进行排序(导致时间复杂度为O(nlogn)),而后遍历数组,并统计相邻两个数字为相等的情况下连续发生的长度,如果这个长度满足条件,则此数字为所求结果。
参考代码:
int majorityNumber(vector<int> nums) {
// write your code here
std::sort(nums.begin(), nums.end());
int count = 1;
int previousNum = nums[0];
for (int i = 1; i < nums.size(); i++)
{
if (previousNum == nums[i])
count++;
else if (count > nums.size() / 2)
return previousNum;
else
{
previousNum = nums[i];
count = 1;
}
}
return previousNum;
}
解法三:
定义两个指针preIndex,postIndex,代表数组中两个元素的下标,初始化为0,1。
利用循环,比较两个指针所指向的值,如果相等,则postIndex后移一步,如果不相等,则preIndex和postIndex同时后移两步,表示‘删除’两个元素。循环结束后,返回preIndex所指向的元素。
参考代码:
/**
* @param nums: a list of integers
* @return: find a majority number
*/
public int majorityNumber(ArrayList<Integer> nums) {
// write your code
int preIndex = 0; //定义两个位置
int postIndex = 1;
while(postIndex < nums.size() - 1){
//如果不相等,两个位置同时向后移动两步
if (nums.get(preIndex) != nums.get(postIndex)){
preIndex += 2;
postIndex += 2;
}else{
//后面的位置向后移动一步
postIndex += 1;
}
}
return nums.get(preIndex);
}
推荐学习内容:
时间复杂度与空间复杂度:
http://baike.baidu.com/view/7527.htm?fr=aladdin
哈希表
http://baike.baidu.com/view/329976.htm?fr=aladdin