数据结构和算法(一)之数组结构
一.数组的基本使用
创建和初始化数组
-
用JavaScript声明、创建和初始化数组很简单,如下:
//创建和初始化数组 let daysOfWeek = new Array(); let daysOfWeek = new Array(7); let daysOfWeek = new Array('Sunday','Moday','Tuesday','Wednesday','Thursday','Friday','Saturday');
-
代码解析:
- 使用
new
关键字,就能简单地声明并初始化一个数组 - 用这种方式,还可以创建一个指定长度的数组
- 另外,也可以直接将数组元素作为参数传递给它的构造器
- 用
new
创建数组并不是最好的方式。如果你想在JavaScript中创建一个数组,只用中括号([]
)的形式就行了
- 使用
-
使用中括号(
[]
)创建数组:let daysOfWeek = new Array('Sunday','Moday','Tuesday','Wednesday','Thursday','Friday','Saturday');
数组长度和遍历数组
-
如果我们希望获得数组的长度,有一个length属性
//获取数组的长度 alert(daysOfWeek.length)
-
也可以通过下标值来遍历数组:
//普通for方法遍历数组 for(let i = 0;i<daysOfWeek.length;i++){ alert(daysOfWeek[i]) } //通过foreach遍历数组 daysOfWeek.forEach(function (value){ alert(value) })
-
我们来做个练习:
-
求菲波那切数列的前20个数字,并且放在数组中。
-
菲波那切数列数列第一个数字是1,第二个数字也是1,第三项是前两项的和
//求菲波那切数列的前28个数字 let fibonac = []; fibonac[0] = 1; fibonac[1] = 1; for(let i = 2;i < 20;i++){ fibonac[i] = fibonac[i-1] + fibonac[i - 2]; } alert(fibonac);
-
二.数组的常见操作
数组中常见的操作有:添加元素、删除元素、修改元素、获取元素
添加元素
-
JavaScript中,进行上述操作都比较简单,因为语言本身都已经封装好了这些特性。
-
假如我们有一个数组:numbers,初始化0~9
// 初始化一个数组 let numbers = [0,1,2,3,4,5,6,7,8,9];
-
添加一个元素到数组的最后位置:
// 添加一个元素到数组的最后位置 // 方式一: numbers[numbers.length] = 10; // 方式二: numbers.push(11); numbers.push(12,13) alert(numbers);
-
在数组首位插入一个元素:
// 在数组首位插入一个元素 for(let i = numbers.length;i > 0;i--){ numbers[i] = numbers[i-1] } numbers[0] = -1 alert(numbers) // -1,0,1,2,3,4,5,6,7,8,9,10,11,12,13
-
上面代码实现的原理是怎么样的呢?
-
考虑上面代码实现的性能怎样呢?
- 性能并不算非常高
- 这也是数组和链表相对比的一个劣势:在中间位置插入元素的效率比链表低
-
当然,我们在数组首位插入数据可以直接使用unshift方法
// 通过unshift在首位插入数据 numbers.unshift(-2); numbers.unshift(-4,-3); alert(numbers) // -4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13
删除元素
-
如果希望删除数组最后的元素,可以使用pop()方法
// 删除最后的元素 numbers.pop() alert(numbers) //-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12
-
如果我们希望移除的首位元素:
// 删除首位的元素 for(let i = 0;i < numbers.length;i++){ numbers[i] = numbers[i+1] } numbers.pop(); alert(numbers); //-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13
-
当然,我们可以直接使用shift方式来实现:
numbers.shift() alert(numbers)
任意位置
-
任意位置?
- 前面我们学习的主要是在数组的开头和结尾处添加和删除数据
- 那如果我们希望在数组的中间位置进行一些操作应该怎么办呢?
-
一方面,我们可以自己封装这样的函数,但JS已经给我们提供了一个splice方法
-
通过splice删除数据
// 删除指定位置的几个元素 numbers.splice(5,3); alert(numbers); // -4,-3,-2,-1,0,4,5,6,7,8,9,10,11,12,13
-
代码解析:
- 上面的代码会删除索引为5,6,7位置的元素。
- 第一个参数表示 索引起始的位置为5(其实是第6个元素,因为索引从0开始的),删除3个元素。
-
如果我们希望使用splice来插入数据呢?
// 插入指定位置元素 numbers.splice(5,3,3,2,1); alert(numbers); //-4,-3,-2,-1,0,3,2,1,4,5,6,7,8,9,10,11,12,13
-
代码解析:
- 上面的代码会从索引5的位置开始修改数据,修改多少个呢?第二个参数来决定的。
- 第一个参数依然是索引的位置为5(第六个位置)
- 第二个参数是要将数组中多少个元素给替换掉,我们这里是3个(也可以使用3个元素来替换2个,可以自己尝试一下)
- 后面跟着的就是要替换的元素。
三.数组的其他操作
JavaScript中添加了很多方便操作数据的方法
常见方法
-
我们先对常见的方法简单来看一下
方法名 方法描述 concat
连接2个或更多数组,并返回结果 every
对数组中的每一项运行给定函数,如果该行苏对每一项都返回 ture
,则返回ture
,否则返回false
filter
对数组中的每一项运行给定函数,返回该函数会返回 true
的项组成的数组forEach
对数组中的每一项运行给定函数。这个方法没有返回值 join
将所有的数组元素连接成一个字符串 indexOf
返回第一个与给定参数相等的数组元素的索引,没有找到则返回-1 lastIndexOf
返回在数组中搜索到的与给定参数相等的元素的索引里最大的值 map
对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组 reverse
颠倒数组中元素的顺序,原先第一个元素现在变成最后一个,同样原先的最后一个元素变成了现在的第一个 slice
传入索引值,将数组里对应索引范围内的元素作为新元素返回 some
对数组中的每一项运行给定函数,如果任一项返回 true
,则结果为true
,并且迭代结束sort
对照字母表顺序组排序,支持传入指定排序方法的函数作为参数 toString
将数组作为字符串返回 valueOf
和 toString
类似,将数组作为字符串返回
数组合并
-
数组的合并非常简单,使用
concat
即可(也可以直接+进行合并)// 数组的合并 let nums1 = [1,2,3] let nums2 = [100,200,300] let newNums = nums1.concat(nums2) alert(newNums) // 1,2,3,100,200,300 newNums = nums1 + nums2 alert(newNums) // 1,2,3,100,200,300
迭代方法
-
为了方便操作数组,JS提供了很多迭代器方法:
-
every()
方法 -
some()
方法// 定义数组 let names = ["abc","cb","mba","dna"] // 判断数组中是否含有a字符的字符 let flag = names.some(function(t){ alert(t); return t.indexOf("a") != -1; }) alert(flag)
-
forEach()
方法 -
filter()
方法 -
map()
方法
reduce
方法
-
我们单独拿出
reduce
方法,因为这个方法相对来说难理解一点 -
首先,我们来看这个方法需要的参数:
arr.reduce(callback[,initialValue])
-
参数
callback
(一个在数组中每一项上调用回调的函数,接受四个函数:)previousValue
(上一次调用回调函数时的返回值,或者初始值)currentValue
(当前正在处理的数组元素)currentIndex
(当前正在处理的数组元素下标)array
(调用reduce()
方法的数组)
initialValue
(可选的初始值。作为第一次调用回调函数时传给previousValue
的值)
-
有些晦涩难懂,我们直接看例子
-
求一个数字的累加和
-
使用
for
实现:// 定义数组 let numbers = [1,2,3,4] // for实现累加 let total = 0 for(let i = 0;i<numbers.length;i++){ total += numbers[i] } alert(total) // 10
-
使用
forEach
简化for
循环-
相对于
for
循环,forEach
更符合我们的思维(遍历数组中的元素)// 使用forEach let total = 0 numbers.forEach(function(t){ total += t }) alert(total)
-
-
使用
reduce
方法实现// 使用reduce方法 let total = numbers.reduce(function(pre,cur){ return pre + cur }) alert(total)
代码解析:
- pre中每次传入的参数是不固定的,而是上次执行函数时的结果保存在了pre中
- 第一次执行时,pre为0,cur为1
- 第二次执行时,pre为1(0+1,上次函数执行的结果),cur为2
- 第三次执行时,pre为3(1+2,上次函数执行的结果),cur为3
- 第四次执行时,pre为6(3+3,上次函数执行的结果),cur为4
- 当cur为4时,数组中的元素遍历完了,就直接将第四次的结果,作为
reduce
函数的返回值进行返回。
-
似乎和
forEach
比较没有太大的优势呢?- 通过这个代码你会发现,你不需要在调用函数前先定义一个变量,只需要一个变量来接受方法最终即可。
- 但是这就是优势吗?不是,优势在于
reduce
方法有返回值,而forEach
没有。 - 这算什么优势吗?如果
reduce
方法有返回值,那么reduce
方法本身就可以作为参数直接传递给另外一个需要reduce
返回值得作为参数的函数。而forEach
中你只能先将每次函数的结果保存在一个变量,最后再将变量传入到参数中。 - 没错,这就是最近非常流行的函数式编程。也是为了几乎每个可以使用函数式编程的语言都有
reduce
这个方法的原因。 - 关于函数式编程,不再本次课程的讨论之中,只是看到了这个函数,给大家延伸了一下而已。
-
initialValue
还需要讲吗?- 其实就是第一次执行
reduce
中的函数时,pre的值。 - 因为默认pre第一次执行时为0.
- 其实就是第一次执行
-