深入数组拓展应用一

深入数组拓展应用一

内容大纲:

  • 数组高频方法

  • 实战演练

  • 数组查找方法

数组高频方法

首先来回顾一下数组有关的高频方法,这些方法都是原生 JavaScript 为我们提供了的,熟练的掌握这些方法可以让我们做开发时达到事半功倍的效果。

slice( )splice( )

slice( ) 方法用于截取数组。该方法可以接收一个或者两个参数,代表返回项的起始和结束位置。

  • 1 个参数:那就代表起始位置,返回从指定的起始位置到数组末尾的所有项目

  • 2 个参数:那就代表从指定的起始位置到指定的末尾位置之间的项,但是不包括结束位置的项目

注:slice( ) 方法不会影响原始数组

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 一个参数
var i = arr.slice(3);
console.log(i); // [ 4, 5, 6, 7, 8, 9, 10 ]
console.log(arr); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
// 两个参数
var j = arr.slice(2, 6);
console.log(j); // [ 3, 4, 5, 6 ]
console.log(arr); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

如果传入的是负数,则用数组长度加上该数来确定相应的位置。

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 一个参数
var i = arr.slice(-3);// 等价于slice(7)
console.log(i); // [ 8, 9, 10 ]

// 两个参数
var j = arr.slice(-6, -2); // 等价于slice(4,8)
console.log(j); // [ 5, 6, 7, 8 ]
// 不满足条件返回空数组
var k = arr.slice(-2, -6); // 等价于slice(8,4)
console.log(k); // []

splice( ) 这个方法非常的厉害,可以实现对数组的 3 种类型的操作:删除插入替换,相当于是增删改操作都可以用这个方法来实现。

删除:可以删除任意数量的元素,只需要指定 2 个参数:要参数的第一项位置和要删除的项数

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 从下标为3的元素开始删除,删除5个元素
// 将删除的元素返回给i
var i = arr.splice(3, 5);
console.log(i); // [ 4, 5, 6, 7, 8 ]
console.log(arr); // [ 1, 2, 3, 9, 10 ]

插入:可以向任意位置插入任意数量的元素。只需要提供 3 个参数:起始位置,0(要删除的项目),要插入的项目。

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 从下标为3的元素之前开始插入
var i = arr.splice(3, 0, "red", "blue");
console.log(i); // []
console.log(arr);
// [ 1, 2, 3, 'red', 'blue', 4, 5, 6, 7, 8, 9, 10 ]

替换:替换的原理在于插入的时候同时进行删除,这样就实现了替换功能。

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 从下标为3的元素之前开始插入
// 插入多少,刚好就删除多少
var i = arr.splice(3, 2, "red", "blue");
console.log(i); // [ 4, 5 ]
console.log(arr);
// [ 1, 2, 3, 'red', 'blue', 6, 7, 8, 9, 10 ]

数组和字符串相互转换

join( ) 是将数组转为字符串,可以传入分隔符作为参数。

var arr = [1, 2, 3];
var str = arr.join("");
console.log(str); // 123
var str2 = arr.join(",");
console.log(str2); // 1,2,3

split( ) 是将字符串转为数组,传入参数指明以什么作为分隔符。

var str = "Hello";
var arr = str.split("");
console.log(arr); // [ 'H', 'e', 'l', 'l', 'o' ]
var arr2 = str.split("l");
console.log(arr2); // [ 'He', '', 'o' ]

数组重排序方法

重排序涉及到两个方法:reverse( )sort( )

reverse( ) 用于反转数组项的顺序,注意使用该方法时会改变原来数组的顺序,而不是返回一个副本。

var arr = [1, 2, 3, 4, 5];
console.log(arr.reverse()); // [ 5, 4, 3, 2, 1 ]
console.log(arr); // [ 5, 4, 3, 2, 1 ]

sort( ) 是按照升序排列数组每一项。

var arr = [0, 12, 3, 7, -12, 23];
console.log(arr.sort());
// [ -12, 0, 12, 23, 3, 7 ]

可以看到,我们调用 sort( ) 方法以后排序并没有按照正确的升序来进行排序,原因是因为默认排序方式是根据字符编码的顺序进行排序。

如果想要按照数字的大小来进行排序,那么需要传入 sortby 参数。

arr.sort(sortby)

sortby 参数是一个可选参数,用来规定如何进行排序,该参数必须是一个函数。该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 ab,其返回值如下:

  • a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
  • a 等于 b,则返回 0
  • a 大于 b,则返回一个大于 0 的值。
var arr = [0, 12, 3, 7, -12, 23];
console.log(arr.sort(function (a, b) {
    if (a < b) {
        return -1;
    } else if (a > b) {
        return 1;
    } else {
        return 0;
    }
}));

