时间复杂度:3Sum算法在立方时间内?

时间:2016-02-24 22:50:42

标签: javascript algorithm time-complexity binary-search

如何利用二进制搜索来提高算法的时间复杂度?

我正在审查一些采访的时间复杂度。我在使算法更有时间效率方面遇到了麻烦。这是我对3-Sum问题的强力解决方案:有多少三元组总和为0?背景:我没有CS学位。

//BRUTE FORCE SOLUTION: N^3
var threeSum = function(list){
  var count = 0;

  //checking each triple
  for(var i = 0; i < list.length; i++){
    for(var j = i+1; j < list.length; j++){
      for(var k = j+1; k < list.length; k++){

        if(list[i] + list[j] + list[k] === 0){count++;}
      }
    }
  }
  return count;
};

//binary search code
var binarySearch = function(target, array){
  var lo  = 0;
  var hi  = array.length - 1;
  //base case
  while(lo <= hi){
    var mid = Math.floor( lo + (hi - lo) / 2 );
    if(target === array[mid]) return mid;
    if(target < array[mid]){
      hi = mid - 1;
    }
    if(target > array[mid]){
      lo = mid + 1;
    }
  }
  // value not found
  return -1;
}

我正在从普林斯顿大学在线阅读算法课程。教授指出,使用二进制搜索算法可以提高算法效率。

根据教授的说法,我们会:

  • 对列表进行排序
  • 对于每对数字array [i]&amp; array [j]二进制搜索 - (array [i] + array [j])

但是,我无法理解二进制搜索是如何解决问题的。这是演讲的幻灯片,我仍然试图理解,但也许对其他人有用:

enter image description here

我确信那里有几种有效的解决方案:随意加入您的实施,因为它可以帮助我和其他未来的读者。感谢

4 个答案:

答案 0 :(得分:4)

  

但是,我无法理解二进制搜索如何解决问题。

这就是n ^ 2 log(n)算法的工作原理:

  1. 按O(nlogn)时间
  2. 对列表进行排序
  3. 查找所有数字对(i,j),即O(n ^ 2)运行时。
  4. 然后,对于每对(i,j),它找到k的数字k = sum - j - i。这是恒定时间O(1)
  5. 算法会检查每个k是否存在,因为元组(i,j,k)将总和为sum。为此,请执行二进制搜索,这需要log(n)时间。
  6. 最终运行时间为O(nlogn)+ O(logn * n ^ 2)= O(n ^ 2logn)

    另一种(更快)解决方案是用散列表替换排序部分。然后,查找值k将花费O(1)时间而不是logn

答案 1 :(得分:1)

二分搜索方法试图解决的问题是将三次算法(这是你的强力算法)的复杂性降低到~N ^ 2 log N算法。

正如其他评论者指出的那样,我们知道以下陈述:list[i] + list[j] + list[k] == 0true,而不是我们找到的3SUM结果。这跟-(list[i] + list[j]) == list[k]说的一样。因此,算法的目标是检查每个i索引和j索引对是否存在满足前一个等式的对应k索引。二进制搜索可以在~log N时间内找到那些k索引。因此,整体增长顺序为~N ^ 2 log N(外部for循环对应于N ^ 2部分)。

至于javascript中的实现,我会这样做:

var threesum = function(list) {
  list.sort(function(a,b){return a - b});
  console.log(list);
  var cnt = 0;
  for(var i=0; i<list.length; i++) {
    for(var j=i+1; j<list.length; j++) {
      var k = bsearch(-(list[i]+list[j]), list);
      if (k!= null && k > j) {
        console.log("[i=%d], [j=%d], [k=%d]", i,j,k);
        cnt++;
      }
    }
  }
  return cnt;
};

var bsearch = function(key, a) {
  var lo = 0;
  var hi = a.length-1;
  while (lo <= hi) {
    var mid = lo + Math.floor((hi - lo) / 2);
    if (a[mid] < key) {
      lo = mid + 1;
    } else if (a[mid] > key) {
      hi = mid - 1;
    } else {
      return mid;
    }
  }
  return null;
};

threesum([41, 48, 31, 32, 34, 38, 1, -9, 12, 13, 99, 5, -65, 8, 3, -3])

答案 2 :(得分:0)

该算法基本上按以下方式工作:

  • 对数组进行排序(最坏情况var output = [Bool]() output.reserveCapacity(input.characters.count) for char in input.characters { output.append(char == "1") } ,具体取决于排序算法)
  • 生成所有数字对 - 取O(n ^ 2)
  • 对于每对O(n ^ 2),可能存在(i , j),这样0 = i + j + k k k . - (i + j){{1 O(log n)is simply i&lt; k&lt;避免j`不保留以排除重复。

因此,总时间复杂度为, thus we can easily look it up by binary search in

答案 3 :(得分:0)

const threeSum =(array,target)=>{
  
  let result =[]
  
   array = array.sort((a,b)=> a-b)
  
  for(let i=0; i < array.length-2; i++){
    
    let left = i+1;
    let right = array.length -1;
    
    while(left < right){
      
      let sum = array[i]+ array[left]+ array[right];
      
      if(sum === target){
        result.push([array[i],array[left], array[right]]);
        
        left++;
        right--
      }else if(sum < target){
        
        //sum is lower than target so increment left pointer 
        left++;
      }else if(sum > target){
        //sum is greater than target so increment right pointer 
        right--;
      }
    }
    
    
  }
  //return the list 
  return result;
}

let a = [12, 3, 1, 2, -6, 5, -8, 6];
console.log(threeSum(a, 0));

  Time Complexity: O(n^2) 
  Space Complexity: O(1)