【问题标题】:Return the number of array elements in order that satisfy a condition返回满足条件的数组元素个数
【发布时间】:2018-09-16 18:18:24
【问题描述】:

我试图让我的函数返回数组索引的最大数量,当按顺序相加时,等于 0 - 不容易解释,但下面是一些带注释的测试示例:

longestSlice([-1, -1, 1, -1, 1, 0, 1, -1, -1]); // should return 7 (slice starts at 2nd position)
longestSlice([1, 1, -1, -1, -1, -1, -1, 1, 1]); // should return 4 (both the first four elements and the last four elements)
longestSlice([-1, -1, -1, -1, 1, 0, 1, -1, -1]); // should return 5 (slice starts at 4th position)

我的函数正确返回前 2 个测试,但不是第 3 个测试。我认为我的逻辑是错误的,但我一生都无法弄清楚解决它的逻辑。如何实现?我试过了:

// if a[i] + a[i+1] = 0, count++, then if a[i-1] + a[i+2] = 0, count++ etc - won't work
// if a[i] + a[i+1] + a[i+2] + ... + a[i+n/2] > n (or -n) - won't work
// tests considering: count no of -1, 0 and 1 instances

另外,有人可以解释一下如何轻松解决这种情况,因为它看起来比看起来更困难,而且我对 javascript 还比较陌生。感谢您在这里的任何建议。

function longestSlice(arr) {

	var N = arr.length;
	var totalSum = 0, sliceLength = 0;
	var slices = [];

	if (N < 1) {
		throw new RangeError('Bad Input - Function Aborted');
	} else {
		// continue...
		for (i = 0; i < N; i++) {
			totalSum += arr[i];
			sliceLength++;
			console.log('totalSum: ' + totalSum + '\nsliceLength: ' + sliceLength);
			
			if (totalSum === 0) {
				slices.push(sliceLength);
				sliceLength = 0;
				console.log('sliceLength reset to: ' + sliceLength);
			}
		}
		return slices;
	}
}

