解决接雨水问题

 

 

先说说基本思路

其中list集合里面,只遍历false的元素,如果遍历完了会赋值为true,如图listBool集合绿色表示true,红色表示false,这个listBool集合与list集合有对应关系。

图1

图2

图3

 

根据以上3个图片,找出规律

① 在orderlyList里面搜索所有false的元素,而且要从最大的开始搜索。搜索一个最大的元素和一个次大的元素。一开始,最大的当然是9了,次大的元素还是9,那是因为9有2个

② 在图一中,遍历最大的元素到次大的元素,而且两个元素之间的索引差值要最大。遍历的时候,累加雨水(9-2)+(9-7)+(9-4)+(9-0)+(9-8)=  24 

③ 遍历完第一轮以后,因为9已经被遍历过一次了,所以9的个数应该从listNumCountMap集合中减1。现在,最大的元素是9,而次大的元素是8,那么我们就继续遍历从9到8之间的元素,并累加雨水,如图2。(8-0)+(8-3)=  13

④ 接下来,最大的元素是8,次大的元素为7。从元素8到元素7之间遍历。此时累加雨水时,不需要累加之前遍历过的元素,(7-2)+(7-5)+(7-3)= 11⑤ 这一步开始,你需要自己画一画图了。这时候,7就是最大元素,6是次大元素,那么遍历7到6之间的元素,累加雨水,

(6-0)=6

⑥ 此时,最大元素为6,此大元素为2,这时候要注意了,因为元素2最初有3个,已经被遍历了2个了,第二个元素2已经遍历了,所以不能再用来积累雨水了,于是要取最后一个元素2用来累加雨水。从元素6到元素2之间遍历,累加雨水

(2-0)+(2-1)+(2-0)= 5

这时候,list集合只剩下一个元素6没有遍历了,其它全部都是true

我们来看看,把累加的雨水加起来,等于多少呢?

24+13+11+6+5=59

 

用代码验证一下对不对,行不行。

package rain2;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import rain2.QuickList;

public class GetRain2 {
	//用来记录list集合有哪些元素(不重复),并且从大到小排列
	public static List<Integer> orderlyList ;
	//用来记录list集合的某个元素是否全部遍历完了,例如元素6全部遍历完了,元素6对应的索引就会被赋值为true
	public static boolean[] orderlyBool;
	//用来记录list集合中的某个元素是否遍历过,做个标记,一旦被遍历,会被赋值为true
	public static boolean[] listBool;
	//记录累加的雨水
	public static int count;
	
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		//用来测试性能
//		Random random = new Random();
//		
//		for (int i = 0; i < 500000; i++) {
//			list.add(random.nextInt(100));
//		}

		list.add(7);
		list.add(2);
		list.add(5);
		list.add(3);
		list.add(9);
		list.add(2);
		list.add(7);
		list.add(4);
		list.add(0);
		list.add(8);
		list.add(9);
		list.add(0);
		list.add(3);
		list.add(8);
		list.add(0);
		list.add(6);
		list.add(0);
		list.add(1);
		list.add(0);
		list.add(2);


		long startTime = System.nanoTime();
		System.out.println("能装下水滴的数量:"+GetRain2.getRain(list));
		long endTime = System.nanoTime(); 
		System.out.println("程序运行时间:" + (endTime - startTime) + "ns"); //输出程序运行时间
		System.out.println("程序运行时间:" + (endTime - startTime)/1000000 + "ms");
	}
	
	public static int getRain(List<Integer> list) {
		
		Set<Integer> set = new HashSet<Integer>();
		set.addAll(list);
		orderlyList = new ArrayList<Integer>(set);
		QuickList.sort(orderlyList);
		System.out.println(orderlyList);
		
		orderlyBool = new boolean[orderlyList.size()];
		listBool = new boolean[list.size()];
		//赋初始值为全部false
		Arrays.fill(orderlyBool, false);
		Arrays.fill(listBool, false);
		
		
		List<Integer> pointList = new ArrayList<Integer>();
		Map<Integer, Integer> listNumCountMap = new HashMap<Integer, Integer>();
		for(int i=0;i<list.size();i++) {
			if(!listNumCountMap.containsKey(list.get(i))) {
				listNumCountMap.put(list.get(i), 1);
			}else {
				listNumCountMap.put(list.get(i), listNumCountMap.get(list.get(i))+1);
			}
		}
		System.out.println(listNumCountMap);
		int point = 0;
		int i=0;
		while(i<orderlyBool.length) {
			
			if(orderlyBool[i]==false) {
				
				pointList.add(orderlyList.get(i));
				point++;
				
				if(listNumCountMap.get(orderlyList.get(i))<=1) {
					i++;
				}
				
				if(pointList.size()>=2) {
					int minIndex = 0;
					int maxIndex = 0;
					Integer pre = pointList.get(point-2);
					Integer aft = pointList.get(point-1);
					boolean aGtb = list.indexOf(pre) <= (listBool[list.indexOf(aft)]?list.lastIndexOf(aft):list.indexOf(aft));
					minIndex = list.indexOf(aGtb?pre:aft);
					maxIndex = aGtb?list.lastIndexOf(aft):list.lastIndexOf(pre);
					for(int j=minIndex;j<maxIndex;j++) {
						if(listBool[j]==false) {
							
							count = count + aft - (aft>=list.get(j)?list.get(j):aft);
							if(aGtb){
								listBool[j]=true;
								listNumCountMap.put(list.get(j),listNumCountMap.get(list.get(j))-1);
								
								if(listNumCountMap.get(list.get(j))==0) {
									orderlyBool[orderlyList.indexOf(list.get(j))]=true;
								}
							}else {
								if(j!=minIndex){
									listBool[j]=true;
									listNumCountMap.put(list.get(j),listNumCountMap.get(list.get(j))-1);
									if(listNumCountMap.get(list.get(j))==0) {
										orderlyBool[orderlyList.indexOf(list.get(j))]=true;
									}
								}
								
								if(j==(maxIndex-1)) {
									listBool[maxIndex]=true;
									listNumCountMap.put(list.get(maxIndex),listNumCountMap.get(list.get(maxIndex))-1);
									if(listNumCountMap.get(list.get(maxIndex))==0) {
										orderlyBool[orderlyList.indexOf(list.get(maxIndex))]=true;
									}
								}
							}
						
						}
						
					}
				
				}
				
			}
			if(i==orderlyBool.length) {
				break;
			}
			if(orderlyBool[i]==true) {
				i++;
			}

		}
	
		return count;
	}
}

