计划赶不上变化,本来想深入学习python的我,无奈要转到js开发,在js基本0基础的情况下,最近也狂补js知识了。
本着好记性不如烂笔头的信念,我决定总结一下js中数组的使用。
js中数组的声明可以有如下几种方式:
var arr = []; // 简写模式
var arr = new Array(); // new一个array对象
var arr = new Array(arrayLength); // new一个确定长度的array对象
要说明的是:
虽然第三种方法声明了数组的长度,但是实际上数组长度是可变的。也就是说,即使指定了长度为5,仍然可以将元素存储在规定长度之外,这时数组的长度也会随之改变。
此外,还需要明确的一点:
js是弱类型语言,也就是数组中的元素类型不需要一样。
举个数组中元素类型不一致的例子:
var arr = [1, 2, 3, 4, 'wangzhengyi', 'bululu'];
for (var i = 0; i < arr.length; i ++) {
console.log(arr[i]);
}
JavaScript数组的索引值也是从0开始的,我们可以直接通过数组名+下标的方式对数组元素进行访问。
示例代码如下:
var arr = [1, 2, 3];
console.log(arr[0]);
console.log(arr[1]);
此外,数组的遍历推荐使用连续for循环的模式,不推荐for-in,具体原因参考:Loop through array in JavaScript
遍历数组示例代码如下:
var arr = [1, 2, 3, 4, 'wangzhengyi', 'bululu'];
for (var i = 0, len = arr.length; i < len; i ++) {
console.log(arr[i]);
}
注意:
上述代码中,一个小优化在于提前获取数组的大小,这样不需要每次遍历都去查询数组大小。对于超大数组来说,能提高一定的效率。
有三种方法可以往一个数组中添加新的元素,分别是:push、unshift、splice。下面我分别来介绍一下这三种方法。
push方法,在数组末尾添加元素。示例代码如下:
var arr = [];
arr.push(1);
arr.push(2);
arr.push(3);
for (var i = 0, len = arr.length; i < len; i ++) {
console.log(arr[i]);
}
执行结果为:
1
2
3
unshift方法,是在数组头部添加元素。示例代码如下:
var arr = [];
arr.unshift(1);
arr.unshift(2);
arr.unshift(3);
for (var i = 0, len = arr.length; i < len; i ++) {
console.log(arr[i]);
}
执行结果如下:
3
2
1
splice方法是在数组的指定位置插入新元素,之前的元素则是自动顺序后移。注意splice的函数原型为:
array.splice(index, howMany, element...)
howMany表示要删除的元素个数,如果只是添加元素,此时howMany需要置为0。
示例代码如下:
var arr = [1, 2, 3, 4];
arr.splice(1, 0, 7, 8, 9);
for (var i = 0, len = arr.length; i < len; i ++) {
console.log(arr[i]);
}
执行结果如下:
1
7
8
9
2
3
4
与增加数组元素一样,删除数组中的元素也有三个方法,分别是:pop、shift和splice。接下来,分别讲解一下这三个函数的用法。
pop方法是移除数组中的最后一个元素。push和pop的组合可以将数组实现类似于栈(先入后出)的功能。示例代码如下:
var arr = [];
arr.push(1);
arr.push(2);
arr.push(3);
while (arr.length != 0) {
var ele = arr.pop();
console.log(ele);
}
shift方法是移除第一个元素,数组中的元素自动前移。(这种方法肯定对应着效率问题,时间复杂度是O(n))。
var arr = [];
arr.push(1);
arr.push(2);
arr.push(3);
function traverseArray(arr) {
for (var i = 0, len = arr.length; i < len; i ++) {
console.log(arr[i]);
}
}
while (arr.length != 0) {
var ele = arr.shift();
traverseArray(arr);
}
大家可以自己考虑运行结果。
在增加数组元素的时候,我们就讲过splice,这个函数原型中有一个howMany参数,代表从index开始删除之后的多少个元素。
示例代码如下:
var arr = [1, 2, 3, 4, 5, 6, 7];
function traverseArray(arr) {
for (var i = 0, len = arr.length; i < len; i ++) {
console.log(arr[i]);
}
}
arr.splice(1, 3);
traverseArray(arr);
执行结果为:
1
5
7
举个例子,代码如下:
var arr1 = [1, 2, 3, 4];
var arr2 = arr1;
这个时候,arr2只是保存arr1数组在堆内存的地址,并没有在堆内存重新申请内存搞一个数组出来。所以对arr2的修改会同时影响到arr1。因此,如果我们需要拷贝一份数组该怎么做呢?这就引出了需要学习的slice和concat函数。
这里的slice和python语法的slice是一样的,都是返回数组的切片。slice函数原型为:
array.slice(begin, end)
返回从begin到end的所有元素,注意包含begin,但是不包含end。
缺省begin,默认从0开始。缺省end,默认到数组末尾。
因此,拷贝数组我们可以通过如下代码实现:
var arr1 = [1, 2, 3, 4];
var arr2 = arr1.slice();
arr2[2] = 10000
function traverseArray(arr) {
for (var i = 0, len = arr.length; i < len; i ++) {
console.log(arr[i]);
}
}
traverseArray(arr1);
traverseArray(arr2);
执行结果如下:
1
2
3
4
1
2
10000
4
concat方法将创建一个新数组,然后将调用它的对象(this 指向的对象)中的元素以及所有参数中的数组类型的参数中的元素以及非数组类型的参数本身按照顺序放入这个新数组,并返回该数组.
示例代码如下:
var alpha = ["a", "b", "c"];
var number = [1, 2, 3]
// 新数组为["a", "b", "c", 1, 2, 3]
var complex = alpha.concat(number);
我的JS水平就是渣渣,所以我就用类似于JAVA和C的方式来写JavaScript的排序算法了。
而且这里我不讲算法原理,仅仅只是代码实现,可能会有Bug,欢迎大家博客评论指导。
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
实现代码如下:
function insertSort(arr) {
if (!arr) return;
var len = arr.length;
if (len == 0 || len == 1) return;
for (var i = 1, len = arr.length; i < len; i ++) {
var stand = arr[i];
for (var j = i - 1; j >= 0; j --) {
if (arr[j] > stand) {
arr[j + 1] = arr[j];
} else {
arr[j + 1] = stand;
break;
}
}
}
return arr;
}
时间复杂度为:O(n^2)
当然,该算法是有优化余地的,例如将搜索替换的位置算法改为二分查找。
经典的排序算法,提到冒泡排序我就心痛。本科时候的必须论文的冒泡排序算法的改进,结果写完论文之后都不能完整的实现冒泡排序算法,好尴尬。
if (!arr) return;
var len = arr.length;
if (len == 0 || len == 1) return;
for (var i = 0; i < len; i ++) {
for (var j = 0; j < len - i - 1; j ++) {
if (arr[j] > arr[j + 1]) {
var tmp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = tmp;
}
}
}
return arr;
}
时间复杂度为:O(n^2)
非常经典的排序算法,排序过程主要i分为三步:
实现代码如下:
function quickSort(arr, bt, ed) {
if (bt < ed) {
var pivot = findPartition(arr, bt, ed);
quickSort(arr, bt, pivot - 1);
quickSort(arr, pivot + 1, ed);
}
}
function findPartition(arr, bt, ed) {
var stand = arr[bt];
while (bt < ed) {
while (bt < ed && arr[ed] >= stand) {
ed --;
}
if (bt < ed) {
arr[bt ++] = arr[ed];
}
while (bt < ed && arr[bt] <= stand) {
bt ++;
}
if (bt < ed) {
arr[ed --] = arr[bt];
}
}
arr[bt] = stand;
return bt;
}
时间复杂度为:O(nlogn)。
也是非常经典的排序算法,我就是借着学习js的机会复习经典的排序算法了。归并排序的思想可以参考我的这篇博客:归并排序。我这里只写js实现。
function mergeSort(arr, bt, ed) {
if (bt < ed) {
var mid = bt + parseInt((ed - bt) / 2);
mergeSort(arr, bt, mid);
mergeSort(arr, mid + 1, ed);
mergeArray(arr, bt, mid, ed);
}
}
function mergeArray(arr, bt, mid, ed) {
var mArr = [];
var i = bt, j = mid + 1;
while (i <= mid && j <= ed) {
if (arr[i] <= arr[j]) {
mArr.push(arr[i++]);
} else {
mArr.push(arr[j ++]);
}
}
if (i <= mid) {
mArr = mArr.concat(arr.slice(i, mid + 1));
}
if (j <= ed) {
mArr = mArr.concat(arr.slice(j, ed + 1));
}
for (var h = 0; h < mArr.length; h ++) {
arr[bt + h] = mArr[h];
}
}
写归并排序的时候还有一个小插曲:就是js不能自动取整,后来用了parseInt方法,感觉萌萌大。