「这是我参与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()