在上面的代码中,我们根据 ab 之间的大小比较来返回 1-1 或者 0,然后实际上只要是正值和负值即可。

在后面学习了 ES6 中的箭头函数后,上面的代码可以直接简化为:

var arr = [0, 12, 3, 7, -12, 23];
console.log(arr.sort((a, b) => a - b));

最后需要注意的是,reverse( )sort( ) 方法,返回值是经过排序之后的数组。

位置方法

JavaScript 还为数组提供了位置方法:indexOf( )lastIndexOf( )

这两个方法都接收两个参数:要查找的项目和查找的起点位置索引。区别在于一个是从数组开头开始找,一个是从数组末尾开始找。如果没找到就返回 -1

var arr = ["H", "e", "l", "l", "o"];
console.log(arr.indexOf("l")); // 2
console.log(arr.lastIndexOf("l")); // 3
console.log(arr.indexOf("z")); // -1

还需要注意的是,这两个方法进行查找时使用的是全等进行比较。

var arr = ["1", "2", "3"];
console.log(arr.indexOf(1)); // -1

自定义查找

JavaScript 中提供了 2 个和上面位置方法很相似的:find( )findIndex( )

该两个方法都接受一个回调函数,第一个是返回数组中符合回调函数要求的第一个值,第二个是返回数组中符合回调函数的第一个值的下标。

var arr = [3, 10, 23, 20];

console.log(arr.find(function(item){
    return item >= 18;
})); // 23

console.log(arr.findIndex(function(item){
    return item >= 18;
})); // 2

迭代方法

这些方法都需要用户传入一个回调函数,之后这些方法会去迭代数组的每一项,并且将每一项应用到回调函数中。

  • every( )
  • some( )
  • filter( )
  • forEach( )
  • map( )

every( ) 是对数组的每一项运行给定的函数,如果该函数每一项都返回 true,则返回 true

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 将数组的每一项传入到回调函数,如果每一项返回 true,那么最终返回 true
var i = arr.every(function (item) {
    if (item % 2 == 0) {
        return true;
    } else {
        return false;
    }
});
console.log(i); // false

every( ) 比较相似的是 some( ),该方法可以对数组的每一项运行指定的函数,如果该函数只要有一项返回 true 则返回 true

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 将数组的每一项传入到回调函数,如果有一项返回 true,那么最终返回 true
var i = arr.some(function (item) {
    if (item % 2 == 0) {
        return true;
    } else {
        return false;
    }
});
console.log(i); // true

filter( ) 中的 filter 是过滤的意思,这个方法会返回一个数组,数组里面返回过滤过的元素。

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 将数组的每一项传入到回调函数,然后将返回为 true 的项目组成一个数组
var i = arr.filter(function (item) {
    if (item % 2 == 0) {
        return true;
    } else {
        return false;
    }
});
console.log(i); // [ 2, 4, 6, 8, 10 ]

forEach( ) 方法在前面介绍数组遍历的时候,就已经见到过了。该方法就是简单的将数组每一项传入到函数,然后执行该函数里面的代码。需要注意一下的是,该回调函数没有返回值。

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 将数组的每一项传入到回调函数,然后执行回调函数里面的操作
var i = arr.forEach(function (item) {
    console.log(item);
});
console.log(i); // undefined

map( ) 方法是对数组的每一项运行回调函数。最终返回一个数组,这个数组是每次调用函数后的运行结果。

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 将数组的每一项传入到回调函数,然后将返回的结果组成一个新的数组返回
var i = arr.map(function (item) {
    if (item % 2 == 0) {
        return true;
    } else {
        return false;
    }
});
console.log(i);
// [ false, true, false, true, false, true, false, true, false, true ]

实战演练

上面我们已经介绍了很多数组的方法,但是并不是说上面的那些方法就是 JavaScript 数组中所有的方法,仅仅是高频方法而已。

如果想了解 JavaScript 中数组所有的方法,可以参阅 MDNhttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array

下面我们在掌握了上面的方法的基础上,来做一些实战演练。

  1. 字符串反转,例如 “abcd123” 变为 “321dcba”
var str = "abcd123";
console.log(str.split("").reverse().join(""));
  1. 查询子串首次出现的位置,例如字符串 “abccbaxzabcasdfaxzffgvb” 中查找子串 “axz” 的结果为 5
var str = "abccbaxzabcasdfaxzffgvbasdaxzqwe";
console.log(str.indexOf("axz"));

升级难度:查找所有的 “axz”,将它们的下标保存到一个数组里面

var str = "abccbaxzabcasdfaxzffgvbasdaxzqwe";
var substr = "axz";

/**
 * @param {string} haystack
 * @param {string} needle
 * @return {array}
 */
