数组中出现次数超过一半的数字(剑指offer-39)

1、题目描述

    找出整数序列出现次数超过一半的数,比如1,2,3,2,2,2,5,4,2序列中,出现次数超过一半的数字为2。对于这个题目,首先最简单的暴力解法是先对数组排序,然后取中位数即可,时间复杂度为O(n*logn)。第二种方式是使用额外的一个map来保存每个元素出现的次数,时间复杂度为O(n),空间复杂度为O(n)。当然这两种方式是不能通过面试滴。

2、解题思路(一)

    如果序列中存在次数超过一半的数,则这个数出现的次数比其他所有数字出现的次数的和还要多。我们可以考虑在遍历数组是保存两个值:一个是数组中的一个数字,另一个是次数。当我们开始遍历数组时,如果下一个数和之前保存的数相同,则次数加一,如果不同则次数减一。如果次数减为0,则保存当前数字,次数设置为1。遍历完成之后,我们要找的数字就是最后一次把次数设为1对应的数字。时间复杂度为O(n),代码如下:

#include<iostream>
#include<vector>
using namespace std;

//找出序列中出现次数超过一半的数字
void SurpassHalfNumber(vector<int> input) {
	int count = 1;
	int index = 1;
	for (int i = 1; i < input.size(); i++) {
		if (input[i] == input[i - 1])
			count++;
		else {
			count--;
			if (count == 0) {
				index = i;
				count = 1;
			}
		}	
	}
	cout << "number:" << input[index] << endl;
}

3、解题思路(二)

    由于这个数在数组中出现的次数超过一半,因此这个数必然是数组排序后的中位数。因此可以采用基于快速排序划分的思想,先确定一个枢纽值,将这个枢纽值的位置和n/2进行比较,如果两者相同,则找到中位数,直接返回;如果比n/2大,那么在划分后的左半部分继续划分查找;如果比n/2小,那么在划分后的右半部分继续查找。直到找到一个枢纽值它的位置为n/2,时间复杂度为O(n),算法如下:

// 快速排序的一次划分
int Partition(vector<int> &input, int start, int end) {
	int temp = input[start];
	int low = start, high = end;
	while (low < high) {
		while (low<high&&input[high]>=temp)high--;
		input[low] = input[high];
		while (low < high&&input[low] <= temp)low++;
		input[high] = input[low];	
	}
	input[low] = temp;
	return low;
}


// 找出序列中出现次数超过一半的数字:使用划分的方法
// 找出超过一半的数字相当于找中位数,可以用快排的思想来做
void SurpassHalfNumber2(vector<int> input) {

	int start = 0;
	int end = input.size()-1;
	int pos = Partition(input, start, end);
	while (pos != input.size()/2&&start<=end) {
		if (pos < input.size()/2) {			
			start = pos + 1;
			pos = Partition(input, start, end);
		}
		else {
			end = pos - 1;
			pos = Partition(input, start, end);
		}	
	}
	cout << "number:" << input[pos]<<endl;
}

总结:两种解法的时间复杂度均为O(n),但是第二种解法会改变数组元素的位置,如果要求不能改变数组元素的位置,则只能用第一种解法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值