学习js数据结构与算法-数组(3)

89 阅读4分钟

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。

个人觉得要想进大厂,就必须学习了解算法和数据结构,经历了多次面试之后,也知道了算法和数据结构的重要性,所以,我也打算从今天开始,进入学习,下载了一下电子书,边看书,边跟着书的例子来敲一下代码,下面就开始总结一下对算法和数据结构的学习吧。

第四天:继续了解数组

目前为止,我们已经知道了怎么添加元素到数组的开头或末尾,以及怎样删除数组开头和结尾位置上的元素。那么如何在数组中的任意位置上删除或添加元素呢?

这个通用方法实现比较难:我们先了解一下splice这个方法的特性。splice一般传两个参数或两个以上的参数。但是传一个参数参数也是可以的。每个参数都有它的含义

param1: 表示索引的位置,从哪开始删除

param2:表示要删除的个数

param3-n:表示要添加的元素。

首先,我们看看如果传一个参数,会发生什么

// 删除从第一个数组之后的所有元素
arr.splice(1) // [2,3] 返回了被删除的元素
arr // [1]

再看看如果传两个参数,又会发生什么

const arr = [1,2,3,4,5,6,7,8] // 发现3个元素不太好演示,接下来的数组长度都是8。
// 从第一个元素之后开始删除,删除了3个元素
arr.splice(1,3) //  [2, 3, 4]返回了被删除的元素
arr // [1, 5, 6, 7, 8] 

继续看如果传三个或者更多参数,会发生什么

// 从第一个元素之后开始删除,删除了3个元素,并且在第一个元素后面添加了9和10这两个元素
arr.splice(1,3,9,10) // [2, 3, 4]返回了被删除的元素
arr // [1,9,10 5, 6, 7, 8] 

还有就是如果第一第二个输入负数的时候,会发生什么

// 首先看看如果第一个参数是负数,可以看到元素从第6(数组的长度+第一个参数)之后删除两个,是因为第六个元素之后只剩7和8两个元素给他删除了,所以只删除了7和8,是因为如果删除的数量大于剩余要删除的数量,则会将删除的个数置为数组的长度减去第一个参数
arr.splice(-2,3,9,10) //  [7, 8]
arr // [1, 2, 3, 4, 5, 6, 9, 10]
// 如果第一个参数的绝对值大于数组的长度,那么它会置为零,相当于从第一个开始删除
arr.splice(-9,3,9,10) // [1, 2, 3]

// 如果第二个参数是负数,他会让相当于零
arr.splice(-2,-2,9,10) //  []
arr // [1, 2, 3, 4, 5, 6, 9, 10, 7, 8]

下面,根据上面的例子,实现一下splice这个方法

参考神三元同学的splice实现。在他实现的基础上做了一下兼容,如果一个参数都没传,v8原版的是会返回一个空对象,他实现的那个会直接报错,没有做这个兼容。还有就是去掉了第二部分的优化,那部分我实在是没看懂。

const sliceDeleteElements = (array, startIndex, deleteCount, deleteArr) => {
  for (let i = 0; i < deleteCount; i++) {
    let index = startIndex + i;
    if (index in array) {
      let current = array[index];
      deleteArr[i] = current;
    }
  }
};

const movePostElements = (array, startIndex, len, deleteCount, addElements) => {
  // 如果添加的元素和删除的元素个数相等,相当于元素的替换,数组长度不变,被删除元素后面的元素不需要挪动
  if (deleteCount === addElements.length) return;
  // 如果添加的元素和删除的元素个数不相等,则移动后面的元素
  else if (deleteCount > addElements.length) {
    // 删除的元素比新增的元素多,那么后面的元素整体向前挪动
    // 一共需要挪动 len - startIndex - deleteCount 个元素
    for (let i = startIndex + deleteCount; i < len; i++) {
      let fromIndex = i;
      // 将要挪动到的目标位置
      let toIndex = i - (deleteCount - addElements.length);
      if (fromIndex in array) {
        array[toIndex] = array[fromIndex];
      } else {
        delete array[toIndex];
      }
    }
    // 注意注意!这里我们把后面的元素向前挪,相当于数组长度减小了,需要删除冗余元素
    // 目前长度为 len + addElements - deleteCount
    for (let i = len - 1; i >= len + addElements.length - deleteCount; i--) {
      delete array[i];
    }
  } else if (deleteCount < addElements.length) {
    // 删除的元素比新增的元素少,那么后面的元素整体向后挪动
    // 思考一下: 这里为什么要从后往前遍历?从前往后会产生什么问题?
    for (let i = len - 1; i >= startIndex + deleteCount; i--) {
      let fromIndex = i;
      // 将要挪动到的目标位置
      let toIndex = i + (addElements.length - deleteCount);
      if (fromIndex in array) {
        array[toIndex] = array[fromIndex];
      } else {
        delete array[toIndex];
      }
    }
  }
};

const computeStartIndex = (start_i, len) => {
  // 处理索引负数的情况
  if (startIndex < 0) {
    return startIndex + len > 0 ? startIndex + len : 0;
  }
  return startIndex >= len ? len : startIndex;
};

const computeDeleteCount = (startIndex, len, deleteCount, argumentsLen) => {
  // 删除数目没有传,默认删除startIndex及后面所有的
  if (argumentsLen === 1) return len - startIndex;
  // 删除数目过小
  if (deleteCount < 0) return 0;
  // 删除数目过大
  if (deleteCount > len - deleteCount) return len - startIndex;
  return deleteCount;
};

function toLen(value) {
  if(value === undefined) return 0
  return value
}

Array.prototype.splice = function (startIndex, deleteCount, ...addElements) {
  let argumentsLen = arguments.length;
  let array = Object(this);
  let len = toLen(array.length);
  let deleteArr = new Array(toLen(deleteCount));

  startIndex = computeStartIndex(startIndex, len);
  deleteCount = toLen(computeDeleteCount(startIndex, len, deleteCount, argumentsLen));

  // 拷贝删除的元素
  sliceDeleteElements(array, startIndex, deleteCount, deleteArr);
  // 移动删除元素后面的元素
  movePostElements(array, startIndex, len, deleteCount, addElements);

  // 插入新元素
  for (let i = 0; i < addElements.length; i++) {
    array[startIndex + i] = addElements[i];
  }

  array.length = len - deleteCount + addElements.length;

  return deleteArr;
};

[1,2,3].splice()