算法基础
1、 冒泡排序
先来个常规思路
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1; j++) {
if (arr[j] > arr[j + 1]) {
const temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
console.log(bubbleSort([2, 45, 1, 23, 55, 21, 44, 53, 7, 26]));
1.1、 优化点一
- 第二层循环,思考下循环次数: arr.length - 1;
- 冒泡排序的基本逻辑是从左往右滑动窗口,将大的元素移动到右边。
- 第一次循环执行下来,最大的元素已经到了最右边了,第二次循环其实不需要再考虑最后一个元素了
- 同样的,第三次循环也不需要比较倒数第二个了
优化之后的代码如下:
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
const temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
console.log(bubbleSort([2,45,1,23,55,21,44,53,7,26]));
1.2、 优化点二
- 考虑一个场景,第二层循环执行下来,并没有做过元素互换,说明什么
- 说明数组排序已经是从小到大排序的了
- 这样的话,我们直接结束循环即可
function bubbleSort(arr) {
let swapped = true;
for (let i = 0; i < arr.length - 1 && swapped; i++) {
swapped = false;
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
const temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
}
}
}
return arr;
}
console.log(bubbleSort([2,45,1,23,55,21,44,53,7,26]));
1.3、 优化点三
其实第一个循环完全也是没有必要的。
function bubbleSort(arr) {
let swapped = true;
while(swapped) {
swapped = false;
for (let j = 0; j < arr.length - 1; j++) {
if (arr[j] > arr[j + 1]) {
const temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
}
}
}
return arr;
}
console.log(bubbleSort([2,45,1,23,55,21,44,53,7,26]));
1.4、 优化点四
// 数组元素互换,可以使用 array destructuring
// example
const [first, second, third] = ["Laide", "Gabriel", "Jets"];
const arr = [1, 2, 3];
[arr[1], arr[2]] = [arr[2], arr[1]];
console.log(arr);
// [1, 3, 2]
function bubbleSort(arr) {
let swapped = true;
while(swapped) {
swapped = false;
for (let j = 0; j < arr.length - 1; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
swapped = true;
}
}
}
return arr;
}
console.log(bubbleSort([2,45,1,23,55,21,44,53,7,26]));
2、 快速排序
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
const middleIndex = Math.floor(arr.length / 2);
const middleIndexValue = arr[middleIndex];
const left = [];
const right = [];
for (let i = 0; i < arr.length; i++) {
if (i === middleIndex) continue;
if (arr[i] < middleIndexValue) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), middleIndexValue, ...quickSort(right)];
}
console.log(quickSort([1, 3, 5, 7, 9, 2, 4, 6]));
3、 插入排序
插入排序的核心思路是:每次从原数组往新数组插入,插入的位置要求大于左边,小于右边。
边界条件:
- 小于第一个,直接往左边插入
- 比新数组最后一个元素大,没有后边了,运行右边为 undefined
function insertSort(arr) {
const res = [arr.shift()];
while(arr.length) {
const value = arr.shift();
for (let i = 0; i < res.length; i++) {
const current = res[i];
const next = res[i + 1];
if (i === 0 && value <= current) {
res.unshift(value);
} else if (value > current && (value <= next || next === undefined)) {
res.splice(i + 1, 0, value);
break;
}
}
}
return res;
}
function rang(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
let count = 100;
while (count--) {
const list = Array(10).fill(0).map(() => rang(1, 500));
const a = insertSort(list);
console.log(a.length, JSON.stringify(a));
}
4、 选择排序
选择排序:核心思想就是选择一个最大的元素,往新的数组去执行 push 操作。
function selectSort(a) {
const sortedArr = [];
while (a.length > 0) {
let max = a[0];
let markIndex = 0;
for (let i=1; i < a.length; i++) {
if (a[i] < max) {
max = a[i];
markIndex = i;
}
}
sortedArr.push(max);
a.splice(markIndex, 1);
}
return sortedArr;
}
function rang(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
let count = 100;
while (count--) {
const list = Array(10).fill(0).map(() => rang(1, 500));
const a = selectSort(list);
console.log(a.length, JSON.stringify(a));
}
5、 算法:动态规划
1块、4块、5块,求总数n块的最小硬币数
function minCoins(coins, target) {
// 创建一个数组存储最小硬币数
const dp = new Array(target + 1).fill(Infinity);
// 初始化 0 元的硬币数为 0
dp[0] = 0;
// 遍历每个目标金额
for (let i = 1; i <= target; i++) {
// 遍历每种硬币面额
for (let j = 0; j < coins.length; j++) {
// 如果当前硬币面额小于等于目标金额
if (coins[j] <= i) {
// 更新最小硬币数
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}
// 如果无法凑成目标金额,返回 -1
return dp[target] === Infinity ? -1 : dp[target];
}
// 示例用法
const coins = [1, 4, 5];
const target = 10;
const result = minCoins(coins, target);
console.log(result); // 输出 2
这个解决方案使用动态规划的方法来求解最小硬币数。它首先创建一个数组 dp,存储每个目标金额所需的最小硬币数。初始化 dp[0] 为 0,因为 0 元不需要任何硬币。
然后,它遍历每个目标金额,并对于每个目标金额,遍历所有可用的硬币面额。如果当前硬币面额小于等于目标金额,则更新 dp[i] 为 dp[i - coins[j]] + 1,这样就可以得到最小硬币数。
最后,如果无法凑成目标金额,则返回 -1,否则返回 dp[target]。
这个解决方案的时间复杂度是 O(n * m),其中 n 是目标金额,m 是硬币种类数。空间复杂度是 O(n),用于存储 dp 数组。
6、 数据转换
// 问题:
// {a:1,b:{c:2,d:{e:3},g:[4,{h:5}]}}
// 转成
// {
// "a":1,
// "b.c":2,
// "b.d.e":3,
// "b.g[0]":4,
// "b.g[1].h":5,
// }
function dataTransform(obj, path = '', data = {}) {
if (Array.isArray(obj)) {
obj.forEach((item, index) => {
if (typeof item === 'object') {
dataTransform(item, `${path}[${index}].`, data);
} else {
data[`${path}[${index}]`] = item;
}
});
} else if (typeof obj === 'object') {
for (let key in obj) {
const item = obj[key];
if (typeof item === 'object') {
dataTransform(item, `${path}${key}${Array.isArray(item) ? '' : '.'}`, data);
} else {
data[`${path}${key}`] = item;
}
}
}
return data;
}
console.log(dataTransform({a:1,b:{c:2,d:{e:3},g:[4,{h:5}]}, x: [
3,
{ y: 4, z: [45] }
]}));
7、 数组转树型结构
// const arr = [
// { id: 1, name: "i1" },
// { id: 2, name: "i2", parentId: 1 },
// { id: 4, name: "i4", parentId: 3 },
// { id: 3, name: "i3", parentId: 2 },
// { id: 8, name: "i8", parentId: 7 },
// ];
const arr = [
{ id: 1, name: 'i1'},
{ id: 2, name: 'i2', parentId: 9},
{ id: 3, name: 'i3', parentId: 5},
{ id: 4, name: 'i4', parentId: 1},
{ id: 5, name: 'i5', parentId: 1},
{ id: 6, name: 'i6', parentId: 2},
{ id: 7, name: 'i7', parentId: 3},
];
function transformArrayToTree(arr) {
const tree = {};
const result = [];
for (const node of arr) {
tree[node.id] = node;
}
for (const node of arr) {
if (node.parentId) {
const parent = tree[node.parentId];
if (parent) {
parent.children = parent.children || [];
parent.children.push(node);
delete node.parentId;
}
} else {
result.push(node);
}
}
return result;
}
console.log(JSON.stringify(transformArrayToTree(arr), null, 2));