本文仅为刷题整理以供自己后续复习,难易程度为本人自身感受,与官方无关。有错误之处请指出!
文章目录
- 一、二维数组的查找
- 二、替换空格
- 三、从头到尾打印链表
- 四、重建二叉树
- 五、用两个栈实现队列
- 六、旋转数组中的最小数字
- 七、斐波那契数列
- 八、做一个动态规划的总结
- 九、矩形覆盖
- 十、二进制中1的个数
- 十一、数值的整数次方
- 十二、调整数组顺序使奇数位于偶数前面
- 十三、链表中倒数第k个节点
- 十四、反转链表
- 十五、合并两个排序的链表
- 十六、树的子结构
- 十七、二叉树的镜像
- 十八、顺时针打印矩阵
- 十九、包含min函数的栈
- 二十、栈的压入、弹出序列
- 二十一、从上到下打印二叉树
- 二十二、二叉搜索树的后序遍历序列
- 二十三、二叉树中和为某一值的路径
- 二十四、复杂链表的复制
- 二十五、二叉搜索树与双向链表
- 二十六、字符串的排列
- 二十七、数组中出现次数超过一半的数
- 二十八、最小的k个数
- 二十九、连续子数组和的最大值
- 三十、1~n整数中1出现的次数
- 三十一、把数组排成最小的数
- 三十二、丑数
- 三十三、第一个只出现一次的字符
- 三十四、数组中的逆序对
- 三十五、两个链表中的第一个公共节点
- 三十六、数字在排序数组中出现的次数
- 三十七、二叉树的深度
- 三十八、平衡二叉树
- 三十九、数组中只出现一次的数字
- 四十、和为S的连续正数序列
- 四十一、和为S的字符串
- 四十二、左旋转字符串
- 四十三、单次翻转序列
- 四十四、扑克牌顺序
- 自己的一些容易错的点
一、二维数组的查找
简单
题目描述
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序,请完成一个函数,输入这样的一个二维数组和整数,判断数组中是否有该整数。
分析
既然是有序的,如果使用暴力就太浪费时间了。如果是以为数组,那么可以采用二分法,从中间开始,小的话就往右,大的话就往左。
代码
function Find(target,array){
if(matrix.length==0) return false//防止输入为空数组,空数组不存在array[0].length,会报错。
const n = array.length, // 数组的行数,注意二维数组.length表示行数
m = array[0].length; //数组的列数
let row = n-1,
col = 0;
while(row >=0 && col <= m - 1){
if(array[row][col] > target){
row--;
}else if(array[row][col] < target){
col++;
}else return true;
}
return false;
}
二、替换空格
简单
题目描述
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为
We Are Happy
.则经过替换之后的字符串为We%20Are%20Happy
。
我的思路:
循环然后替换。
更简单的直接使用正则
代码
function replaceSpace(str){
return str.replace(/\s/g,'%20');
}
三、从头到尾打印链表
简单
题目描述
输入一个链表,从尾到头打印链表每个节点的值
分析:
可以使用栈和递归
代码
/* function ListNode(x){
this.val = x;
this.next = null;
} */
function printListFromTailToHead(head){
const res = [];
let pNode = head;
while(pNode !== null){
res.unshift(pNode.val);
pNode = pNode.next;
}
return res;
}
四、重建二叉树
简单,主要是理解清除几种遍历的顺序
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字,例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
复习
- 前序遍历:根 左 右
- 中序遍历:左 根 右
- 后序遍历:左 右 根
分析
关于二叉树,一般使用递归
- 确定根,确定左子树,确定右子树
- 在左子树中递归
- 在右子树中递归
- 打印当前根
function reConstructBinaryTree(pre,vin){
if(pre.length === 0 || vin.length === 0){
return null;
}
//前序第一个是根节点,也是中序左右子树的分割点
const index = vin.indexOf(pre[0]), //因为前序的第一个为根节点
left = vin.slice(0,index), //表示索引从0到index-1的元素
right = vin.slice(index + 1); //表示从index+1这个到结束
return{
val:pre[0],
//递归左右子树的前序、中序
left:reConstructBinaryTree(pre.slice(1,index+1),left),
right:reConstructBinaryTree(pre.slice(index+1),right)
};
}
五、用两个栈实现队列
简单,但是刚开始有点不知道怎么下手
题目
用两个栈来实现一个队列 ,完成队列的push和pop操作,队列中的元素为int类型
知识点回顾
栈是先进后出,队列是先进先出
分析
用两个栈的话,可以一个用来push,一个用来pop
push是尾插,pop是尾删
const outStack = [],
inStack = [];
function push(node){
inStack.push(node);
}
function pop(){
//只要用来出栈的那个队列是空的,就先把进栈的数组的数据加到出栈的数组
if(!outStack.length){
while(inStack.length){
outStack.push(inStack.pop());
}
}
return outStack.pop();
}
六、旋转数组中的最小数字
简单
题目
把一个数组最开始的若干元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
分析
因为原数组是非递减的,所以原数组中的每个元素一定是小于或等于后一个元素。显然元素组中最小的元素在第一个。这样的话,我只用在旋转数组中找到第一个出现前一个比后一个大的数字,后一个就一定是最小的。O(n)
function minNumberInRotateArray(rotateArray){
if(rotateArray.length === 0) return fasle;
for(let i = 0;i < rotateArray.length;i++){
if(rotateArray[i]>rotateArray[i+1]) return rotateArray[i+1];
}
return rotateArray[0];
}
第二种方法,在第一种方法的基础上,如果采用二分法,时间效率更高,O(logn)
但是别忘了考虑数组长度为0的情况、是一个数字全部是一样的 这两种情况
function minNumberInRotateArray(rotateArray){
if(rotateArray.length===0) return false;
let left = 0,
right = rotateArray.length - 1;
while(right-left>1){
let mid = left + (right-left>>1); //右移运算符,这相当于除2取整
if(rotateArray[left] ==rotateArray[right] && rotateArray[left]===rotateArray[mid]) return rotateArray[left];
if(rotateArray[mid] > rotateArray[right]){
left = mid;
}else{
right = mid;
}
}
return Math.min(rotateArray[left],rotateArray[right]);
}
七、斐波那契数列
递归简单 动态规划不太懂
题目
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
分析
可以使用递归,但是动态规划更好
动态规划的优点
最优子结构
无后效性
子问题重叠
代码
递归
let count = 0;
function Fibonacci(n){
count++
if(n == 1 || n == 2)
return 1
return Fibonacci(n-1)+Fibonacci(n-2)
}
function Fibonacci(n){
let f = 0,
g = 1;
while(n--){
g += f;
f = g - f;
}
return f;
}
八、做一个动态规划的总结
学习源于博主整理:告别动态规划,连刷40道动规算法题,我总结了动规的套路(ps:感谢大佬,我终于搞懂动态规划类的题目了)
1.定义
动态规划,无非就是利用历史记录,来避免我们的重复计算。而这些历史记录,我们需要一些变量来保存,一般是用一维数组或者二维数组来保存。
2.步骤
- 定义数组元素的含义
因为上面的定义说了,我们会用一个数组,来保存历史数组,假设用一维数组dp[]
。这个时候有一个非常非常重要的点,就是规定你这个数组元素的含义,例如你的dp[i]
是代表什么意思 - 找出数组元素之间的关系式
动态规划有点儿类似于我们高中所学习的归纳法。当我们需要计算dp[i]
时,是可以利用dp[n-1]
,dp[n-2]
…dp[1]
,,来推出dp[n]
的,也就是可以利用历史数据来退出新的元素值,所以我们要找出数组元素之间的关系式。
例如dp[n]=dp[n-1]+dp[n-2]
,这个就是他们的关系式。而这一步,也是最难的异步。
学过动态规划的可能经常听到最优子结构,把大的问题拆分成小的问题。
- 找出初始值。比如
dp[n]=dp[n-1]+dp[n-2]
,我们可以通过dp[n-1]
和dp[n-2]
来计算dp[n]
.但是我们需要知道初始值。要是一直推算下去,会由dp[3]=dp[2]+dp[1]
,而dp[2]
和dp[1]
是不能再分解的了,所以我们必须要能够直接获得dp[2]
和dp[1]
的值。这就是所谓的初始值。
有了初始值 以后,并且有了数组元素之间的关系式,那么我们就可以得到dp[n]
的值了。
3.案例一
简单的一维DP
问题描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个N级台阶总共由多少种跳法。
分析
(1)定义数组元素的含义
我们的问题是求青蛙跳上N级台阶总共有多少种跳法,那我们就定义
dp[i]
的含义为:跳上一个i级的台阶总共有dp[i]
种跳法。,这样,如果我们能够算出dp[n]
,那就是我们想要求的答案
(2)找出数组元素间的关系式
我们的目的是要求
dp[n]
,动态规划的题,就是把一个规模比较大的问题分成几个规模比较小的问题,然后由小问题推导出大的问题。也就是说,dp[n]
的规模是n,比它规模小的是n-1,n-2,n-3…也就是说,dp[n]
一定会和dp[n-1],dp[n-2]...
存在某种关系的。我们要找出他们的关系。
那么应该怎么找他们的关系呢
因为青蛙可以选择只跳一级,也可以选择跳两级,所以青蛙到达第N级的台阶由两种方式:
(1)一种是从第n-1级跳上来
(2)一种是从第n-2级跳上来
由于我们是要算所有可能的跳法的,所以有dp[n]=dp[n-1]+dp[n-2]
(3)找出初始条件
当N=1时,
dp[1]=dp[0]+dp[-1],
,而我们数组是不允许下标为负数的,所以对于dp[1]
,我们必须要直接给出它的数值,相当于初始值,显然,dp[1]=1
。一样,dp[0]=0
.(因为0个台阶,那肯定是0种跳法了)。于是得出初始值:dp[0]=0
function(n){
if(n<=1) return n;
let dp = [];
dp.length = n+1;
//给出初始值
dp[0]=0;
dp[1]=1;
//通过关系式来计算出dp[n]
for(let i = 2;i<=n;i++){
dp[i] = dp[i-1]+dp[i-2];
}
//返回最终结果
return dp[n];
}
不知道为什么,使用dp[i]在力扣上总是部分用例不能通过
var numWays = function(n) {
const MOD=1000000007
let a=1,b=1,sum=1;
for(var i=2;i<=n;i++){
a=b;
b=sum;
sum=(a+b)%MOD
}
return sum
};
(4)再说初始化
但上面的代码并不够严谨。错在对初始值的寻找不够严谨。当
n=2
的时候,显然dp[2] = dp[1] + dp[0] = 1
错了。
也就是说,在找初始值的时候不要找漏了,dp[2]
也算一个初始值。不能通过公式计算得出。
4.案例二
二维数组的DP
问题描述
一个机器人位于m X n网格的左上角(起始点在下图中标记为Start).机器人每次只能向下或者向右移动移动一步。机器人视图达到网格的右下角(在下图中标记为“Finish"),问总共右多少条不同的路径?
分析
(1)定义数组元素的含义
由于我们的目的是从左上角到右下角一共有多少种路径,我们就定义dp[i][j]
的含义为:当机器人从左上角走到(i,j)这个位置时,一共有dp[i][j]
种路径。那么,dp[m-1][n-1]
就是我们要的答案了。
注意,这个网格相当于一个二维数组,数组是从下标为0开始算起的,所以右下角的位置是(m-1,n-),所以
dp[m-1][n-1]
就是我们要找的答案
(2)找出数组元素间的关系式
可以想象一下,机器人要怎么样才能达到(i,j)这个位置?由于机器人可以向下走或者向右走,所以有两种方式到达。
一种是从(i-1,j)这个位置走一步到位
一种是从(i,j-1)这个位置走一步到达
因为是计算所有可能的步骤,所以是把所有可能走的路径都加起来,所以关系式是dp[i][j]=dp[i-1][j]+dp[i][j-1]
.
(3)找出初始值
显然,当dp[i][j]
中,如果i或者j有一个为0,那么还能使用关系式吗?答是不能的,因为这个时候把i-1或者j-1,就变成复述了,数组就会出现问题。所以我们初始值是计算出所有的dp[0][0...n-1]
和多有的dp[0....m-1][0]
。这个是非常容易计算的,相当于计算机图中的最上面一行和左边一列,因此初始值如下:
dp[0][0...n-1]=1;//相当于最上面的一行,机器人只能一直往左走
dp[0...m-1][0]=1;//相当于最左面一列,机器人只能一直往下走
代码
function fn(m,n){
if(m<=0||n<=0) return 0;
let dp = [][];
for(var i = 0;i<m;i++){
dp[i][0]=1;
}
for(var i = 0;i<n;i++){
dp[0][i]=1;
}
//推导出dp[m-1][n-1]
for(var i = 1;i<m;i++){
for(var j = 1;j<n;j++){
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
5.案例三
问题描述
给定一个包含非负整数的mXn网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明
每次只能向下或者向右移动一步
相当于是计算最右路径
步骤
(1)定义数组元素的含义
定义
dp[i] [j]
的含义为:当机器人从左上角走到(i, j) 这个位置时,最下的路径和是dp[i] [j]
。那么dp[m-1][n-1]
就是我们要的答案。
(2)找出关系数组元素间的关系式
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+arr[i,j]
(3)找出初始值
显然,当
dp[i][j]
中,如果i或者j有一个为0,那么还能使用关系式吗?答案是不可以。因为这个时候把i-1或者j-1,就变成了复述了,数组就会出现问题。所以我们的初始值是计算出所有的dp[0][0...n-1]
和所有的dp[0...m-1][0]
.这个还是非常容易计算的
代码
function fn(arr){
let m = arr.length;
let n = arr[0].length;
if(m<=0||n<=0) return 0;
let dp = [];
dp[0][0]=arr[0][0];
for(var i = 1;i<m;i++){
dp[i][0]=dp[i-1][0]+arr[i][0];
}
for(var i = 1;i<n;i++){
dp[0][i]=dp[0][i-1]+arr[0][i];
}
for(var i = 1;i<m;i++){
for(var j=1;j<n;j++){
dp[i][j]=Math.min(dp[i-1][j],dp[i][j-])+arr[i][j];
}
}
return dp[m-1][n-1];
}
九、矩形覆盖
十、二进制中1的个数
题目
输入一个整数,输出该数二进制表示中1的个数,其中负数用补码表示
复习
机器数和真值
机器数:一个数在计算机中的二进制表示形式,叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号,正数为0,负数为1.比如,十进制中的数+3,计算机字长为8位,转换成二进制就是00000011.如果是-3,就是10000011.
这里00000011和10000011就叫做机器数
真值:因为第一位是符号位,所以及其数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位1代表负,其真正数值是 -3 而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
原码,反码,补码
原码
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:
[+1]原 = 0000 0001
[-1]原 = 1000 0001
反码
正数的反码是本身,负数的反码是在原码的基础上,符号位不变,其余各个位取反
补码
正数的补码就是本身
负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1.也就是在反码的基础上加1
分析
如果一个数与1与运算结果为1,可以说明该数转换为二进制的话,最右边的那一位是1。所以回到这题上,我只用将这个数转为二进制,然后右移,(或者左移1)与1进行与运算,使用count来计数。
代码
方法一
function NumberOf1(n){
let count = 0;
let flag = 1;
while(flag){
if(n&1) count++;
flag=flag<<1;
}
return count;
方法二
function newNumberOf1(n){
let count=0;
while(n){
n=n&n-1;
count++;
}
return count;
}
十一、数值的整数次方
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
复习
先了解一下快速幂
不要一看到幂就循环去乘
另外,记住,二进制数每右移一次相当于除以2
判断奇数偶数可以用这个数与1进行与运算
代码
function Power(base,exponent){
let res = 1,
n;
if(exponent>0){
//指数大于0的情况
n = exponent;
}else if(exponent<0){
//指数小于0的情况
if(!base) throw new Error('分母不能为0');
n = -exponent;
}else{
//指数等于0的情况
return 1;
}
while(n){
//也可以使用递归,这里采用了循环
if(n&1)//为奇数的时候,二进制数最后一个肯定是1
//当指数为奇数的时候,包括了1
res *= base;
base *= base;
n>>=1;
}
return exponent>0?res:1/res;
十二、调整数组顺序使奇数位于偶数前面
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位数的后半部分。并且保证奇数和偶数之间的相对位置不变。
题目分析
判断是否为奇数,统计奇数的个数,然后新建数组,把所有奇数存进去数组前面,剩下的存进去数组后面
代码
function reOrderArray(array){
let oddBegin = 0,
oddCount = 0;
const newArray = [];
for(let i =0;i<array.length;i++){
if(array[i]&1){
oddCount++;
}
}
for(let i = 0;i<array.length;i++){
if(array[i]&1){
newArray[pddBegin++] = array[i];
}else{
newArray[oddCount++]=array[i];
}
}
return newArray;
}
十三、链表中倒数第k个节点
简单
题目描述
输入一个链表,输出该链表中倒数第K个节点
题目分析
用两个指针来跑,两个指针中间相距k-1个节点,第一个指针先跑,跑到了第K个节点时,第二个指针则是第一个节点。这个时候两个一起跑。当第一个跑到了最后一个节点时,这时候第一个指针则是倒数第k个节点
简单的来说,就是弄两个指针,都指向头,第一个指针先跑k个。然后再两个指针一起跑,知道前面那个真正跑到了尾节点,那么另一个就一定是倒数第K个
代码
function FindKthToTail(head,k){
if(head === null || k<=0) return null;
let pNode1 = head,
pNode2 = head;
while(--k){
if(pNode2.next!==null){
pNode2=pNode2.next;
}else{
return null;
}
}
while(pNode2.next!==null){
pNode1 = pNode1.next;
pNode2 = pNode2.next;
}
return pNode1;
}
十四、反转链表
题目描述
输入一个链表,反转链表以后,输出链表的所有元素
题目分析
至少需要三个
代码
function ReverseList(pHead){
let pPre = null,
pNext = null;
while(pHead!==null){
pNext = pHead.next;
pHead.next = pPre;
pPre = pHead;
pHead = pNext;
}
retrun pPre;
}
十五、合并两个排序的链表
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表。合成的链表必须满足单调不递减的规则。
分析
既然两个链表是递增的,我们只需要不断比较两个链表的头就好了
代码
function Merge(pHead1,pHead2){
let pMergeHead = null;
if(pHead1 === null) return pHead2;
if(pHead2 === null) return pHead1;
if(pHead1.val<pHead2.val){
pMergeHead = pHead1;
pMergeHead.next = Merge(pHead1.next,pHead2);
}else{
pMergeHead =pHead2;
pMergeHead.next = Merge(pHead1,pHead.next);
}
return pMergeHead;
}
十六、树的子结构
感觉还不太会
题目描述
输入两颗树A,B,判断B是不是A的子结构
题目分析
分析如何判断树B是不是树A的子结构,只需要两步。很容易看出是一个递归的过程。一般在数的求解方面都和递归有关。
(1)在树A中找到和B的根节点的值一样的节点R
(2)判断树A中以R为根系欸但的子树是不是包含和树B一样的节点
代码
function HasSubtree(pRoot1,pRoot2){
let res = false;
if(pRoot1===njull || pRoot2 === null) return false;
if(pRoot1.val === pRoot2.val) res = doseTress1HasTree2(pRoot1,pRoot2);
if(!res) res = HasSubtree(pRoot1.left,pRoot2);
if(!res) res = HasSubtree(pRoot1.right,pRoot2);
return res;
}
function doseTree1HasTree2(pRoot1,pRoot2){
if(pRoot2===null) return true;
if(pRoot1 === null) retuern false;
if(pRoot1.val!==pRoot2.val) return false;
return doseTree1HasTree2(pRoot1.left,pRoot2.left) && doesTree1HasTree2(pRoot1.right,pRoot2.right);
}
十七、二叉树的镜像
题目描述
操作给定的二叉树,将其变为源二叉树的镜像
分析
交换左右节点,递归即可
代码
function Mirror(root){
if(root===null) return;
[root.left,root.right]=[root.right,root.left];//应该要先交换,再往下递归
Mirror(root.left);
Mirror(root.right);
return root;
}
十八、顺时针打印矩阵
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。比如,如果输入以下矩阵:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
输出结构为1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
题目分析
可以看出肯定是需要先输出1,2,3,4.接下来8,12,16。观察可以发现,我们可以将举证往左转以下,这样又可以输出第一列,。。。循环下去
function printMatrix2(matrix){
if(!matrix) return;
let res = [];//用res来存最后要输出的元素
//二维数组.shift,输出的是第一行
const firstRow = matrix.shift();
res=res.concat(firstRow);
while(matrix.length){
//判断数组是否为空一定要用matrix.length来判断
matrix = rotateMatrix(matrix);
res = res.concat(matrix.shift());
}
return res;
}
//来反转数组
function rotateMatrix(matrix){
if(matrix.length===1) return matrix; //数组为一维数组时,直接返回原数组
const rows = matrix.length,
cols= matrix[0].length,
newMatrix = [];
for(let j = cols-1;j>=0;j--){
const tempMatrix = [];
for(let i = 0;i<rows;i++){
tempMatrix.push(matrix[i][j]);
}
newMatrix.push(tempMatrix);
}
return newMatrix;
}
十九、包含min函数的栈
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数
分析
要得到最小值肯定需要比较,和栈里面的数据一一比较,但是栈这种数据结构,你又只能和栈顶弹出来的数据进行比较,所以肯定需要一个临时栈。
可以添加一个辅助栈,每次压入数据栈时,把当前栈里面最小的值压入辅助栈当中,这样辅助栈的栈顶的数据一直是数据栈中的最小的值
比如输入5,4,3,8,10,11,12,1
则min依次入栈,5,4,3,3,3,3,3,1,z这样辅助栈的栈顶一直是最小的值
代码
const stack= [],
minStack = [];
let tmp = null;
function push(node){
if(tmp!==null){
if(tmp>node){
tmp=node;
}
stack.push(node);
minStack.push(tmp);
}else{
tmp=node;
stack.push(node);
minStack.push(tmp);
}
}
function pop(){
stack.pop();
minStack.pop();
}
function top(){
return stack[stack.length-1];
}
function min(){
return minStack[minStack.length-1];
}
二十、栈的压入、弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
题目分析
栈是先进后出的。
(1)借用一个辅助的栈,遍历压栈顺序,先将第一个放入栈中,这里是1
(2)r然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4
(3)很显然1不等于4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素
(4)则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成
(5)如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序
代码
function IsPopOrder(pushV,popV){
if(pushV.length!==popV.length || pushV.length===0)
return false;
var stack = [];
for(let i of pushV){
stack.push(i);
while(stack[stack.length-1]===popV[0]&&stack.length!==0){
stack.pop();
popV.shift();
}
}
return stack.length===0?true:false;
}
二十一、从上到下打印二叉树
题目描述
从上往下打印出二叉树的每个节点,同层节点从左至又打印## 题目分析
题目分析
相当于广度遍历
(1)树的广度遍历用队列,利用先进先出的特点来保存之前的节点,并操作之前的节点
(2)树的深度遍历一般用栈或者递归,利用先进后厨的特点来保存之前的节点,把之前的节点留到后面操作
代码
function PrintFromTopBottom(root){
const queue = [],
res = [];
if(root===null){
return res;
}
queue.push(root);
while(queue.length){
const pRoot = queue.shift();
if(pRoot.left!==null){
queue.push(pRoot.left);
}
if(pRoot.right!==null){
queue.push(pRoot.right);
}
res.push(pRoot.val);
}
return res;
}
二十二、二叉搜索树的后序遍历序列
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后续遍历的结果。如果是则输出yes,否则输出no,假设输入的数组的任意两个数字都互不相同。
复习
(1)概念
二叉排序树也叫二叉查找树、二叉搜索树
(2)性质
- 若它的左子树不空,则左子树上所有节点的值均小于它根节点的值
若它的右子树不空,则右子树上所有节点的值均大于它根节点的值
它的左、右树又分为二叉排序树
(3)数组的every方法
every是考察数组的整体特性,也就是考察数组中所有元素的共性。比如所有元素是否都是奇数,或者所有元素是否都是偶数。它关注的是数组整体元素的共性。只要有一个不满足,循环就会结束,接下来的数据就不会继续判断。
分析
1.找到该数组的最后一个树即位根节点
2.遍历该数组,找到第一个大于根节点的树,就是(左右子树的分割点),该数左边都是左子树部分,该数右边的除了根节点都是右子树的部分
3.利用数组的every方法,判断右子树部分是不是都满足条件(值大于root).every方法如果都满足条件则返回true,不满足返回false(空数组的时候也返回true).
4.对左右子树进行递归调用
代码
var verifyPostorder = function(postorder){
let len = postorder.length-1;
//根节点
let root = postorder[len];
if(len<2) return true;
let i = 0;
for(;i<len;i++){
if(postorder[i]>root) break;
}
let result = postorder.slice(i,len).every(x=>x>root);
if(result){
//对左右子树进行递归调用,左右子树通过i进行分割
return verifyPostorder(postorder.slice(0,i)) && verifyPostorder(postorder.slice(i,len))
}else{
return false
}
};
二十三、二叉树中和为某一值的路径
题目描述
输入一颗二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。路径定义为从根节点开始往下一直到叶节点所经过的结点形成一条路径。
题目分析
显然这题需要深度遍历整个树,并且把已经走过的节点的和与期望值作比较就选哪个,如果走到底还不符合要求的话,就要回退值。
代码
function FindPath(root, expectNumber) {
// write code here
const list = [],
listAll = [];
return findpath(root, expectNumber, list, listAll);
}
function findpath(root,expectNumber,list,listAll){
if(root===null){
return listAll;
}
list.push(root.val);
const x = expectNumber - root.val;
if(root.left === null && root.right === null && x=== 0){
listAll.push(Array.of(...list));
}
findpath(root.left,x,list,listAll);
findpath(root.right,x,list,listAll);
list.pop();
return listAll;
}
二十四、复杂链表的复制
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点。)返回结果为复制后复杂链表的head。(注意:输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
有点难,还不会
二十五、二叉搜索树与双向链表
还不是很懂
题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树种节点指针的指向。
题目分析
要生产排序的双向链表,那么只能是中序遍历,因为中序遍历才能从小到大,所以需要递归。
(1)先对左子树调整为双向链表,并用变量pLast指向最后一个节点
(2)再将中间节点和pLast连起来
(3)再去调整右子树
代码
function Convert(pRootOfTree) {
// write code here
if (pRootOfTree === null) return null;
let pLast = null;
pLast = ConvertNode(pRootOfTree, pLast);
let pHead = pLast;
while (pHead && pHead.left) {
pHead = pHead.left;
}
return pHead;
}
function ConvertNode(pNode, pLast) {
if (pNode === null) return;
if (pNode.left) {
pLast = ConvertNode(pNode.left, pLast);
}
pNode.left = pLast;
if (pLast) {
pLast.right = pNode;
}
pLast = pNode;
if (pNode.right) {
pLast = ConvertNode(pNode.right, pLast);
}
return pLast;
}
二十六、字符串的排列
难,还不是很会
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符串a,b,c所能排列出的所有字符串abc,acb,bca,cab和cba
输入描述:输入一个字符串,长度不超过9(可能有字符重复),字符只能包括大小写字母
题目分析
(1)递归法
1.把字符串分为两部分,第一部分为第一个字符,第二部分为第一个字符以后的字符串
2.然后接下来求后面那部分的全排列
3.再将第一个字符与后面的那部分字符逐个交换
(2)回溯法
也就是利用树去尝试不同的可能性,不断地去字符串数组里面拿一个字符出来拼接字符串,当字符串数组被拿空时,就把结果添加进结果数组里,然后回溯上一层。(通过往数组加回去字符以及拼接的字符串减少一个来回溯。
二十七、数组中出现次数超过一半的数
题目描述
数组中有有一个数字出现次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一般,因此输出2,如果不存在则输出0
题目分析
(1)基于快排
因为根据题目,那么排序后的数组中间的数就是那个出现次数超过一半的数
(2)根据数组特点来做
数组中有一个数字出现的次数超过数组长度的一般,也就是说它出现的次数比其他所有数字出现的次数的和还有多,那么就可以下手了。
第一种方法需要修改数组,第二种方法不需要修改数组
注意不要随便使用sort,sort是按照Unicode编码来的,13可能会排在2的前面。
复习
快排的思想是:通过一次排序将无序的数据分为两个独立的部分,其中一部分数据的值均比另一部分要小,再对这两部分分别进行排序,使得整个序列有序
//基于快排,没有使用教程中的方法,因为题目已经明确说了存在这个数,那么排序好以后,中间的肯定是我们要找的这个数
function MoreThanHalfNumSolution(numbers){
if(numbers.length <= 1) return numbers;
let arr=quickSort(numnbers);
let key = numbers.length>>1;
return arr[key];
}
function quickSort( arr ) {
const num = arr[0];
let left = [], right = [];
for(let i = 1;i < arr.length; i++) {
if(arr[i]<=num) left.push(arr[i]);
else right.push(arr[i]);
}
return quickSort(left).concat([num],quickSort(right));
}
二十八、最小的k个数
题目描述
输入n个整数,找出其中最小的k个数,例如输入4,5,1,6,2,7,3,8这八个数字,则最小的4个数字就是1,2,3,4
题目分析
(1)快排
(2)利用一个长度为k的额外容器,来存储最小的k个数字。容器未满则填满,再添加数字,将数字和容器的最大值比较,小的话就替换,大的话就舍去。这个容器要求可以直接得到最大值、能删除最大值,能添加值。那么很容易想到应该用最大堆当这个容器,当然也可以使用红黑树来实现。
代码
function GetLeastNumbersSolution(input, k){
if(input.length <= 1) return input;
let arr = quickSort(numnbers);
let newArr=[];
for(let i=0;i<input.length;i++){
newArr.push(arr[i]);
}
return newArr;
}
function quickSort( arr ) {
const num = arr[0];
let left = [], right = [];
for(let i = 1;i < arr.length; i++) {
if(arr[i]<=num) left.push(arr[i]);
else right.push(arr[i]);
}
return quickSort(left).concat([num],quickSort(right));
}
二十九、连续子数组和的最大值
题目描述
输入一个长度为n的整型数组nums,数组中的一个或连续多个正数组成一个子数组,子数组最小长度为1.求所有子数组的和的最大值。
输入:[1,-2,3,10,-4,7,2,-5]
返回值:18
题目分析
典型的动态规划问题,题目有一个进阶的要求,我们先解决空间复杂度为O(n),再在这基础上进行优化就会通透很多。
步骤
(1)确定dp数组
dp[i]表示以第i个数结尾的 连续子数组的最大和
显然,子数组的和的最大值就是dp数组中的最大值。因此我们只需要求出每个位置的dp[i],然后返回数组中的最大值即可。
(2)确定递推公式
dp[i]=Math.max(dp[i-1]+nbums[i],nums[i])
;
(3)初始化
dp[0]=nums[0]
,然后将其他的全部初始化为最小值,在js中用-Infinity
表示负无穷
(4)确定遍历顺序
由转移方程我们看了一知道,dp[i]是由dp[i-1]推出来的,所以从前向后遍历就好了。
代码
//空间复杂度为O(n)
var maxSubArray = =function(nums){
let dp = new Array(nums.length).fill(-Infinity);//初始化
dp[0]=nums[0];
let max = dp[0]; //记录dp数组中的最大值
for(let i=1;i<nums.length;i++){
dp[i]=Math.max(dp[i-1]+nums[i],nums[i]);
max = Math.max(max,dp[i]);
}
return max;
};
//空间复杂读为O(1)
function(nums){
let pre = nums[0],max=pre;
for(let i = 1;i<nums.length;i++){
pre=Math.max(pre+nums[i],nums[i]);
max=Math.max(max,pre);
}
return max;
};
连续子数组的最大和(进阶版)
要求
输入一个长度为n的整型数组,数组中的一个或连续多个整数组成一个子数组,找到一个具有最大和的连续子数组
1.子数组是连续的,比如[1,3,5,7,9]的子数组有[1,3],[3,5,7]等等,但是[1,3,7]不是子数组
2,如果存在多个最大和的连续子数组,那么返回其中长度最长的,该题数据保证这个最长的只存在一个
3.该题定义的子数组的最小长度为1,不存在为空的子数组,即不存在[]是某个数组的子数组
4.返回的数组不计入空间复杂度计算
和上面一样,状态转移为dp[i]=max(dp[i-1]+array[i],array[i]),这是最基本的求连续子数组的最大和。
但是题目要求需要返回长度最长的一个,我们则每次用left、right记录该数组的其实,需要更新最大值的时候(要么子数组和更大,要么子数组和相等的情况下区间要更长)顺便更新最终的区间首尾,我们的区间长度就是最长的。
function FindGreatSumOfSubArray(array){
let dp = new Array(array.length).fill(-Infinity);
dp[0]=array[0];
let max = dp[0];
//滑动区间
let left = 0,right=0;
//记录最大的区间
let maxL = 0,maxR=0;
for(let i =1;i<array.length;i++){
right++;
dp[i]=Math.max(dp[i-1]+array[i],array[]);
//更改区间新起点
if(dp[i-1]+array[i]<array[i]) left=right;
//更新最大值
if(dp[i]>max||dp[i]==max && (right-left+1)>(maxR-maxL+1)){
max = dp[i];
maxL = left;
maxR = right;
}
}
let resArr = new Array(maR-maxL+1);
for(let i=maxL;i<=maxR;i++){
resArr[i-maxL]=array[i];
}
return resArr;
}
三十、1~n整数中1出现的次数
三十一、把数组排成最小的数
题目描述
输入一个正整数数组,把数组里所有的数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。比如输入数组{3,32,321},则打印出这三个数字能派出的最小数字为 321323
题目分析
主要就是定义新的排序规则,也就是把前一个数和后一个数拼接起来的数,然后再与后一个数和前一个数拼接起来的数比较字典序
代码
function PrintMinNumber(numbers){
numbers.sort(function(s1,s2){
const c1=`${s1}${s2}`;
const c2=`${s2}${s1}`;
return c1>c2;
});
let min='';
numbers.forEach((i)=>min+=i);
return min;
}
三十二、丑数
还没有完全掌握
题目描述
把只包含因子2,3和5的数称作丑数。例如6,8都是丑数,但14不是,因为它包含因子7。习惯上我们把1当作第一个丑数。求按从小到大的顺序的第N个丑数。
题目分析
主要在于理解丑数的概念,只包含因子2,3和5的数称为丑数,那么我们可以先把因子2,3,5分离出来,那么剩下的就是其他因子,看是否为1,为1的话说明没有其他因子,那就是为丑数。不是1的话说明有其他因子,那么就不是丑数。
(1)暴力法,缺点就是暴力法连丑数的也计算,可能超时
(2)动态规划的思想,把前面的丑数先存着,生成后面的丑数。t2,t3,t5是判断点,用于判断从何处开始选出并乘以对应因子肯定会大于当前数组中的最大丑数,而前面的丑数不用考虑
代码
function GetUglyNumber_Solution(index){
if(index<=1) return 0;
let count = 0;
let num = 0;
while(count<index){
num++;
if(isUgly(num)){
count++;
}
}
return num;
}
function isUgly(num){
while(num%2===0) num/=2;
while(num%3===0) num/=3;
while(num%5===0) num/=5;
return num===1;
}
但是暴力法会超时
function GetUglyNumberSolution(index){
if(index<7) return index;
const res=[];
res[0]=1;
let t2 =0,
t3=0,
t5=0;
for(let i=1;i<index;i++){
res[i]=Math.min(res[t2]*2,res[t3]*3,res[t5]*5);
if(res[i]===res[t2]*2) t2++;
if(res[i] === res[t3]*3) t3++;
if(res[i] === res[t5]*5) t5++;
}
return res[index-1];
}
三十三、第一个只出现一次的字符
题目描述
在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置。
题目分析
只需要用map记录字符出现的次数就行,比较简单的题
代码
function FirstNotRepeatingChar(str){
if(str.length<1||str.length>10000) return -1;
const map={};
for(let i=0;i<str.length;i++){
if(!map[str[i]]){
map[str[i]]=1;
}else{
map[str[i]]++;
}
}
for(let i=0;i<str.length;i++){
if(map[str[i]]===1){
return i;
}
}
return -1;
}
三十四、数组中的逆序对
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。即输出P%1000000007
输入:题目保证输入的数组中没有相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
题目分析
显然暴力会超时
需要使用归并排序
代码
function InversePairs(data){
if(!data||data.length<2) return 0;
const copy = data.slice();
let count = 0;
count = mergeCount(data,copy,0,data.length-1);
return count%1000000007;
}
function mergeCount(data,copy,start,end){
if(start === end) return 0;
const mid = end-start>>1,
三十五、两个链表中的第一个公共节点
题目描述
输入两个链表,找出他们的第一个公共节点
题目分析
第一个公共节点,也就是说后面都是
我的理解是,第一个链表到公共节点的距离为a,另一个为b,a不一定等于b,但是a+b+c==a+b+c,c为剩下公共的长
疑问
但是有一个疑问,后面一定是全部一样的吗?
代码
function FindFirstCommonNode(pHead1,pHead2){
let a=pHead1;
let b = pHead2;
while(a!==b){
a=a?a.next:pHead2;
b=b?b.next:pHead1;
}
return a;
三十六、数字在排序数组中出现的次数
题目描述
统计一个数字在排序数组中出现的次数
题目分析
可以使用暴力
使用二分的话,一般我们二分是查找单个数字,但是现在是多个,所以我们需要确定数的开始位置和结束位置,(未整理)
代码
function GetNumberOfK(data,k){
var start = data.indexOf(k),
count = 0,
i = start;
while(i<data.length && data[i]==k){
count++;
i++
}
return count;
}
三十七、二叉树的深度
题目描述
输入一颗二叉树,求该树的深度,从根节点到叶节点一次警告的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度
题目分析
树的深度 = 左子树的深度和右子树深度中最大者+1
代码
function TreeDepth(pRoot){
if(pRoot===null) return 0;
const leftDep = TreeDepth(pRoot.left);
const rightDep = TreeDepth([pRoot.right);
return Math.max(leftDep,rightDep)+1;
}
三十八、平衡二叉树
题目描述
输入一颗二叉树,判断该二叉树是否是平衡二叉树
复习
平衡二叉树
(1)是二叉排序树
(2)任何一个节点的左子树或者右子树都是平很二叉树(左右高度差小于等于1)
题目分析
第一种方法
正常思路,应该会获得节点的左子树和右子树的高度,然后比较高度差是否小于1
这样的问题就是节点会被重复遍历
第二种方法
改进办法就是在求高度的同时判断是否平衡,如果不平衡就返回-1,否则返回树的高度。并且当左子树高度为-1时,就没必要去求右子树的高度了,可以直接一路返回到最上层了。
代码
//第一种
function IsBalanced_Solution(pRoot){
if(pRoot == null) return true;
let leftLen = TreeDepth(pRoot.left);
let rightLen = TreeDepth(pRoot.right);
return Math.abs(rightLen-leftLen)<=1 && IsBalanced_Solution(pRoot.left) && IsBalanced_Solution(pRoot.right);
}
function TreeDepth(pRoot){
if(pRoot==null) return 0;
let leftLen = TreeDepth(pRoot.left);
let rightLen = TreeDepth(pRoot.right);
return Math.max(leftLen,rightLen)+1;
}
三十九、数组中只出现一次的数字
题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次,请写程序找出这两个只出现一次的数字。
分析
(1)使用js中的indexOf()和lastIndexOf(),只要两个相等,就是只出现一次的数
(2)使用map记录下每个数的次数,占空间
代码
//第一种方法
function FindNumsAppearOnce(array){
const res=[];
for(let i=0;i<array.length;i++){
if(array.indexOf(array[i])===array.lastIndexOf(array[i])){
res.push(array[i]);
}
}
return res;
}
//第二种方法
function FindNumsAppearOnce2(array){
const map={},
res=[];
for(let i=0;i<array.length;i++){
if(!map[array[i]]){
map[array[i]]=1;
}else{
map[array[i]]++;
}
}
//遍历map
for(let i=0;i<array.length;i++){
if(map[array[i]]===1){
res.push(array[i]);
}
}
return res;
}
四十、和为S的连续正数序列
题目描述
小明很喜欢数学,有一天他在做数学作业时,要求计算出9-16的和,他马上就写出了正确答案是100.但是他并不满足于此。他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22.现在把问题交给你,你能不能也很快的找出所有和为S的连续正整数序列?
输出描述
输出所有和为S的连续正数序列。序列内按照从小到大的顺序,序列间按照开始数字从小到大的顺序
题目分析
使用left和right两个变量来代替滑动窗口的左边界和右便捷
核心while循环的条件是:只要左边界 小于 目标值的一般就要进入循环,只要不小于循环结束,说明此时以及找完了
定义temp变量,用来存放滑动窗口中的所有元素的和
当temp小于target的值的时候,右便捷向右扩一个。
当temp大于target的时候,左边向右一个
代码
var findContinuousSequence = function(target){
let left=1;
let right=1;
let arr=[];
let temp =0;
//下面的这个result数组是用来返回的
let result=[];
//核心条件是当left>=target/2的时候 left+right》=target
while(left<(target/2)){
while(temp<target){
temp=temp+right;
arr.push(right);
right+=1;
}
while(temp>target){
temp=temp-left;
arr.shift();
left+=1;
}
if(target === temp){
temp=temp-left;
left++;
result.push([...arr]);
arr.shift();
}
}
return result;
};
四十一、和为S的字符串
题目描述
输入一个递增排序的数组和为一个数字S,在数组中查找两个数,他们的和等于S,如果有多对数字的和等于S,输出两个数的乘积最小的。
题目分析
题目很简单,使用双指针就可以做出来。相距最远乘积最小。
代码
function FindNumbersWithSum(array,sum){
if(array.length<2) return [];
let left = 0,
right=array.length-1;
const res=[];
while(left<right){
if(array[left]+array[right]<sum){
left++;
}else if(array[left]+array[right]>sum){
right--;
}else{
res.push(array[left],array[right]);
break;
}
}
return res;
}
四十二、左旋转字符串
题目描述
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=“abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”
代码
function LeftRotateString(str,n){
if(str===null||str.length===0) return '';
n=n%str.length;
return str.slice(n)+str.slice(0,n);
四十三、单次翻转序列
题目描述
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
代码
function ReverseSentence(str){
return str
.split(' ')
.reverse()
.join(' ');
}
四十四、扑克牌顺序
自己的一些容易错的点
- 想要用根节点的值,不要直接写
root
,应该写root.val
~
表示取反,~~
表示去掉小数部分- 数组的一些方法
Array(5),fill(1) //[1,1,1,1,1]
- sort()
(1)sort()方法用于对数组的元素进行排序(常规数组、对象数组)
(2)排序顺序可以是字母或数字,并按升序或降序。
(3)默认排序顺序为按字母顺序升序(按照字符串unicode编码)
(4)注意:当数字是按字母顺序排列时”40“将排在”5“前面
使用数字排序,你必须通过一个函数作为参数来调用
函数指定数字是按照升序还是降序排列
这种方式会改变原始数组
(5)基本使用
见该博主整理
- map
map()方法会返回一个新的数组,数组中的元素为原始数组元素调用函数处理后的值
map方法会给原数组中的每个元素都按顺序调用一次回调函数。回调函数每次执行后的返回值(包括undefined)组合起来形成一个新数组。回调函数只会在有值的索引上被调用,那些从来没有被赋过值或者用delete删除的索引则不会被调用
- Math的几个方法
Math.round() 四舍五入
Math.ceil()向上取整
Math.floor()向下取整
Math.abs()取绝对值
- splice和slice用法的区别
splice
向数组中添加项目或者删除项目,并返回删除的项目
array.splice(index,howmany,item1,...item)
//1.index必写,整数,指定在什么位置添加、删除项目,使用负值指定从数组末尾开始的位置
//2可选,要删除的项目数,若设置为0,则不会删除任何项目
//3可选,表示要添加到数组中的新项目
//返回值:新数组,包含删除的项目(如果有)
//会改变原数组
let fruits = ["Banana","Orange","Apple","Mango"];
let ret = fruits.splice(2,1,"Lemon","Kiwi"];
console.log(fruits);//["Banana","Orange","Lemon","Kiwi","Mango"];
console.log(ret);//["Apple"]
slice
是从已有的数组中返回选定的元素,提取数组中的某一部分,并以新的字符串返回被提取的部分
slice方法不会改变原数组
array.slice(start,end)
//1.start可选,规定从何处开始选取,如果该参数为负数,则表示从原数组中倒数第几个元素开始提取
//2.end可选,规定从何处结束选取。该参数是数组片段结束处的数组下标,截取的片段不包含该元素
//3.返回值:返回一个新的数组,包含从start(包括该元素)的arrayObject中的元素
```javascript
var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var citrus = fruits.slice(1,3);
console.log(citrus) // [Orange,Lemon]
- split()方法
split()方法用于把字符串分割成字符串数组
sr.split(separator,howmany)
//1.必需,字符串或正则表达式,从该参数指定的地方分割stringObject
//2.howmany可选,该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多余这个参数指定的数组,如果没有设置该赞数,整个字符串都会被分割,不考虑它的长度
- reverse()方法
reverse()方法将数组中的元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组
- join()方法
用于将数组中所有元素放入一个字符串,元素通过指定的分隔符进行分割
join不传参数或者传入undefined会默认以逗号分割。