【问题标题】:JS HackerRank Jesse and Cookies - Heap problem times outJS HackerRank Jesse 和 Cookies - 堆问题超时
【发布时间】:2019-05-17 09:24:51
【问题描述】:

我正在尝试解决 Hackerrank 问题Jesse and Cookies

Jesse 喜欢饼干,并希望某些饼干的甜度大于价值 ????。为此,反复混合两种甜度最低的饼干。这将创建一个特殊的组合 cookie:

甜度 = (1 × 最不甜的饼干 + 2 × 2 次最不甜的饼干)。

这种情况一直持续到所有 cookie 的甜度 ≥ ????。

鉴于许多 cookie 的甜度,确定所需的最少操作数。如果不可能,返回-1。

示例

k = 9
A = [2,7,3,6,4,6]

最小值是 2、3。
删除它们,然后将 2 + 2 × 3 = 8 返回到数组。现在A = [8,7,6,4,6].
删除 4, 6 并将 4 + 2 × 6 = 16 返回到数组。现在A = [16,8,7,6].
删除 6、7,返回 6 + 2 × 7 = 20 和 A = [20,16,8,7]
最后,删除 8、7 并将 7 + 2 × 8 = 23 返回给 A。现在是 A = [23,20,16]
所有值均 ≥ ???? = 9,因此过程在 4 次迭代后停止。返回 4。

我找不到针对此问题的 JavaScript 解决方案或提示。我的代码似乎可以正常工作,只是它对一个大数组(输入大小 > 100 万)超时。

有没有办法让我的代码更有效率?我认为时间复杂度在线性和 O(n log n) 之间。

我的代码:

function cookies(k, A) {
  A.sort((a,b)=>a-b)
  let ops = 0;
  while (A[0] < k && A.length > 1) {
    ops++;
    let calc = (A[0] * 1) + (A[1] * 2);
    A.splice(0, 2);
    let inserted = false
    if (A.length === 0) { // when the array is empty after splice
      A.push(calc);
    } else {
      for (var i = 0; i < A.length && !inserted; i++) {
        if (A[A.length - 1] < calc) {
          A.push(calc)
          inserted = true
        } else if (A[i] >= calc) {
          A.splice(i, 0, calc);
          inserted = true
        }
      }
    }
  }
  if (A[0] < k) {
    ops = -1;
  }
  return ops;
}

【问题讨论】:

  • 您的代码是 O(n^2),因为您有两个嵌套的线性循环。提高效率的方法是使用heap,正如您的标题和标签所暗示的那样,但是您的代码没有使用,这对外部循环应该没有任何作用,但对于内部循环来说是奇迹。
  • 您需要一个更高效的优先级队列。见stackoverflow.com/questions/42919469/…

标签: javascript heap


【解决方案1】:

我使用 java 解决了它。你可以适应Javascript。

此代码不需要使用堆。它只适用于传递的同一个数组。为我通过了所有测试。

static int cookies(int k, int[] arr) {
    /*
     * Write your code here.
     */
    Arrays.sort(arr);
    int i = 0,
        c = arr.length,
        i0 = 0,
        c0 = 0,
        op = 0;
    while( (arr[i]<k || arr[i0]<k) && (c0-i0 + c-i)>1 ) {
        int s1 = i0==c0 || arr[i]<=arr[i0] ? arr[i++] : arr[i0++], 
            s2 = i0==c0 || (i!=c && arr[i]<=arr[i0]) ? arr[i++] : arr[i0++];
        arr[c0++] = s1 + 2*s2;
        op++;
        if( i==c ) {
            i = i0;
            c = c0;
            c0 = i0;
        }
    }

    return c-i>1 || arr[i]>=k ? op : -1;
}
  • 首先对数组进行排序。
  • 对于新计算的值,将它们存储在数组[i0-c0]范围内,这个新数组不需要排序,因为它已经排序了。
  • 当 array[i-c] 到达(i==c: true) 结束时,忘记它,并在 arr[i0-c0] 上工作。

