不同高度墙的盛水问题两种算法C++实现

22 篇文章 0 订阅

         题目在伯乐在线中看到的,据说是twitter面试时的题目,感觉蛮有意思的。于是就将两种算法都用C++实现了,就当练练手吧。


        “在这个图片里我们有不同高度的墙。这个图片由一个整数数组所代表,数组中每个数是墙的高度。上边的图可以表示为数组[2,5,1,2,3,4,7,7,6]”

       “假如开始下雨了,那么墙之间的水坑能够装多少水呢?”


     “以1×1的方块为单位计算容积。所以,在上边的图中下标为1以左的都会漏掉。下标7以右的也会漏掉。剩下的只有在1和6之间的一坑水,容积是10”

        作者已经给出了两种算法,逻辑如下:

        两次遍历的算法:

        如果我们从左至右遍历列表,每个下标水的量最多是到现在为止最大的数。这表示如果我们已知右边有相等或更大的,我们可以知道存下的水有多少。反向遍历的时候也一样:如果我们知道左边有比右边最大的数更大的,我们装水是毫无问题的。

        基于这个想法,一个解决方法是:先找到最大值,从左遍历到最大值,然后从右遍历到最大值。这个方法需要两次遍历:一次找到最大值,另一次分成了两个子遍历。

        一次遍历的算法:

        一次遍历的方法通过两端的指针相向移动避免了寻找最大值。如果(左指针找到的左指针以左的最大值)小于(右指针找到右指针以右的最大值),将左指针向右移动一位。否则右指针向左移动一位。重复过程直到两个指针相遇。(解释起来很麻烦,但是代码很简单)

        下面是用C++的算法实现:

        两次遍历:

/************************************************************************/
/* 两次遍历的方法                                                                     */
/************************************************************************/
int calculateVolume(int arrays[],int length){
	
	//先求出数组中的最大值,第一次遍历
	int max=0;//最大值
	int max_pos;//最大值对于数组的下标
	for (int i=0;i<length;i++)
	{
		if (max<arrays[i])
		{
			max=arrays[i];
			max_pos=i;
		}
	}

	int max_left=0;//从左边开始遍历时的极大值
	int max_right=0;//从右边开始遍历时的极大值
	int volume=0;//容积

	//从左边开始向右遍历到最大值处
	for(int i=0;i<max_pos;i++){
		//不断更新左边的极大值
		if (max_left<arrays[i])
		{
			max_left=arrays[i];
		}
		//否则,加上新增加的容积
		else{
			volume+=(max_left-arrays[i]);
		}
	}

	//从最右边开始向左遍历到最大值处
	for(int j=length-1;j>max_pos;j--){
		//不断更新右边的极大值
		if (max_right<arrays[j])
		{
			max_right=arrays[j];
		}
		//否则,加上新增加的容积
		else{
			volume+=(max_right-arrays[j]);
		}
	}
	
	return volume;
}

测试:

void main(){
	int arrays1[]={2,5,1,2,3,4,7,7,6};
	int arrays2[]={2,5,1,3,1,2,1,7,7,6};
	int arrays3[]={6,1,4,6,7,5,1,6,4};
	std::cout<<"arrays1 voleme: "<<calculateVolume(arrays1,9)<<std::endl;
	std::cout<<"arrays2 voleme: "<<calculateVolume(arrays2,10)<<std::endl;
	std::cout<<"arrays3 voleme: "<<calculateVolume(arrays3,9)<<std::endl;
}


        一次遍历的算法:

/************************************************************************/
/* 一次遍历的方法                                                                     */
/************************************************************************/
int calculateVolumeEx(int  arrays[],int length){
	int pos_left=0;//从左开始遍历的指针的下标
	int pos_right=length-1;//从右开始遍历的指针的下标
	int max_left=arrays[pos_left];//从左开始遍历的过程中的极大值
	int max_right=arrays[pos_right];//从右开始遍历的过程中的极大值
	int volume=0;//保存容积的变量

	//当右边的指针的下标大于坐标指针的下标时,重复循环
	//循环调出时即遍历完了数组中的所有元素
	while(pos_right>pos_left){

		//如果左指针找到的极大值小于右指针找到的极大值,左指针右移
		if (max_left<max_right)
		{
			pos_left=pos_left+1;

			//当左指针指向的新的元素大于之前找到的极大值时,用新的值替换极大值
			if (arrays[pos_left]>=max_left)
			{
				max_left=arrays[pos_left];
			}
			//否则总容积加上新的容积
			else{
				volume+=(max_left-arrays[pos_left]);
			}
		}
		//如果左指针找打的极大值大于或等于右指针找到的极大值,右指针左移
		else{
			pos_right=pos_right-1;
			int tmp_right=arrays[pos_right];//保存右指针指向的临时变量

			//当右指针指向的新的元素大于之前找到的极大值时,用新的值替换极大值
			if (arrays[pos_right]>=max_right)
			{
				max_right=arrays[pos_right];
			}
			//否则总容积加上新的容积
			else{
				volume+=(max_right-arrays[pos_right]);
			}
		}
	}

	return volume;

}

测试:

void main(){
	int arrays1[]={2,5,1,2,3,4,7,7,6};
	int arrays2[]={2,5,1,3,1,2,1,7,7,6};
	int arrays3[]={6,1,4,6,7,5,1,6,4};
	std::cout<<"arrays1 voleme: "<<calculateVolumeEx(arrays1,9)<<std::endl;
	std::cout<<"arrays2 voleme: "<<calculateVolumeEx(arrays2,10)<<std::endl;
	std::cout<<"arrays3 voleme: "<<calculateVolumeEx(arrays3,9)<<std::endl;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值