【问题标题】:Alternative and optimized solution for the given problem给定问题的替代和优化解决方案
【发布时间】:2020-09-10 01:33:10
【问题描述】:

这是我的面试问题,我可以解决,但是留下了面试官的便条,说这不是优化的代码,我无法在优化的解决方案中解决它,任何人都可以帮助。

问题:给定一个数组array1,按照格式重新排列数组

  1. 所有负数及其绝对数首先插入到数组中(已排序)。
  2. 所有剩余的项目都应连接到新数组(已排序)
//input 
var array = [8,7,0,6,4,-9,-7,2,-1,3,5,1,10];
//output
var array2 = [-7,7,-1,1,-9,0,2,3,4,5,6,8,10];

我的代码:

function sort(arr){  
  var arrange = arr.sort((a, b)=>{ return a-b});
  var array1=[];
  for(let i=0; i<arrange.length; i++){
    let firstItem = Math.abs(arrange[i]);
    for(let j=i+1; j<arrange.length; j++){
       if(firstItem === Math.abs(arrange[j])){
         array1.push(arrange[i], arrange[j])       
       }
   }
  }
  arrange = arrange.filter((item, i)=>{
     return array1.indexOf(item) === -1
  })
 return [...array1, ...arrange]
} 


console.log(sort(array));

JSBIN

也请给出一些提示,我的方法有什么问题。

注意:我写的代码是o(n^2),他想要一个O(log n),他的意思是,不用嵌套for循环,我们可以解决。我们可以吗?

【问题讨论】:

  • 将负数和正数放在单独的排序数组中,反转一个,进行合并以找到对,再次反转并附加其余的。

标签: javascript arrays optimization logic


【解决方案1】:

此解决方案未优化,因为如果我们忽略某些循环(如排序和过滤复杂性)会消耗处理时间。它有 O(n^2) 复杂度。这意味着如果元素数量增加,则需要更多时间。

在这类问题中,尽量忽略嵌套循环