【讨论】:

    【解决方案2】:

    确实是可以用堆高效解决的问题。由于 JavaScript 没有原生堆,只需实现自己的堆。

    您还应该处理巨大的输入,但大多数值都大于 k。这些不应该是堆的一部分——它只会使堆操​​作不必要地变慢。此外,当 cookie 被扩充时,它们仅在它们还不够好时才需要重新进入堆中。

    当堆最终只有一个值(小于k)时需要特别小心。在这种情况下,需要检查是否创建了任何好的 cookie(因此没有最终出现在堆中)。如果是这样,那么再通过一次操作就可以找到解决方案。但如果不是,则表示无解,应返回-1。

    这是一个 JavaScript 实现:

    /* MinHeap implementation without payload. */
    const MinHeap = { 
        /* siftDown:
         * The node at the given index of the given heap is sifted down in its subtree 
         * until it does not have a child with a lesser value. 
         */
        siftDown(arr, i=0, value=arr[i]) {
            if (i >= arr.length) return;
            while (true) {
                // Choose the child with the least value
                let j = i*2+1;
                if (j+1 < arr.length && arr[j] > arr[j+1]) j++;
                // If no child has lesser value, then we've found the spot!
                if (j >= arr.length || value <= arr[j]) break;
                // Move the selected child value one level up...
                arr[i] = arr[j];
                // ...and consider the child slot for putting our sifted value
                i = j;
            }
            arr[i] = value; // Place the sifted value at the found spot
        },
        /* heapify:
         * The given array is reordered in-place so that it becomes a valid heap.
         * Elements in the given array must have a [0] property (e.g. arrays). That [0] value
         * serves as the key to establish the heap order. The rest of such an element is just payload.
         * It also returns the heap.
         */
        heapify(arr) {
            // Establish heap with an incremental, bottom-up process
            for (let i = arr.length>>1; i--; ) this.siftDown(arr, i);
            return arr;
        },
        /* pop:
         * Extracts the root of the given heap, and returns it (the subarray).
         * Returns undefined if the heap is empty
         */
        pop(arr) {
            // Pop the last leaf from the given heap, and exchange it with its root
            return this.exchange(arr, arr.pop());
        },
        /* exchange:
         * Replaces the root node of the given heap with the given node, and returns the previous root.
         * Returns the given node if the heap is empty.
         * This is similar to a call of pop and push, but is more efficient.
         */
        exchange(arr, value) {
            if (!arr.length) return value;
            // Get the root node, so to return it later
            let oldValue = arr[0];
            // Inject the replacing node using the sift-down process
            this.siftDown(arr, 0, value);
            return oldValue;
        },
        /* push:
         * Inserts the given node into the given heap. It returns the heap.
         */
        push(arr, value) {
            // First assume the insertion spot is at the very end (as a leaf)
            let i = arr.length;
            let j;
            // Then follow the path to the root, moving values down for as long as they
            // are greater than the value to be inserted
            while ((j = (i-1)>>1) >= 0 && value < arr[j]) {
                arr[i] = arr[j];
                i = j;
            }
            // Found the insertion spot
            arr[i] = value;
            return arr;
        }
    };
    
    
    function cookies(k, arr) {
        // Remove values that are already OK so to keep heap size minimal
        const heap = arr.filter(val => val < k);
        let greaterPresent = heap.length < arr.length; // Mark whether there is a good cookie
        MinHeap.heapify(heap);
        let result = 0;
        while (heap.length > 1) {
            const newValue = MinHeap.pop(heap) + MinHeap.pop(heap) * 2;
            // Only push result back to heap if it still is not great enough
            if (newValue < k) MinHeap.push(heap, newValue);
            else greaterPresent = true; // Otherwise just mark that we have a good cookie
            result++;
        }
        // If not good cookies were created, then return -1
        // Otherwise, if there is still 1 element in the heap, add 1
        return greaterPresent ? result + heap.length : -1;
    }
    
    // Example run
    console.log(cookies(9, [2,7,3,6,4,6])); // 4

    【讨论】:

      猜你喜欢
      • 2021-05-10
      • 1970-01-01
      • 2011-11-20
      • 2019-09-08
      • 1970-01-01
      • 2017-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多