同时寻找最大值和第二大值 锦标赛算法

问题:在一个数组中同时寻找最大值和第二大值,这里假设数组的元素个数n大于2

方法一:遍历数组,设置max和secondMax标志,如果有大于max的就更新max,如果有小于max但是大于secondMax的就更新secondMax。

比较次数:在a[0]和a[1]中找出临时的max和secondMax需要一次比较。在剩下的n-2个数中最坏时需要同max和secondMax分别比较,总共比较2(n-2),所以总的比较次数为1+2(n-2)。代码如下:

void traverse_find(int a[],int len,int *max,int *secondMax){
	int maxt,secondMaxt;
	int i;

	if(a[0]<a[1]){     //选出临时的最大和第二大值,需要一次比较
		maxt = a[1];
		secondMaxt = a[0];
	}else{
		maxt = a[0];
		secondMaxt = a[1];
	}

	for(i=2;i<len;i++){
		if(a[i]>maxt){<span style="white-space:pre">	</span>
			secondMaxt = maxt;
			maxt = a[i];
		}else if(a[i]>secondMaxt){  //这种情况是需要两次比较
			secondMaxt = a[i];
		}
	}
	*max = maxt;
	*secondMax = secondMaxt;
}

方法二:使用锦标赛算法,设置一个额外的大小为2n的数组(在优化的算法中额外数组的大小为n就可以)b,首先将原数组的n个数复制到额外数组的后n个位置,将额外数组b理解为一个类似于最大堆的结构,然后从倒数第二位置开始,以此选择两个数的较大者放到父亲节点中,依次进行,直到到根节点(b[1]),那么b[1]一定是这n个数中的最大值,第二大值一定同最大值比较过且输给了最大值,因此找第二大值时只需要从直接输给最大值的元素中去找,直接输给最大值的元素的个数有“lgn的上界”个;找最大值需要n-1次比较,找第二大值需要“lgn的上界”-1次比较,所以一共需要n+"lgn的上界"-2次比较,实用敌手策略可以证明在最坏情况下这是寻找第二大值的最少的比较次数。

示意图如下,其中红色数字表示最大值的上升过程,黄色节点表示直接输给最大值的元素,找第二大值就从这些黄色节点中寻找。



    1):使用额外空间为2n的锦标赛算法,这个算法直接申请空间为2n的数组,并直接将已有数组复制到额外数组的后n个位置中:

void tournamet(int a[],int len,int *max,int *secondMax){
	int aLen = 2*len;
	int *ta = (int*)malloc(sizeof(int)*aLen);
	int i;
	int secondLarge = -1;

	for(i=len;i<aLen;i++){
		ta[i] = a[i-len];
	}

	for(i=aLen-2;i>=2;i-=2){
		ta[i/2] = max(ta[i],ta[i+1]);
	}
	*max = ta[1];  //ta[1]即是保存的最大值
	
	//下面寻找第二大值,在直接输给最大值的的数中找
	for(i=1;2*i<aLen;){
		if(ta[2*i]==ta[i]){
			if(secondLarge<ta[2*i+1])
				secondLarge = ta[2*i+1];
			i = 2*i;
		}else{
			if(secondLarge<ta[2*i])
				secondLarge = ta[2*i];
			i = 2*i+1;
		}
	}	
	*secondMax = secondLarge;
}
  2):使用额外空间为n的锦标赛算法,这个算法直接使用原来的数组作为堆的叶子节点,在寻找的过程中如果超过额外数组的大小就从原始数组中寻找,最大下标不超过2n。算法如下,注意存在一个节点的左孩子和右孩子分别在两个数组中情况:

