在任意维数组中搜索元素,并在搜索到的元素位置后插入一个指定元素
例如有一个4维数组:
arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
现要求写一个方法,查找这个数组中的元素8,并在第一次找到的8后面插入一个指定的值99;
调用该方法后,期望arr4的值会变成:
[1, 2, [3, 4, [5, 6, [7, 8, 99, [9, 10]]]]]
这个问题可以分为两个部分:
- 查找一个任意维数数组中的某个元素。
- 在任意维数数组中指定的索引中插入指定值。
第二个问题是比较直观的,只需要定义好索引的表示方法和利用迭代的数组访问方式就能解决。
我们定义一个n维数组A的某个元素的索引为一个数组I。这个I的元素索引为A的维度,从高到低排列。
而I的元素为A中对应维度的索引值。
例如,设有A = [1,2,3,[4,5,[6,7,8],6,7,10]],那么A中元素8的索引为I = [3,2,2]。
因此我们可以这么解决第二个问题:
//arr:目标数组,index:插入值所在索引,t:插入的值
function insertArray(arr, index, t) {
let counter = 0;
for (let idx of index) {
if (counter === index.length-1) {
arr.splice(idx, 0, t);
} else {
arr = arr[idx];
}
counter++;
}
}
let A = [1, 2, 3, [4, 5, [6, 7, 8], 6, 7, 10]];
let I = [3, 2, 2];
insertArray(A, I, 8);
console.log(A); //[1, 2, 3, [4, 5, [6, 7, 8, 8], 6, 7, 10]]
回到第一个问题,我们需要一个方法,输入一个任意维数的数组和一个元素的值,返回一个表示该元素在数组中的索引的数组。
由于这是一个对任意深度的数组的搜索,我们可以用递归的思维解决。记录每次进入更低维度时的索引值。
function searchArray(arr, target) {
let isDone = false;
let index = [];
function search(arr, target) {
// index = index || [];
for (let i = 0; i < arr.length; i++) {
if (isDone) return;
if (arr[i] !== target) {
if (arr[i] instanceof Array) { //如果可以进入下一维度,则进去搜索
index.push(i);
search(arr[i], target);
}
else if (i === arr.length - 1) { //在当前维度找不到目标时,退回上一维度
index.pop();
}
}
else {
index.push(i);
isDone = true;
}
}
}
search(arr,target);
return index;
}
let A = [1, 2, 3, [4, 5, [6, 7, 8], 6, 7, 10]];
let index = searchArray(A, 8);
console.log(index); // [3, 2, 2];
这么写个人觉得非常丑,为了把两个标志变量包含进函数里,我在递归函数外面又套了一层函数。我相信一定有方法可以把返回信息从递归内层传回外层的。不管怎么样我们算是把问题解决了。
我们也可以把两个问题一次性解决掉。因为在我们搜索元素的时候,可以顺便把要插入的值插到数组中,但是我们要注意在数组插入值以后,就要停止搜索返回,或者跳过插入值继续搜索后面的元素。不然如果插入的值跟搜索目标一样时,插入操作将会没完没了。
function searchArray(arr, target, value) {
for (let i=0; i < arr.length; i++){
if (arr[i] !== target) {
if (Array.isArray(arr[i])) {
searchArray(arr[i], target, value);
}
}
else {
arr.splice(i+1, 0, value);
return; //或 i++; 这样写只能限定每个维度只插入一次,如果不同维度中都出现目标,则会在每个维度的目标后都会插入value。因为这个return只能回到上一层递归,而不能返回所有递归调用。
}
}
}
let arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
searchArray(arr4, 8, 8)
console.log(arr4); //输出:[1, 2, [3, 4, [5, 6, [7, 8, 8, [9, 10]]]]]
为了确保只插入一次,我只能又用这种丑陋的写法:
function searchArray(arr, target, value) {
let isDone = false;
function search(arr, target, value) {
for (let i = 0; i < arr.length; i++) {
if (isDone) return;
if (arr[i] !== target) {
if (Array.isArray(arr[i])) {
search(arr[i], target, value);
}
}
else {
arr.splice(i + 1, 0, value);
isDone = true;
}
}
}
search(arr,target, value);
}
let arr4 = [1, 2, 3, [4, 5, [6, 7, 8], 6, 7, 8, 10]];
searchArray(arr4, 8, 8)
console.log(arr4); //输出:[1, 2, 3, [4, 5, [6, 7, 8, 8], 6, 7, 8, 10]]