下面我会列举数组排序、斐波那契数列、数组去重、数组扁平化、输出一个正数n,要求输出所有和为n的连续正整数序列这几种算法题的几种实现解法~
我比较着重讲的是算法思想,尽量通俗易懂,看了真香系列~
举个简单的例子,数组[5,4,3,2,1]
第一轮比较,变为了[4,3,2,1,5]
,一共比较了四次
第二轮比较,变为了[3,2,1,4,5]
,一共比较了三次(无需和最后一项对比,因为上一轮已经把该数组中最大的数放到了末尾)
第三轮比较,变为了[2,1,3,4,5]
,一共比较了两次
第四轮比较,变为了[1,2,3,4,5]
,一共比较了一次,结束~
显而易见,一共需要两次for循环,上面这种说的还是最坏的一种情况。另外例举一种最好的情况,数组[1,2,3,4,5]
,第一轮比较的时候,结果是无需交换任何值,这意味着,以后的每一轮比较,都是无需交换值的,因为此时数组已经排好序了;数组[2,1,3,4,5]
,也是同理,第一轮比较后,交换了一次值,变为了[1,2,3,4,5]
,第二轮比较后,同样发现无需交换值,此时我们可以直接结束循环,返回结果了。
Array.prototype.bubble = function () {
let _this = this,
n = _this.length,
flag = false;// 优化
// 控制比较多少轮
for (let i = 0; i < n - 1; i++) {
// 每轮比较多少次
for (let j = 0; j < n - 1 - i; j++) {
// 当前项与后一项对比,如果比后一项大,则交换位置
if (_this[j] > _this[j + 1]) {
[_this[j], _this[j+1]] = [_this[j+1], _this[j];
flag = true;
}
}
if (!flag) break;
flag = false;
}
return _this;
}
const arr = [2, 1, 4, 6, 17, 5, 3, 88, 34];
console.log(arr.bubble());
举个形象的例子,就像抓牌一样,先抓一张牌放在手里
Array.prototype.insert = function () {
let _this = this,
n = _this.length,
ary = [];
ary.push(_this[0]);
for (let i = 1; i < n; i++) {
for (let j = ary.length - 1; j >= 0; j--) {
if (_this[i] > ary[j]) {
ary.splice(j + 1, 0, _this[i]);// 在位置j后面插入_this[i]
break;
}
if (j === 0) ary.unshift(_this[i])
}
}
return ary;
}
const arr = [2, 1, 4, 6, 17, 5, 3, 88, 34];
console.log(arr.insert());
这里是用到二分法(递归实现)
下面这种图是这种思想的一个图(看不懂就跳过把,毕竟这种图有丢丢潦草)
Array.prototype.middle = function () {
let _this = this,
middle = Math.floor(_this.length / 2);// 当数组不可再分时,结束递归(即数组长度为1时)
if (middle === 0) return _this;
let left = [],
right = [];
for (let i = 0; i < _this.length; i++) {
if (i === middle) continue;
_this[i] < _this[middle] ? left.unshift(_this[i]) : right.push(_this[i]);
}
return left.middle().concat(_this[middle], right.middle());
}
const arr = [2, 1, 4, 6, 17, 5, 3, 88, 34];
console.log(arr.middle());
什么是斐波那契数列呢?这种数组的特点就是:数组前两项为1,后面每一项的值都是自己前两项的值的和,例如它~
[1,1,2,3,5,8,13,21....]
如何输出该数列中下标为n的数呢?
下面我例举了两种方案,第一种超级好理解,一看就会那种~
[1,1]
,每次前两项相加的值添加到该数组中边看着代码理解吧~
function feibonaqi(n) {
let arr = [1, 1];
// 循环多少次 n+1-2次
for (let i = 0; i < n + 1 - 2; i++) {
arr.push(arr[arr.length - 2] + arr[arr.length - 1])
}
return arr[arr.length - 1]
}
console.log(feibonaqi(5));
第二种方案是递归实现的~
function feibonaqi(n) {
if (n === 0 || n === 1) {
return 1;
}
return feibonaqi(n - 1) + feibonaqi(n - 2);
}
网络上数组去重的算法数不胜数,但归根到底,都是那几种思想,这里我列出三种方案,看完这三种方案,你同样也可以写出“关于数组去重的n种算法”~
Set
是无序不可重复的多个value的集合体,重复的元素在Set中自动被过滤掉。
注意:Set返回的数据类型是"object"
console.log(typeof new Set([1,2,3,3,2]));//object
let arr = [1, 2, 4, 5, 2, 3, 4, 6, 7, 7, 8, 1];
let ary = Array.from(new Set(arr)); // 将数据类型转换为数组类型
//==============================
// 或者这样子做也可以将数据类型转换为数组类型
let ary = [...new Set(arr)];//利用es6的拓展运算符,将一个数组转为用逗号分隔的参数序列
这种思想呢,就是遍历原数组,通过includes/indexof
来查看是否有重复元素。
(1)新建一个数组来装。遍历原数组,如果在新数组中没有找到正在遍历的元素,则添加进去
let arr = [1, 2, 4, 5, 2, 3, 4, 6, 7, 7, 8, 1];
let ary = [];
for (let i = 0; i < arr.length; i++) {
// if(ary.indexOf(arr[i]) === -1) ary.push(arr[i])
if(!ary.includes(arr[i])) ary.push(arr[i])
}
console.log(ary);
(2)删除原来数组的重复项。遍历原数组,如果有重新项,则删除。但这样子做容易有数组塌陷的问题,注意哦!
let arr = [1, 2, 4, 5, 2, 3, 4, 6, 7, 7, 8, 1];
for(let i = 0;i<arr.length;i++){
if(arr.indexOf(arr[i]) !== i){
arr.splice(i,1);
i--;// 防止数组塌陷,缺点就是每次只要有重复项,该项后面的所有元素的下标都要重新排一遍
}
}
console.log(arr)
另外一种解决数组塌陷的方式,如下~
let arr = [1, 2, 4, 5, 2, 3, 4, 6, 7, 7, 8, 1];
for(let i = 0;i<arr.length;i++){
if(arr.indexOf(arr[i]) !== i){
arr[i] = null;// 把重复项赋值为null
}
}
// 再遍历一次数组,把null值的元素剔除掉
arr = arr.filter(item =>item !== null);// 如果省略{}就可以省略return
console.log(arr)
首先就是将数组排序,这样子重复的项自然会相邻,利用repalce的正则捕获就可以完成了。这里如果不了解replace里面回调函数的运行机制的孩纸,可以去补补正则的功课哈~
let arr = [1, 2, 4, 5, 2, 3, 4, 6, 7, 7, 8, 1];
arr.sort((a,b)=>a-b);
arr = arr.join('@')+'@';
let reg = /(\d+@)\1*/g,
ary=[];
// 匹配几次,回调函数就执行几次,
arr.replace(reg,(current,arg)=>ary.push(Number(arg.slice(0,arg.length-1))))
console.log(ary)
例如[1,2,3,4,5]
就是一维数组,下面这个是四维数组
let arr = [ // 这是个四维数组
[1, 2, 3, 4, 6],
22,
[1, 2, 3, [1, 1, 4, 5], [8, 9, 7, [1, 2, 3, 4, 77, 24], 6, 6, 5]]
]
扁平化就是把数组”降维“降到一维的意思~
实现数组扁平化同样也有很多方法,这里也是例举几种~
es6里面数组原型的flat方法,可以实现扁平化
arr.flat(n); //arr数组扁平化n级
arr = arr.flat(Infinity);//将数组扁平化无穷极,也就是直接到一维数组啦
(1)
while (arr.some(item => Array.isArray(item))) {// 只有有任意一个元素是数组
arr = [].concat(...arr)
}
(2) 利用递归实现~
只要里面的元素有一个是数组类型的,就继续循环该元素,直到循环的数组的元素没有数组类型为止,返回该元素
function flat() {
let ary = [],
_this = this;
let fn = (arr) => {
for (let i = 0; i < arr.length; i++) {
if (arr[i] instanceof Array) {
fn(arr[i]);
continue;
}
ary.push(arr[i]);//把不是数组的元素放到新数组里面去
}
}
fn(_this);
return ary;
}
Array.prototype.flat = flat;
console.log(arr.flat())
例如,输入15
,输出[ [ 1, 2, 3, 4, 5 ], [ 4, 5, 6 ], [ 7, 8 ] ]
解题思想就是——先算出该正数的中间数(向上取整),因为数列最大的数不可能超过这个数。
例如15的中间数为8,8+9就肯定会超过15,所以输出结果里面序列的数字一定是1~8之间的,那么我们就在这个区间进行判断即可
直接上代码把~
// 创建一组a-b之间的连续正数数组
function createArr(a, b) {
// let arr =[];
// for(let i = a;i<=b;i++){
// arr.push(i);
// }
// return arr;
return new Array(b - a + 1).fill(null).map(item => item = a++)
}
function fn(n) {
let middle = Math.ceil(n / 2);// 该序列最大的数不超过这个数
let arr = [],
sum = 0;
for (let i = 1; i <= middle; i++) {
sum = 0;
for (let j = i; j <= middle; j++) {
sum += j;
if (sum === n) {
arr.push(createArr(i, j));
break;
}
if (sum > n) {
break;
}
}
}
return arr;
}
console.log(fn(15))
能看到这里的你,一定很棒!!