剑指offer:数组中出现次数超过一半的数字和最小的 k个数

本文介绍了如何找出数组中出现次数超过一半的数字,并探讨了几种高效查找数组中小的k个数的方法,包括使用集合、排序算法、最大堆等。

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

分析
遍历数组,使用 map 计数,超过一半即返回。有个问题:时间复杂度应该怎么算。

int MoreThanHalfNum_Solution(vector<int> numbers) {
	map<int, int> cnt;
	for (int i = 0; i < numbers.size(); ++i) {
		cnt[numbers[i]]++;
		if (cnt[numbers[i]] > numbers.size() / 2) {
			return numbers[i];
		}
	}
	return 0;
}

最小的 k 个数
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

分析
此题的做法很多种。
方法一:我的第一直觉就是用 set 。很简单,而且默认是排好序的。当然了,用 STL 都是一种蛮作弊的方法。我也不知道时间复杂度该怎么算 。
代码如下:

vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
	vector<int> input_k;
	if (k > input.size()) {
		return input_k;
	}
	set<int> A;
	for (int i = 0; i < input.size(); ++i) {
		A.insert(input[i]);
	}
	
	int count = 0;
	for(set<int>::iterator iter = A.begin(); iter != A.end() && count < k; ++iter) {
		input_k.push_back(*iter);
		count++;
	}
	return input_k;
}

方法二:使用排序算法,一般选择快速排序。C++ 中自带 sort() 和 qsort() 函数。就不写了。

方法三:因为只需要得到前 k 个数,而不用对它们进行排序。那么先将数组中的前 k 个数作为最小的.时间复杂度:O(n*k)
注意考虑特殊情况:k == 0

int findMax(vector<int> &A) {
	int max = A[0];
	int index = 0;
	for (int i = 0; i < A.size() - 1; ++i) {
		if (max < A[i + 1]) {
			max = A[i + 1];
			index = i + 1;
		}
	}
	swap(A[A.size() - 1], A[index]);
	return max;
}

vector<int> GetLeastNumbers_Solution2(vector<int> input, int k) {
	if(k > input.size() || k == 0){
		vecotr<int> A;
		return A;
	}
	vector<int> output_k(input.begin(), input.begin() + k);
	int max = findMax(output_k);
	for (int i = k; i < input.size(); ++i) {
		if (input[i] < max) {
			output_k[k - 1] = input[i];
			max = findMax(output_k);
		}
	}
	return output_k;
}

方法四:与方法三类似,只是使用最大堆,元素有 k 个。这样就不用每次都寻找 k 个元素中的最大的了。最大堆1可用 C++ 自带的 prioprity_queue 实现。时间复杂度:O(n*logk)。
代码如下:

vector<int> GetLeastNumbers_Solution3(vector<int> input, int k) {
	vector<int> output_k;
	if (k > input.size() || k == 0) {
		return output_k;
	}
	priority_queue<int, vector<int>, less<int>> heap;
	for (int i = 0; i < k; ++i) {
		heap.push(input[i]);
	}
	for (int i = k; i < input.size(); ++i) {
		if (input[i] < heap.top()) {
			heap.pop();
			heap.push(input[i]);
		}
	}
	while (!heap.empty()) {
		output_k.push_back(heap.top());
		heap.pop();
	}
	return output_k;
}

方法五
类似于快速排序,只是这里只是找元素,找到第 k 大的元素就可以了。时间为 O(n) + O(n/2) + … + O(1) = O(n) 。
代码如下(不懂为什么超时了):

vector<int> GetLeastNumbers_Solution4(vector<int> input, int k) {
	if (k > input.size() || k == 0) {
		vector<int> output;
		return output;
	}
	int p = 0;
	int r = input.size() - 1;
	int key_pos = k;
	int key = input[p];
	int i, j;
	while (1) {
		i = p; 
		j = r;
		key = input[p];
		while (i < j) {
			while (input[j] > key) --j;
			while (input[i] < key) ++i;
			swap(input[i], input[j]);
		}
		//input[i - 1] = key;
		if (k - 1 > i) {
			p = i + 1;
		}
		else if (k - 1 == i) {
			vector<int> output_k(input.begin(), input.begin() + k);
			return output_k;
			
		}
		else if (k - 1 < i) {
			r = i - 1;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值