剑指offer39. 数组中出现次数超过一半的数字 P39

剑指offer39. 数组中出现次数超过一半的数字 P39

题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1, 2, 3, 2, 2, 2, 5, 4, 2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。

1. 基于快排 (会污染数组)
其实只要涉及顺序的问题,大多数都可以排序。这题可以建立在快排的基础上,每做一次划分之后,把当前划分元素的索引index返回,这个数就是整个数组第index小的数,如果index=len/2, 结束递归。因为出现次数超过数组长度一半的元素最后一定有一个在索引是len/2的位置。

bool ansIsValid = true;          // 全局变量,=true时返回的0代表没有找到

bool isValid(int *nums, int len){  //  输入鲁棒性检查
	if (nums == NULL || len < 1)
		return false;
	return true;
}

bool isMoreHalf(int *nums, int len, int num){  //看最后找到的元素的个数是否真的超过一半
	int count = 0;
	for (int i = 0; i < len; ++i) {
		if (nums[i] == num) ++count;
	}
	if (count <= len / 2) return false;        // 必须 > len / 2 才行!!
	return true;
}

int Partition(int *nums, int start, int end) { //快排的一次划分,返回固定元素后的索引
	int flag = nums[start];
	int i = start, j = end;
	while (i < j) {
		while (i < j && nums[j] >= flag) --j;
		int rightmin = nums[j];
		
		while (i < j && nums[i] <= flag) ++i;
		nums[j] = nums[i];
		nums[i] = rightmin;
	}
	nums[start] = nums[i];
	nums[i] = flag;
    return i;            // i的位置不会变了
}

int findCore(int *nums,int len) {      // 主调函数
ansIsValid = true;         // 默认答案无效,不仅下面鲁棒性要用,最后检验也要用
	if (!isValid(nums, len)) {           // 鲁棒性
		return 0; 
	}

	int mid = len >> 1;          // 中位数索引
	int start = 0;
	int end = len - 1;
	int index = Partition(nums, start, end);  // 第一次划分

	/*  为什么比的是索引而不是值,因为index前后一边大一边小是确定的 
		只有index到中间时,才能保证从头到中间,或者从中间到尾巴都是一个数
		这样才能找到超过一半的数 
	 */  

	while (index != mid) {      // 出口
		if (index > mid)  
			end = index -  1;
		else 
			start = index + 1;
			
	   index = Partition(nums, start, end);    // 选择左还是右划分
	}
    
	if (isMoreHalf(nums, len, nums[index])) {  // 检验是不是真的个数超过数组长度一半
		ansIsValid = false;                   // 确实超过
		return nums[index];
	} else {
		return 0;                     
	} 
}

方法2: 比较个数差 (没有污染数组)

从个数入手,有一个数字出现次数超过一半,也就是说它比其他所有数字次数和都多。我们把这个数和其他数分成两组 “相对抗” , 用一个变量存数字res,另一个存次数count。遍历数组,当前数字和上一个相等,次数+1;不等,次数-1,如果次数减到0,把res更新为当前数字,count更新为1. 到最后因为我们要找的数次数比其他的和都多,那么它一定时最后一次把count设成1的数。

bool isAns = true;        // 判断return 0 时是不是有效 

int findSBNumber(int nums[], int len) {
	isAns = true;          // 要在鲁棒性检查之前设置
	if (nums == NULL || len < 1) 
		return 0;
	int ans = nums[0];      // 上文中的res
	int count = 1;
	for (int i = 0; i < len; ++i) {
		if (nums[i] != ans) {
			--count;
			if (count == 0) {
				count = 1;
				ans = nums[i];
			}
		}else {
			++count;
		}
	}
	count = 0;
	for (int i = 0; i < len; ++i) {   // 验证是不是真的个数超过一半 
		if (nums[i] == ans)
			++count;
	}
	
	if (count <= len / 2)
		return 0;    
	isAns = false;  
	return ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值