var strStr = function (haystack, needle) {
    // 当查找空串时
    if (needle === "") return [];
    // 查找到的索引数组
    let result = [];
    // 遍历字符串
    for (let i = 0; i < haystack.length;) {
        // 当查找 haystack 中有一个字符相同于 needle 中第一个字符且截取 needle 字符串长度后跟 needle 字符串比较 
        if (haystack[i] === needle[0] && haystack.substr(i, needle.length) === needle) {
            result.push(i);
            // 移动 i 下标到查找的位置最后一个字符位置
            i = i + needle.length;
        } else {
            i++
        }
    }
    // 没有查找到返回 []
    return result;
};
console.log(strStr (str, substr)); // [ 5, 15, 26 ]
  1. 判断一个数组是否为对称(回文)数组,对称数组的格式为 [ a, b, c, b, a ]、[ a, b, c, c, b, a ]
var arr1 = ['a', 'b', 'c', 'b', 'a'];
var arr2 = ['a', 'b', 'c', 'c', 'b', 'a'];
var arr3 = ['a', 'b', 'c', 'd', 'c', 'f'];

function isPalindrome(arr) {
    var str1 = arr.join("");
    var str2 = arr.reverse().join("");
    return str1 === str2;
}

console.log(isPalindrome(arr1)); // true
console.log(isPalindrome(arr2)); // true
console.log(isPalindrome(arr3)); // false

数组查找方法

查找方法在原生的 JavaScript 中已经提供了。

  • indexOf( )
  • lastIndexOf( )
  • find( )
  • findIndex( )

但是数组的查找一直是面试中比较爱考察的一个点。数组的查找其实并不是仅仅局限于 JavaScript 这门语言,任何有数组这种数据结构的语言,都会涉及到查找的问题。

常见的查找方法有 2 种:

  • 顺序查找
  • 二分查找

顺序查找

顺序查找通常从数组或列表的第一个元素开始,逐一访问所有项目直至找到满足条件的值并返回索引位置。

如果查找到数组或列表的结尾仍未找到满足条件的值则返回 -1

function sequentialSearch(arr, target) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] === target) {
            return i;
        }
    }
    return -1;
}

二分查找

二分查找又被称之为折半查找。二分查找的基本思路如下:

有序数组中,取中间数值作为比较对象,若给定值与中间数值相等,则查找成功。

注意:一定要谨记,二分查找是应用在有序的数组中。

若给定值小于中间数值,则在中间数值的左半区继续查找。若给定值大于中间数值,则在中间数值的右半区继续查找。

不断重复上述过程,直到查找成功,或所查找的区域无记录,查找失败。

使用二分算法,每轮循环必然将查找区域缩减一半。

算法使用 (left + right) / 2 来计算中间索引值,最初的 left0rightarray.length - 1。记住整数除法会舍掉小数部分,因而 5 / 2 的结果是 2

如果中间索引取到的元素值正是待查找值,则查找结束并返回中间索引。如果待查找值比中间索引的元素值小,right 会被设为 middle - 1,然后在左侧区域重复查找过程。如果待查找值比中间索引的元素值大,left 会被设为 middle + 1,然后在右侧区域重复查找过程。当区域左边界大于右边界时,查找也结束,返回表示未找到的 -1

下面是二分查找和顺序查找在效率上的对比:

binarySearch

可以看出 binary search 在查找数字 37 时只需 3 次,而 sequential search 查找 37 时需要 11 次。

下面是二分查找的 JavaScript 实现版本。

非递归版本:

function binarySearch(arr, target) {
    // 初始索引开始位置和结束位置
    var left = 0;
    var right = arr.length - 1;
    while (left <= right) {
        var mid = parseInt((left + right) / 2); // 找到中间下标
        if (target < arr[mid]) {
            right = mid - 1; // 修改有边界
        } else if (target > arr[mid]) {
            left = mid + 1; // 修改左边界
        } else {
            return mid; // 要找的数就是这个数,返回其下标
        }
    }
    // 如果在循环内没有找到查找的 target(left <= right) 的情况则返回 -1
    return -1;
}


var arr = [1, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59];
console.log(binarySearch(arr, 29)); // 9

递归版本:

function binarySearch(arr, target, left = 0, right = arr.length - 1) {
    if (left > right) {
        return -1;
    }
    var mid = parseInt((left + right) / 2);
    if(target < arr[mid]){
        return binarySearch(arr, target, left, mid - 1);
    } else if(target > arr[mid]){
        return binarySearch(arr, target, left + 1, right);
    } else {
        return mid;
    }
}

var arr = [1, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59];
console.log(binarySearch(arr, 53)); // 15

-EOF-

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值