【问题标题】:Optimizing node.js solution for HackerRank QHEAP1针对 HackerRank QHEAP1 优化 node.js 解决方案
【发布时间】:2021-08-06 03:56:00
【问题描述】:

您好,我正在尝试更好地熟悉 Heaps,因此想尝试使用原语实现 HackerRanks>Practice>Data Structures>Heaps>QHEAP1 的解决方案,但是其中两个测试出现超时错误。

快速总结:我需要能够解析标准化输入并处理以下 3 种类型的查询:

  1. 向堆中添加一个元素。
  2. 从堆中删除特定元素。
  3. 打印堆中所有元素的最小值。

我想知道这可以在哪里优化?据我所知,我的del() 将在 O(n) 中执行,因为我需要搜索提供的元素。

// search for and delete specific element {x} from heap
function del(arr, x){
  let i = 0;
  let found = false;
  let n = arr.length;
  while(!found && i < n){
      if(arr[i] == x) found = true;
      i++;
  }
  if(found){
      arr[i-1] = arr[n-1];  // take the last element and overwrite to delete
      arr.length = n - 1;  // shorten array
      downHeap(arr, i);  // perform downHeap opertaion from index deleted
  }
}
// NOTE: customized for minHeap due to requirement to print minimum value
function downHeap(arr, t){
  // use array as binary tree - next index looking down is double current index
  // NOTE: i and t are 1 indexed for heap lookahead
  let i = 2 * t;  
  if(i >= arr.length) return;  // no more room
  // checkes if right child is smallest - if so updates index to right child
  if(i < arr.length - 1 && arr[i - 1] > arr[i]) i = i + 1;
  
  // if lower element is smaller than current element, swap em
  if(arr[i-1] < arr[t-1]){
      swap(arr, i-1, t-1);
      downHeap(arr,i);  // downHeap again at the next level
  } 
}

// insert x into heap 
function insert(arr, x){
  const n = arr.length;
  arr.length = n + 1;  // increasing array size
  arr[n] = x;  // adding el to end of array
  upHeap(arr, arr.length)
}

//NOTE: customized as minHeap due to requirement to print minimum value.
function upHeap(arr, t){
  // using array as binary tree - looking up - parant is half of current index
  const i = Math.floor(t/2);
  // if we've hit zero gone too far - NOTE: i, and t are 1 indexed for heap reference
  // also nothing to do if parent is smaller than current index
  if(i == 0 || arr[i-1] <= arr[t-1]) return;
  
  // child is smaller than parent swap and upHeap from parent
  swap(arr, t-1, i-1)
  upHeap(arr, i)
}

// swahp 
function swap(arr, l, r){
  const t = arr[l];
  arr[l] = arr[r];
  arr[r] = t;
}

PS。作为一个附带问题,我有点在为堆操作索引的1 和用于数组操作的0 索引之间切换(例如,您会注意到 up 和 downHeap 方法中有很多 i-1 语句) - 想知道是否有更聪明的方法来做到这一点?

支持代码:

function processData(input) {
    //Enter your code here
    const inputs = input.split('\n');
    const n = inputs[0];
    let arr = [];
    for(let i = 1; i <= n; i++){
        const query = inputs[i].split(' ');
        const op = query[0];
        if(op == "1"){
            insert(arr, parseInt(query[1]))
        } else if(op == "2"){
            del(arr, parseInt(query[1]))
        } else if(op == "3"){
            console.log(arr[0])
        } else {
            console.log("Error reading op");
        }
    }
} 

process.stdin.resume();
process.stdin.setEncoding("ascii");
_input = "";
process.stdin.on("data", function (input) {
    _input += input;
});

process.stdin.on("end", function () {
   processData(_input);
});

示例输入

22
1 286789035
1 255653921
1 274310529
1 494521015
3
2 255653921
2 286789035
3
1 236295092
1 254828111
2 254828111
1 465995753
1 85886315
1 7959587
1 20842598
2 7959587
3
1 -51159108
3
2 -51159108
3
1 789534713

【问题讨论】:

    标签: javascript arrays algorithm heap


    【解决方案1】:

    该代码确实令人困惑,因为(在您编写时)它有时使用基于 1 的索引,而其他时候它使用它们作为基于 0 的索引。

    例如,在insert 中,以下行表明您打算将ti 设为基于1 的索引,因为您将它们即时转换为基于0 的索引:

    if(arr[i-1] < arr[t-1])
    

    ...但是在这一行中,您将 i 视为基于 0 的索引(arr.length 将是 i 的可接受值,如果它是基于 1 的):

    if(i >= arr.length) return;  // no more room
    

    同样的混淆发生在这里:

    if(i < arr.length - 1 && arr[i - 1] > arr[i]) i = i + 1;
    

    结果你会得到错误的结果。

    当 JavaScript 期望在所有使用索引的地方都使用基于 0 的索引时,使用基于 1 的索引会令人困惑。我没有勇气在那种状态下进一步调试你的代码。我建议在整个代码中使用基于 0 的索引,这意味着索引 t 处的值的左孩子位于索引 t*2+1

    其他一些评论:

    • 要查找值出现在堆中的索引,您不必编写显式循环。只需使用内置的indexOf 方法即可。
    • 递归很好,但downHeapupHeap 函数在使用迭代方法时会更有效,因为这样——而不是交换值——您可以获取值的副本以向上或向下冒泡,然后只有 move (不交换)冲突的值,最终将复制的值插入到正确的位置。与重复交换相比,这将执行更少的分配。
    • 要插入一个值,您可以只使用push 方法而不是“手动”更新length
    • 您可以使用移位运算符代替 Math.floor 进行整数除以 2。

    所以这里是你的代码的更正:

    function del(arr, x) {
        const i = arr.indexOf(x); // This will be faster
        if (i >= 0) {
            const value = arr.pop();
            if (i < arr.length) { // Only assign back when it was not last
                arr[i] = value;
                downHeap(arr, i);
            }
        }
    }
    
    function downHeap(arr, t) {
        const val = arr[t];
        while (true) {
            let i = t * 2 + 1;
            if (i < arr.length - 1 && arr[i] > arr[i + 1]) i = i + 1;
            if (i >= arr.length || arr[i] >= val) break;
            arr[t] = arr[i]; // Don't swap to gain time
            // No recursion to save stack space
            t = i;
        }
        arr[t] = val;
    }
    
    function insert(arr, x) {
        arr.push(x);  // adding element to end of array
        upHeap(arr, arr.length - 1);
    }
    
    function upHeap(arr, t) {
        const val = arr[t];
        while (true) {
            let i = (t - 1) >> 1; // Shift operator may give some speed increase
            if (i < 0 || arr[i] <= val) break;
            arr[t] = arr[i]; // Don't swap to gain time
            // No recursion to save stack space
            t = i;
        }
        arr[t] = val;
    }
    

    【讨论】:

    • 感谢您的评价! cmets 非常感谢!能够通过所有测试。
    猜你喜欢
    • 2019-08-17
    • 2020-11-15
    • 1970-01-01
    • 2021-09-06
    • 1970-01-01
    • 1970-01-01
    • 2017-07-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多