void tournamet_modify(int a[],int len,int *max,int *secondMax){

	int aLen = 2*len;
	int *ta = (int*)malloc(sizeof(int)*len);
	int i;
	int secondLarge = -1;
	
	for(i=aLen-2;i>=2;i-=2){
		if(i>=len){
			ta[i/2] = max(a[i-len],a[i+1-len]);
		}else if(i==len-1){
			ta[i/2] = max(a[0],ta[i]);
		}else{
			ta[i/2] = max(ta[i],ta[i+1]);
		}
	}

	*max = ta[1];  //ta[1]即是保存的最大值

	//下面寻找第二大值,在直接输给最大值的的数中找
	for(i=1;2*i<aLen;){
		if(2*i<len-1){
			if(ta[2*i]==ta[i]){
				if(secondLarge<ta[2*i+1])
					secondLarge = ta[2*i+1];
				i = 2*i;
			}else{
				if(secondLarge<ta[2*i])
					secondLarge = ta[2*i];
				i = 2*i+1;
			}
		}else if(2*i==len-1){
			if(ta[2*i]==ta[i]){
				if(secondLarge<a[0])
					secondLarge = a[0];
				i = 2*i;
			}else{
				if(secondLarge<ta[2*i])
					secondLarge = ta[2*i];
				i = 2*i+1;
			}
		}else{
			if(a[2*i-len]==ta[i]){
				if(secondLarge<a[2*i-len+1])
					secondLarge = a[2*i-len+1];
				i = 2*i;
			}else{
				if(secondLarge<a[2*i-len])
					secondLarge = a[2*i-len];
				i = 2*i+1;
			}
		}
	}	
	*secondMax = secondLarge;
}

上面三个方法可以使用下面main函数调用:

void main(){
	
	int a[] = {100,23,43,6,123,8,8,99,20};
	int max,secondMax;
	int maxt,secondMaxt;
	tournamet_modify(a,9,&max,&secondMax);
	traverse_find(a,9,&maxt,&secondMaxt);
	
	printf("max:%d,second max:%d\n",max,secondMax);
	printf("max:%d,second max:%d\n",maxt,secondMaxt);
	
	printf("\n");
}


  • 6
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
锦标赛算法是一种基于树形结构的算法,可以一组数据最大值。在第二大元素时,可以稍作修改。 具体思路是:首先将所有元素比较,将较小的元素标记为输者,较大的元素标记为胜者。然后,将所有胜者再次比较,得到新的胜者。重复此过程,直到只剩下一个元素,这个元素就是最大值。此时,我们需要回溯查找次大值,具体方法是: 1.在每次比较,如果胜者和输者不同,将输者加入到一个备选元素的集合。 2.在最后剩下一个元素的时候,备选元素集合最大值就是次大值。 以下是C++代码实现: ```c++ #include <iostream> #include <vector> using namespace std; // 比较个元素,返回胜者 int compare(int a, int b) { return a > b ? a : b; } // 从胜者集合查找次大值 int find_second_max(vector<int>& winners) { int second_max = INT_MIN; for (int i = 0; i < winners.size(); i++) { if (winners[i] > second_max) { second_max = winners[i]; } } return second_max; } // 使用锦标赛算法查找第二大元素 int find_second_max(vector<int>& nums) { int n = nums.size(); vector<int> winners(n); // 初始化胜者 for (int i = 0; i < n; i++) { winners[i] = nums[i]; } // 逐轮比较 while (n > 1) { // 计算胜者数量 int m = n / 2; if (n % 2 != 0) { m++; } // 每组比较 for (int i = 0; i < m; i++) { int j = i + m; if (j >= n) { j = i; } winners[i] = compare(winners[i], winners[j]); } // 更新元素数量 n = m; } // 查找次大值 vector<int> candidates; for (int i = 0; i < nums.size(); i++) { if (nums[i] != winners[0]) { candidates.push_back(nums[i]); } } return find_second_max(candidates); } int main() { vector<int> nums = {3, 5, 2, 7, 1, 8, 4, 6}; int second_max = find_second_max(nums); cout << "Second max: " << second_max << endl; return 0; } ``` 这个算法的时间复杂度为O(n),空间复杂度为O(n)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值