leetcode每日一题

98 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

954 二倍数对数数组

题目

给定一个长度为偶数的整数数组 arr,只有对 arr 进行重组后可以满足 “对于每个 0 <= i < len(arr) / 2,都有 arr[2 * i + 1] = 2 * arr[2 * i]” 时,返回 true;否则,返回 false。

目的解析

根据这个题目的说明,我们可以得知,最终是将数组重组成一个索引值为偶数时,下一个索引值的value值为该value值的两倍的新数组

做法分析

①我们第一步肯定是想将数组从小到大排序,这样才能通过小值去找到符合条件的大值/n 但是这里存在着一个特殊的情况,就是负值的情况,对于负值我们需要将它从大到小排序

②如果只是排序就会发现一个问题,位置问题, 我们是根据大小排序的,存在着下一个偶数项的值刚好是上一个偶数项的值的两倍,例如排序得到的数组是【2,3,4,6】 要求的数组是【2,4,3,6】,我们就需要加多一个Map,用来记录每个值被使用过的情况,同时在循环的底部加一个索引值的查找,因为存在这这种情况【2,4,5,10】,我们直接遍历数组的前一半部分, 只能得到2 和 4 ,但是4是被使用过的了,所以就需要跳过4去到5这里

代码

  // 重组得到一个 每项偶数索引值等于它下一个索引值的二分之一 的新数组
  // 难度: 正负值的处理、是否需要排序,如果需要,怎么排,不需要,怎么做
  let leftStack = [] ,rightStack = [];
  const map = new Map();
  for(let i = 0; i < arr.length; i++){
    map.set(arr[i],map.has(arr[i]) ? map.get(arr[i]) + 1 : 1)
    if(arr[i] < 0){
      leftStack.push(arr[i])
    }else{
      rightStack.push(arr[i])
    }
  }
  leftStack = leftStack.sort((a,b) => b - a);
  rightStack = rightStack.sort((a,b) => a - b);
  let result = true;
  let l = 0,indexLeft = 0;
  let num = 0;
  while(l < (leftStack.length / 2)){
    num = leftStack[indexLeft] * 2;
    map.set(num / 2,map.get(num / 2) - 1)
    if(map.get(num)){
      map.set(num,map.get(num) - 1)
    }else{
      result = false;
      break;
    }
    l++;
    while(!map.get(leftStack[++indexLeft]) && indexLeft < leftStack.length - 1){}
  }
  // 负数组不对就直接退出可以了
  if(!result){
    return false;
  }
  let r = 0,indexRight = 0;
  while(r < (rightStack.length / 2)){
    num = rightStack[indexRight] * 2;
    map.set(num / 2,map.get(num / 2) - 1)
    if(map.get(num)){
      map.set(num,map.get(num) - 1)
    }else{
      result = false;
      break;
    }
    r++;
    while(!map.get(rightStack[++indexRight]) && indexRight < rightStack.length - 1){}
  }
 
  return result
};

优化

由于使用了Map,知道了数值和它在数组中有多少个

我们就把题目转成数值理解,就等于我需要判断数组是否存在这样的关系,0的次数为偶数,小的值和它两倍大的值的数量是否一致

优化代码

var canReorderDoubled = function(arr) {
  arr = arr.sort((a,b)=>{
    if(a < 0 && b < 0){
      return b - a
    }else{
      return a - b 
    }
  })
  const map = new Map();
  arr.forEach(item => {
    map.set(item,map.get(item) ? map.get(item) + 1 : 1)
  })
  for(let [key,value] of map){
    // 当前值已经使用过了
    if(value === 0) continue
    // 0的情况
    if(key === 0){
      if(value % 2 !== 0){
        return false
      }
    }else{
      // 该值不存在 或者 数量少
      if(map.get(key * 2) == undefined || map.get(key * 2) < value){
        return false
      }else{
        map.set(key,0);
        map.set(key * 2, map.get(key * 2) - value);
      }
    }
  }
  return true;
};

总结:优化后的代码更好理解,空间复杂度上也有优化,因为不需要用栈去区分当前的正负值了,排列好后,在Map中统计好数目就可以直接遍历使用,时间复杂度为数组中不同数字出现的次数