array专题3-一道题目不断分析就会慢慢有了思路

#697 Degree of an Array
我承认慢慢有了思路的前提是你要见过那些解法,否则怎么想也想不到。多做题目,就像是多看书一样重要。
问题:一个数组的度=这个数组中出现次数最多元素的出现次数。要找的是最短的子数组,而这个数组的度=原数组的度。
思路一:我肯定需要一次循环,找到数组的度;接着计算每个子数组,计算它们的度,找到和原数组的度相同的最短的子数组。每个子数组就是从下标0开始的子数组,从下标1开始的子数组…。所以有了如下代码。代码时间复杂度O(n^2),发生TLE。

public int findShortestSubArray(int[] nums) {
		// 数组的度
		Map<Integer, Integer> countMap = new HashMap<Integer, Integer>();
		int degree = 0;
		for (int num : nums) {
			if (countMap.get(num) == null) {
				countMap.put(num, 1);
			} else {
				countMap.put(num, 1 + countMap.get(num));
			}
			degree = Math.max(degree, countMap.get(num));
		}

		// 找子数组
		int minlength = nums.length;
		for (int start = 0; start < nums.length; start++) {
			Map<Integer, Integer> subCoutMap = new HashMap<Integer, Integer>();
			int subDegree = 0;
			for (int end = start; end < nums.length; end++) {
				int num = nums[end];
				if (subCoutMap.get(num) == null) {
					subCoutMap.put(num, 1);
				} else {
					subCoutMap.put(num, 1 + subCoutMap.get(num));
				}
				subDegree = Math.max(subDegree, subCoutMap.get(num));
				if(subDegree == degree){
					minlength = Math.min(end-start+1, minlength) ;
					break;
				}
			}
		}
		return minlength;
	}

思路二:需要把两层循环改为1层。观察例子中给出的子数组:[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2],符合条件的是[2,2]。既然数组的度是由出现次数最多的元素的频次贡献的,那子数组中肯定包含这个元素。要求最短,那子数组的起始元素和结束元素肯定都是这个元素。所以思路改为:
1 需要一次循环,找到数组的度;
2接着再循环找到这个度是由哪个元素贡献的。例如数组 [1, 2, 2, 3, 1]的度是2,是由元素2贡献的。找到2这个元素;
3最后要循环找到这个元素的起止位置,计算子数组的长度。
所以有了如下代码。注意的是:出现次数最多的元素可能不止一个。

public int findShortestSubArrayV2(int[] nums) {
		// 数组的度
		Map<Integer, Integer> countMap = new HashMap<Integer, Integer>();
		int degree = 0;
		for (int num : nums) {
			if (countMap.get(num) == null) {
				countMap.put(num, 1);
			} else {
				countMap.put(num, 1 + countMap.get(num));
			}
			degree = Math.max(degree, countMap.get(num));
		}
		List<Integer> elementList = new ArrayList<Integer>();
		for(Integer num : countMap.keySet()){
			if(countMap.get(num)==degree){
				elementList.add(num);
			}
		}

		int minLength = nums.length;
		for(int element : elementList){
			int subDegree = 0;
			int start = -1;
			for (int i = 0; i < nums.length; i++) {
				if(nums[i] == element){
					if(start == -1){
						start = i;
					}
					subDegree++;
					if(subDegree == degree){
						minLength = Math.min(minLength, i-start+1);
						break;
					}
				}
			}
		}
		return minLength;
	}

思路三:上面的三步有没有可以合并的呢?是不是可以在计算数组的度的时候,顺便记录下每个元素的起止位置呢?当然可以。第二步寻找出现次数等于数组度的元素,和计算子数组长度放在一起。于是有了以下代码。

public int findShortestSubArrayV3(int[] nums) {
		Map<Integer, Integer> countMap = new HashMap<Integer, Integer>();
		Map<Integer, Integer[]> numIndexMap = new HashMap<Integer, Integer[]>();
		int degree = 0;
		for (int i = 0; i < nums.length; i++) {
			int num = nums[i];
			if (countMap.get(num) == null) {
				countMap.put(num, 1);
			} else {
				countMap.put(num, 1 + countMap.get(num));
			}
			degree = Math.max(degree, countMap.get(num));
			if(numIndexMap.get(num)==null){
				numIndexMap.put(num, new Integer[]{i,i});
			}else{
				numIndexMap.get(num)[1] = i;
			}
		}
		int minLength = nums.length;
		for(int num : countMap.keySet()){
			if(countMap.get(num) == degree){
				minLength = Math.min(minLength, numIndexMap.get(num)[1] - numIndexMap.get(num)[0]+1);
			}
		}
		return minLength;
	}
一步一步改进自己的思路。从最直觉入手。改进的依据是观察标准答案的特征;缩短使用时间。

思路四:看了discuss。两个map合并为一个map,先准备基础数据再计算。不得不说,作者真是牛。作者代码更注重的细节是: M a p &lt; I n t e g e r , i n t [ ] &gt; n u m M a p Map&lt;Integer, int[]&gt; numMap Map<Integer,int[]>numMap 而不是$ Map<Integer, Integer[]> numMap $,我试过了,速度更快。map的get方法尽量调用一次(看我上面代码就知道,我不是这样做的)。作者在最后遍历的是numMap.values(),速度更快。

public int findShortestSubArrayV4(int[] nums) {
		if (nums.length == 0 || nums == null) return 0;
		Map<Integer, int[]> numMap = new HashMap<Integer, int[]>();
		for (int i = 0; i < nums.length; i++) {
			int num = nums[i];
			if (numMap.get(num) == null) {
				numMap.put(num, new int[]{1,i,i});
			} else {
				int[] temp = numMap.get(num);
				temp[0]++;
				temp[2]=i;
			}
		}
		int degree = 0;
		int minLength = nums.length;
		for(int[] values : numMap.values()){
			if(degree < values[0]){
				degree = values[0];
				minLength = values[2]-values[1] +1;
			}else if(degree == values[0]){
				minLength = Math.min(minLength, values[2]-values[1] +1);
			}
		}
		return minLength;
	}

思路5:第二遍刷题。观察到了需要找到出现最多次数元素最左边、最右边的位置。

 	public int findShortestSubArray(int[] nums) {
        Map<Integer,Integer> left = new HashMap<Integer,Integer>();
        Map<Integer,Integer> right = new HashMap<Integer,Integer>();
        Map<Integer,Integer> count = new HashMap<Integer,Integer>();
        int degree = 0; 
        for(int i=0;i<nums.length;i++){
            if(left.get(nums[i])==null) left.put(nums[i],i);
            right.put(nums[i],i);
            if(count.get(nums[i])==null)
                count.put(nums[i],1);
            else
                count.put(nums[i],count.get(nums[i])+1);
            degree = Math.max(degree,count.get(nums[i]));
        }
        int answer = nums.length;
        for(Integer num : left.keySet()){
            if(count.get(num)==degree){
                answer = Math.min(answer,right.get(num)-left.get(num)+1);
            }
        }
        return answer;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值