【讨论】:

    【解决方案2】:

    您的算法存在多个问题

    1. 嵌套 for 循环:O(n2)
    for(let i=0; i<arrange.length; i++){ // ----------O(n)
      let firstItem = Math.abs(arrange[i]);
      for(let j=i+1; j<arrange.length; j++){ // ---------x O(n(n+1)/2)==O(n^2)
           if(firstItem === Math.abs(arrange[j])){
             array1.push(arrange[i], arrange[j])       
           }
       }
    }
    
    1. filter + indexOf 都是 O(n) 所以 O(n2) 也是
    arrange = arrange.filter((item, i)=>{ // O(n)
       return array1.indexOf(item) === -1 // -- x O(n*n) // because indexOf is O(n)
    })
    

    可能在 O(nlog(n)) 内解决它

    第一种可能性:

    • 获取底片,以及它们相关的(可能有几个?)正片对应的地图(neg => 正片)
    • 将阳性结果放在另一个袋子里
    • 对负图进行排序(按键)
    • 输出负图,如果有正则从正包中删除
    • 对阳性袋进行分类
    • 输出它

    const v = [8,7,0,6,4,-9,-7,2,-7,7,7,-1,3,5,1,10]
    function v1 (v) {
      const neg = new Map()
      const pos = new Map()
      v.forEach(el => {
        if (el >= 0) return
        if (neg.has(el)) {
          neg.get(el).push(el)
          return
        }
        neg.set(el, [el])
      })
      v.forEach(el => {
        if (el < 0) return
        if (neg.has(-el)) {
          neg.get(-el).push(el)
          return
        }
        if (pos.has(el)) {
          pos.get(el).push(el)
          return
        }
        pos.set(el, [el])
      })
      const sortedNegs = [...neg.entries()].sort((a, b) => (a[0]-b[0]))
      const output = []
      sortedNegs.forEach(([k, values]) => {
        output.push(values)
        pos.delete(-k)
      })
      const sortedPos = [...pos.entries()].sort((a, b) => (a[0]-b[0]))
    
      sortedPos.forEach(([k, values]) => output.push(values))
      console.log(output.flatMap(x => x))
    }
    v1(v)

    具有相同精神的另一种较短的方法

    • 按重复性对数字进行分组 num => [num,...]
    • 数字排序
    • 每组输出后,看是否存在正反部分
    • 如果是这样输出并删除它

    const v = [8,7,0,6,4,-9,-7,2,-7,7,7,-1,3,5,1,10]
    function v2 (v) {
    
      const groups = v.reduce((m, el) => {
        return m.has(el) ? (m.get(el).push(el), m) : m.set(el, [el])
      }, new Map())
      const sortedGroups = [...groups.entries()].sort((a,b) => a[0] - b[0])
      const output = []
      sortedGroups.forEach(([k, v]) => {
        if (k < 0) {
          output.push(v)
          if (groups.has(-k)) {
            output.push(groups.get(-k))
            groups.delete(-k)
          }
          return
        }
        groups.has(k) && output.push(v)
      })
      console.log(output.flatMap(x => x))
    }
    v2(v)

    最后你可以使用索引来固定更多,但让我们看看:

    const v = [8,7,0,6,4,-9,-7,2,-7,7,7,-1,3,5,1,10]
    function v1 (v) {
      const neg = new Map()
      const pos = new Map()
      v.forEach(el => {
        if (el >= 0) return
        if (neg.has(el)) {
          neg.get(el).push(el)
          return
        }
        neg.set(el, [el])
      })
      v.forEach(el => {
        if (el < 0) return
        if (neg.has(-el)) {
          neg.get(-el).push(el)
          return
        }
        if (pos.has(el)) {
          pos.get(el).push(el)
          return
        }
        pos.set(el, [el])
      })
      const sortedNegs = [...neg.entries()].sort((a, b) => (a[0]-b[0]))
      const output = []
      sortedNegs.forEach(([k, values]) => {
        output.push(values)
        pos.delete(-k)
      })
      const sortedPos = [...pos.entries()].sort((a, b) => (a[0]-b[0]))
    
      sortedPos.forEach(([k, values]) => output.push(values))
      return output.flatMap(x => x)
    }
    v1(v)
    function v2 (v) {
    
      const groups = v.reduce((m, el) => {
        return m.has(el) ? (m.get(el).push(el), m) : m.set(el, [el])
      }, new Map())
      const sortedGroups = [...groups.entries()].sort((a,b) => a[0] - b[0])
      const output = []
      sortedGroups.forEach(([k, v]) => {
        if (k < 0) {
          output.push(v)
          if (groups.has(-k)) {
            output.push(groups.get(-k))
            groups.delete(-k)
          }
          return
        }
        groups.has(k) && output.push(v)
      })
      return output.flatMap(x => x)
    }
    v2(v)
    
    function slow(arr){  
      var arrange = arr.sort((a, b)=>{ return a-b});
      var array1=[];
      for(let i=0; i<arrange.length; i++){
        let firstItem = Math.abs(arrange[i]);
        for(let j=i+1; j<arrange.length; j++){
           if(firstItem === Math.abs(arrange[j])){
             array1.push(arrange[i], arrange[j])       
           }
       }
      }
      arrange = arrange.filter((item, i)=>{
         return array1.indexOf(item) === -1
      })
     return [...array1, ...arrange]
    } 
    
    (() => {
      const big = v.join(',').repeat(100).split(',')
      ;[v1, v2, slow].forEach((meth, i) => {
        console.time('time'+i) //respectively 4ms, 3ms, 1s(!)
        meth(big)
        console.timeEnd('time'+i)
      })  
    })()
    我要强调的是,编写 for 循环和增加索引确实会提高性能,但在尝试进行此类优化之前,算法(及其数据结构)更重要。

    【讨论】:

      【解决方案3】:

      这里我有一个替代解决方案

      //input 
      var array = [8,7,0,6,4,-9,-7,2,-1,3,5,1,10];
      //output
      var array2 = [-7,7,-1,1,-9,0,2,3,4,5,6,8,10];
      
      
      console.clear();
      
      function sort(arr){
        const singular=[];
        const arrange = arr.sort((a, b)=>{ return a-b});
      
        const output = arrange.flatMap((item, i)=>{
           if(item < 0 && arrange.includes(-item)){
               return [item, -item];       
           }else{
             singular.push(item);
           }     
        }).filter((item)=>{ return item; });
      
        const result = [...new Set(output), ...singular];
      
        console.log('result : ', result);
      } 
      
      
      console.log(sort(array));
      
      

      【讨论】:

        【解决方案4】:

        您的解决方案有 2 种排序和一个嵌套的 for 循环,这对业务不利。

        排序具有复杂性:O(nlog(n)),而嵌套的 for 循环具有复杂性:O(n^2)

        当您在问题中看到数组时,首先,请考虑尝试以线性方式解决它们(即,我们可以在一次迭代中得到解决方案吗?)。你的问题需要排序,所以你必须使用排序,没有逃避它。根据经验,除非绝对必要,否则避免使用嵌套的 for 循环 - 能力越大,责任越大!

        查看此解决方案。我不会说它是最好的。但它是相当体面的。它使用一种排序并在数组中循环两次。

        function modifyArray(array) {
            var sorted = array.sort((a, b) => { return a - b });
            let map = {};
            for (var i = 0; i < sorted.length; i++) {
                map[sorted[i]] = true;
            }
        
            let negativesAndAbsolutes = [];
            let uniqueNegatives = [];
            let uniquePositives = [];
            let currNumber = null;
        
        
            for (var i = 0; i < sorted.length; i++) {
                currNumber = sorted[i];
                if (!map[currNumber]) {
                    // already sorted.
                    continue;
                }
                if (currNumber < 0) {
                    if (map[-1 * currNumber]) {
                        // its negative and it has an absolute.
                        negativesAndAbsolutes.push(currNumber, -1 * currNumber);
                        map[currNumber] = false;
                        map[-1 * currNumber] = false;       // don't process it again.
                        continue;
                    } else {
                        // unique negative.
                        uniqueNegatives.push(currNumber);
                    }
                    continue;
                }
                uniquePositives.push(currNumber);
            }
        
            return [...negativesAndAbsolutes, ...uniqueNegatives, ...uniquePositives];
        }
        
        modifyArray([8,7,0,6,4,-9,-7,2,-1,3,5,1,10]);
        

        【讨论】:

          猜你喜欢
          • 2021-09-06
          • 1970-01-01
          • 2021-02-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-05
          • 1970-01-01
          • 2017-08-27
          • 2010-10-04
          相关资源
          最近更新 更多