【问题讨论】:

    标签: javascript algorithm


    【解决方案1】:

    好吧,让我们这样理解:

    假设我们有一个数组长度N,数组的总和是X。现在...

    如果X 为零,则其N(数组长度)是最大数字,总和为0

    如果X 不为零,那么可能有一些数字(假设有 K 个数字)的总和为值 X,结果长度应为 N-K 总和的最大数字数为零。如果没有K (1&lt;=K&lt;=N) 的数字形式0 (by sum),则数组不能通过任何组合的总和产生零。

    好吧,如果有点混乱,让我们举个例子。

    假设一个长度为 75 的数组(所以,N 在这里是 75

    现在,假设 73 数字(特定组合)的最大总和等于 0(所以这里 K73)并且总数组总和是 15 所以肯定它的总和休息2 数字(在其他73 数字总和为0 的组合中)

    所以,我们可以搜索和值15的最小组合,而不是搜索和值0的最长组合(这是整个数组和,这里用前面语句中的X表示)我们可以只需减去输入的length 即可得到结果。在这之前,我们可以

    1. 避免递归
    2. 避免生成最大长度结果的组合(例如,最大长度73, 找到长度2 及其总和的所有组合后退出)
    3. 重新使用上一个组合的结果来生成下一个长度 组合
    4. 当我们发现 sum 为X(整个数组的总和)时退出 最小可能的组合

    那么,算法的最后一步:

    1. 第一步:sum 向上数组
    2. 检查sum是否为0返回array的长度
    3. 如果sum 不为零,则从长度1 的组合开始。为了 长度1 我们可以避免计算sum,因为长度1 所以 item 本身将是该组合的sum 值(1)。所以 如果发现项目具有价值X (the entire sum) 我们禁止休息和 返回&lt;Inputs length - 1&gt; 如果未找到X,则开始生成 2 个数字的所有组合并计算总和的过程。如果我们发现总和是X,那么我们可以打破并返回N - 2。如果所有使用长度2 处理的组合仍然没有找到X,我们可以传递所有长度2 的组合并生成所有长度3 的组合,然后生成4 等等。每当我们得到结果(总和值等于X)时,我们都会相应地中断并返回(N - 3)或(N - 4)。

    现在,继续创建长度为2, 3, 4...的组合

    我们可以创建值的组合,但是由于值可以重复,这将难以管理,因此我们将创建indexes 的组合,并且我们可以随时查找输出输入数组中的值以生成总和。

    假设一个数组包含4 元素,所以我们可以有1, 2, 3 and 4 的组合

    让我们生成索引并在下次使用:

    let array = [-15, 10, 5, 13],
      cm;
    
    function comboNext(a, L) {
      let res = [], i;
      a.forEach(p => {
        let last = p[p.length - 1], i;
        for (i = last + 1; i < L; i++) {res.push(p.concat(i)); }
      })
      return res;
    }
    
    //this is not part of algo, just to format properly
    //to display the combinations properly
    function format(c) {
      return c.map(n=>n.join('+')).join(',\n')
      }
    
    cm = array.map((v, i) => [i]); //just indexes for 1 length combos
    console.log(format(cm) + ' => All of length 1');
    
    
    cm = comboNext(cm, array.length);
    console.log(format(cm) +  ' => All of length 2');
    
    
    cm = comboNext(cm, array.length);
    console.log(format(cm) +  ' => All of length 3');
    
    cm = comboNext(cm, array.length);
    console.log(format(cm) +  ' => All of length 4');

    因为,我们正在重新使用以前的组合,使用每个组合的最后一个索引,然后从那个位置开始

    避免重复组合,并选择所有唯一的组合 它总是会推动增量索引,因此我们可以选择最后一个(它将是更大的)并直接处理而不是在组合中搜索元素并在元素存在时避免该组合。这种情况永远不会到来。例如,您有索引0 1, 2 的组合,而您正在处理2,则无需处理。您可以立即从3 开始处理。同样,如果您有索引组合1,2, 5,您必须为此从6 处理,不要担心3,4 在其他组合中它们会像2,3,4 这样在处理5 时会生成@ 987654385@ 和 2,3,4,6 相应地。所以你不会错过任何东西。

    现在,让我们来实现。我们将返回一个具有success 标志和上次处理的组合的对象,而不是返回原始组合数组。我们将使用简单的for loop 来在任何时间点做exit/break/return

    function longestSlice(arr) {
      let len = arr.length,
        sum = arr.reduce((a, b) => a + b),
        sumAtIndexes = idxs => idxs.reduce((s, e) => s + arr[e], 0),
        comboFor1 = a => {
          let i, combo = [];
          for (i = 0; i < len; i++) {
            if (a[i] === sum) {
              return {
                success: true,
                length: len - 1
              }
            } else {
              combo.push([i])
            }
          }
          return {
            success: false,
            combo,
            length: 0
          };
        },
        comboForN = prevCombos => {
          let combo = [],
            i, com;
          for (i = 0; i < prevCombos.length; i++) {
            let p = prevCombos[i],
              last = p[p.length - 1],
              j;
            for (j = last + 1; j < len; j++) {
              com = p.concat(j);
              if (sumAtIndexes(com) === sum) {
                return {
                  success: true,
                  length: len - com.length
                }
              }
              combo.push(p.concat(j))
            }
          }
          return {
            success: false,
            combo,
            length: 0
          }
        },
        counter = 1,
        combosWithFlag;
    
      if (sum === 0) {
        return len;
      }
    
      combosWithFlag = comboFor1(arr);
    
      for (counter = 2; !combosWithFlag.success && counter <= len; counter++) {
        combosWithFlag = comboForN(combosWithFlag.combo);
      }
      return combosWithFlag.length;
    }
    
    console.log(longestSlice([-1, -1, 1, -1, 1, 0, 1, -1, -1]))
    console.log(longestSlice([1, 1, -1, -1, -1, -1, -1, 1, 1]));
    console.log(longestSlice([-1, -1, -1, -1, 1, 0, 1, -1, -1]));
    console.log(longestSlice([10,20,30,40,-100,-50,25,30, 15,17,3]))
    请注意,我使用了 2 个不同的函数,1 个用于创建长度为 1 的组合(如果非常简单,我们可以避免在那里求和),另一个用于创建任何长度的组合并输入索引的最后一个长度组合。

    【讨论】:

      【解决方案2】:

      你基本上需要一个双循环..

      外循环将从第一个数组元素开始。

      然后子循环从这个开始循环到结束,并保持一个总和,当总和等于0时,它检查它是否是最大的并存储。

      下面的例子..

      function longestSlice (a) {
        let longest = 0;
        for (let s = 0; s < a.length; s ++) {
          let sum = 0;
          for (let sl = s; sl < a.length; sl ++) {
            sum += a[sl];
            if (sum === 0) {
              const t = sl - s + 1;
              if (t > longest) longest = t;
            }
          }
        }
        return longest;
      }
      
      
      const ret = [
        longestSlice([-1, -1, 1, -1, 1, 0, 1, -1, -1]),
        longestSlice([1, 1, -1, -1, -1, -1, -1, 1, 1]),
        longestSlice([-1, -1, -1, -1, 1, 0, 1, -1, -1])
      ];
      
      console.log(ret);

      【讨论】:

        【解决方案3】:
        function longestSlice(arr) {
          let longestSliceLength = 0
          for (let i = 0; i < arr.length - longestSliceLength; i++) {
            let sum = 0
            let currentSliceLength = 0
            for (let j = i; j < arr.length; j++) {
              currentSliceLength++;
              sum += arr[j]
              if (!sum && longestSliceLength < currentSliceLength) {
                longestSliceLength = currentSliceLength
              }
            }
          }
          return longestSliceLength
        }
        

        有人可以解释一下如何轻松解决这种情况

        嗯,我们需要一个算法来解决这个问题,并且没有任何时间/空间限制。我们该如何解决这个问题?最长的子序列应该小于序列本身(显然,有限对象的一部分小于或等于对象大小)。然后让我们尝试所有连续的子序列,从最大的子序列开始(从第一个元素开始,但如果我们愿意,我们可以从另一端开始)。添加数字,计算总和以及总和中已有元素的数量。当总和为零时 - 我们有一个满足要求的子序列。如果它是迄今为止我们看到的最大长度,我们会保存它的长度。重复。希望这会有所帮助。

        【讨论】:

          【解决方案4】:

          之前已经实现过类似的算法。可以递归完成

          function longestSlice(arr, acc=0) {
          
              let N = arr.length;
          
              if (N == 0) {
                  return acc
              } else {
                  let sum = 0
                  let bestSoFar = 0;
                  for (let i =0; i < N; i++) {
                      sum += arr[i]
                      if (sum ==0) {
                          bestSoFar = i+1;
                      }
                  }
          
                  return  longestSlice(arr.slice(1,N), Math.max(bestSoFar, acc));
              }
          }
          
          
          longestSlice([-1, -1, -1, -1, 1, 0, 1, -1, -1]);    // returns 5
          

          【讨论】:

          • 我真的很喜欢您的解决方案,但我不明白一件事 - 这实际上是如何工作的?因为在这种情况下,总和永远不会是 0! (for循环之前除外)
          • @user8758206 是的,sum 应该继续累加,直到到达输入数组的末尾。在这个过程中,每次 sum 变为零,它都会将当前长度(迄今为止最好的切片)记录到bestSoFar。这样,它就不会在到达最长的切片之前停止探索。
          猜你喜欢
          • 1970-01-01
          • 2017-08-09
          • 2011-04-16
          • 2018-07-26
          • 2012-01-14
          • 2021-12-15
          • 2019-06-12
          • 1970-01-01
          相关资源
          最近更新 更多