如何从数组中递归删除连续的重复元素? (javascript)

时间:2018-07-06 20:59:06

标签: javascript arrays recursion

我试图创建一个旨在递归消除数组中任何连续重复元素的函数。它与全局变量一起使用,但是,我发现解决此问题的能力较弱。我的代码基于此code(从字符串中删除所有连续的重复项;使用的语言:C ++)。我知道字符串和数组之间存在可变性差异,但是我不完全了解后台堆栈发生了什么。函数运行后,全局变量正确,但是函数本身的输出却不正确。任何解释或指示将不胜感激。谢谢!

这不是作业问题,我只是想将递归锤入我的头骨,因为它仍然使我陷入困境。双关语很抱歉。

//var testArr = [1, 1, 2, 2, 3, 3, 1, 1, 1]
//compress(testArr); //[1,2,3,1] //<= expected result
//current output [1, 2, 2, 3, 3, 1, 1, 1]
var arr = [];
var compress = function(list) {
     //var arr = [];
    if (list.length === 0) {
        return arr;
    } 
    if (list.length === 1) {
        arr.push(list[0]);
        return list
    }
    if (list.length > 1 && list[0] !== list[1]) {
        arr.push(list[0])
        compress(list.slice(1,));
    }
    if (list.length > 1 && list[0] === list[1]) {
        list.splice(0,1);
        compress(list);
    }
    return list;
}

6 个答案:

答案 0 :(得分:2)

因为list.slice(1,)复制了数组,所以在递归调用中list不是原始数组。进行更改不会更改原始数组。您必须更改要返回的列表(将结果传递到调用堆栈中):

list = [list[0], ...compress(list.slice(1,))];

也许更短:

const compress = arr => arr.length > 1
  ? arr[0] === arr[1]
      ? compress(arr.slice(1))
      :  [arr[0], ...compress(arr.slice(1))]
  : arr;

答案 1 :(得分:2)

基本上,您需要返回arr而不是list

然后,您需要检查一下实际元素的不等价,然后下一个元素进行推送。

继续使用切片数组,最后返回arr

var arr = [];
var compress = function(list) {
        if (list.length === 0) {
            return arr;
        } 
        if (list.length === 1) {
            arr.push(list[0]);
            return arr;
        }
        if (list[0] !== list[1]) {
            arr.push(list[0])
        }
        return compress(list.slice(1));
    };

console.log(compress([1, 1, 2, 2, 3, 3, 1, 1, 1]));

另一种方法是直接在函数中使用arr,这里重命名为result

此代码经过了优化,因为在有更多项目的情况下,它以对递归函数的调用结束。

var compress = function(list, result = []) {
        if (list.length === 0) {
            return result;
        } 
        if (list.length === 1) {
            result.push(list[0]);
            return result;
        }
        if (list[0] !== list[1]) {
            result.push(list[0])
        }
        return compress(list.slice(1), result);
    };

console.log(compress([1, 1, 2, 2, 3, 3, 1, 1, 1]));

一种更短的方法,无需使用其他数组作为结果。

var compress = function(list) {
        return list.length
            ? [].concat(list[0] === list[1] ? [] : list[0], compress(list.slice(1)))
            : [];
    };

console.log(compress([1, 1, 2, 2, 3, 3, 1, 1, 1]));

答案 2 :(得分:2)

您可以通过在列表的尾部进行递归来进行经典的递归(类似于Haskell),而无需使用全局变量:

var compress = function(list) {
    if (list.length === 0) return [];
 
    let [head, ...rest] = list
    let l = compress(rest)
    return (l[0] === head) 
            ? l
            : [head, ...l]
}

var testArr = [1, 1, 1, 1, 2, 2, 3, 3, 1, 1, 1, 2, 2]
console.log(compress(testArr))

答案 3 :(得分:2)

使用ECMAScript 6功能:

const testArr = [1, 1, 2, 2, 3, 3, 1, 1, 1];
const compress = ([head, ...rest]) => {
  if (!head) return [];
  const tail = compress(rest);
  return head === tail[0] ? tail : [head, ...tail];
}
console.log(compress(testArr));


作为旁注,我想指出功能方法要短一些(是的,我知道问题在于递归方法):

const testArr = [1, 1, 2, 2, 3, 3, 1, 1, 1];
const output = testArr.reduce((list, next) => list.slice(-1)[0] === next ? list : [...list, next], []);
console.log(output);

答案 4 :(得分:1)

首先,您需要确定基本情况,即数组的长度小于2的情况。递归部分需要根据没有连续重复项的条件来决定返回数组的外观。

function compress(list) {
  if (list.length <= 1) {
    return list  //base case
  } else if (list[0] === list[1]) {
    return compress(list.slice(1, ))  //discard head of list
  } else {
    return [list.shift()].concat(compress(list))  //keep head
  }
}

console.log(compress([1,2,2,3,3,2,3,3,3,3])) //[1,2,3,2,3]
console.log(compress([2])) //[2]
console.log(compress([])) //[]

答案 5 :(得分:0)

尚不清楚您是否打算修改原始输入。使用slice会产生一个副本以传递到下一个函数调用,但是调用splice确实会修改原始输入,与链接到的C ++代码一样。听起来好像您想摆脱全局变量,所以让我们开始吧。

这里有两个版本。使用JavaScript进行动态参数分配很方便,这意味着我们可以只用一个参数进行第一个函数调用,但可以设置一个默认的第二个参数,然后在递归过程中进行引用。

此版本非常简单。它通过向后遍历并删除多余的元素来修改原始输入,这也可以简单地通过循环来完成。这个版本使用O(n)时间,没有多余的空间。

// Our parameters are the original list and an
// index, i, pointing to our current array cell.
// We default to starting at the end of the list,
// can you think why? (Hint: think what would happen
// after our call to 'splice' if we went forward)
var compress = function(list, i = list.length - 1) {
  // Base case, we've reached the beginning
  // of the list so we're done, return the 
  // modified list
  if (i == 0)
    return list;

  // The current element is the same as
  // the next one we're going to look at
  // (that's the one at i - 1) so remove it!
  if (list[i] == list[i - 1])
    list.splice(i, 1);
    
  // Return the result of continuing
  // each element's examination
  return compress(list, i - 1);
}

console.log(compress([1, 1, 2, 2, 3, 3, 1, 1, 1]));

这是一个不会修改原始列表的版本,并且(与此处的其他答案类似)通过在每个递归调用上创建列表尾部的副本,从而导致O(n^2)空间使用情况(以及时间) :

var compress = function(list) {
  // The list is not long enough
  // to have extra elements, return it.
  if (list.length < 2)
    return list

  // The first two elements are different
  // so we definitely want to keep the
  // first one. Let's place it in an array
  // that we will 'concat' with the result
  // of compressing the list tail (the rest of the list).
  if (list[0] != list[1])
    return [list[0]].concat(compress(list.slice(1)));
  
  // If we got here, the first two elements
  // are similar so we definitely just want
  // the result of compressing the rest of the list.
  return compress(list.slice(1));
}

console.log(compress([1, 1, 2, 2, 3, 3, 1, 1, 1]));