我们发现,结果应该是对的

 

如果按照leetcode官网的题库里面的输入来跑这个代码

 

测试一下该算法性能好不好,看一看性能行不行:

加入50万个元素,进行接雨水,性能还算过得去吧~

 

 

自己想出的办法过于复杂化了,于是有去学习更加好的方法---双指针夹逼的方法

leftHeight和rightHeight分表表示图中小圆点位置的值,height[left]表示当前左指针的值,height[right]表示当前右指针的值

图一

遵循 " 谁小谁移动 " 的原则,right指针从图一到图二的变化,都比left指针小,所以一直移动累加。怎么累加法?

right往右扫的过程累加雨水:( rightHeight - 0)+(rightHeight - 1)+(rightHeight - 0)相当于代码

result = result + rightHeight - height(right)

以此类推

图二

 

但是指针到了6这个元素,比原来的rightHeight==2还要大,这时候就不要再累加雨水了,于是更新小圆点的位置,即rightHeight更新为6了,相当于代码中的break语句,回到最外层的while循环

 

图三

 

right指针移动到8时,比原本的rightHeight==6还要大了,于是更新小圆点的位置,即更新rightHeight值

此时,终于left指针比right指针小了,轮到left指针往右移动

图四

 

 

从图四到图五的过程中,也在累加雨水,这时leftHeight==7,(leftHeight - 2)+(leftHeight - 6)+(leftHeight - 5)+(leftHeight - 3)

相当于代码,

result = result + leftHeight - height[left]

 

图五

当left指针到达9,比原本的7还要大了,所以又更新小圆点的位置,leftHeight变为9了

图6

 

图7图8图9我就不画了,规律是这样的,总之遵循" 谁小谁移动 ",而且遵循 " 当前右(左)指针小于 rightHeight(leftHeight)就累加雨水,否则更新小圆点的位置" 的原则。至此,解决接雨水的问题。

推荐使用双指针方法,效率挺高,加入500万个元素,30毫秒以内就跑完了,非常非常快

package demo;

public class Solution2 {
 
	public static void main(String[] args) {
		int[] height = {7,2,5,3,9,2,7,4,0,8,9,0,3,8,0,6,0,1,0,2};
//		int[] height = new int[5000000];
//		Random random = new Random();
//		for(int i=0;i<height.length;i++) {
//			height[i]=random.nextInt(100);
//		}
		
		Solution2 s2 = new Solution2();
		long startTime = System.nanoTime();
		System.out.println(s2.trap(height));
		long endTime = System.nanoTime(); 
		System.out.println("程序运行时间:" + (endTime - startTime) + "ns"); //输出程序运行时间
	}
	
	public int trap(int[] height) {
		int n = height.length;
		int result = 0;
		if(n == 0 || n == 1) {
			return result;
		}
		int left = 0;
		//variable left represents the left border of the area where can contain water
		while(left < n - 1 && height[left + 1] >= height[left]) {
			left++;
		}
		int right = n - 1;
		//variable right represents the right border of the area where can contain water
		while(right >= 1 && height[right - 1] >= height[right]) {
			right--;
		}
		while(left < right) {
			int leftHeight = height[left];
			int rightHeight = height[right];
			if(leftHeight <= rightHeight) {
				while(left < right) {
					left++;
					if(height[left] < leftHeight) {
						result += leftHeight - height[left];
					}else {
						break;
					}
				}
			}else {
				while(left < right) {
					right--;
					if(height[right] < rightHeight) {		
						result += rightHeight - height[right];
					}else {
						break;
					}
				}
			}
		}
		return result;
	}

}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值