一、排序算法
1.冒泡排序
基本思想:重复地走访要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
function bubbleSort(arr) {
const n = arr.length;
for (let i = 0; i < n - 1; i++) {
for (let j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换元素
const temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
2.快速排序
基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
const pivot = arr[0];
const left = [];
const right = [];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot,...quickSort(right)];
}
3.选择排序
基本思想:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
function selectionSort(arr) {
const n = arr.length;
for (let i = 0; i < n - 1; i++) {
let minIndex = i;
for (let j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
if (minIndex!== i) {
// 交换元素
const temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
return arr;
}
二、搜索算法
1. 线性搜索
基本思想:从数组的一端开始,逐个检查每个元素,直到找到目标元素或遍历完整个数组。
function linearSearch(arr, target) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) {
return i;
}
}
return -1;
}
2. 二分搜索
基本思想:针对一个有序数组,每次取中间值与目标值比较,根据比较结果缩小搜索范围,重复这个过程直到找到目标值或确定目标值不存在。
function binarySearch(arr, target) {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
三、数组和字符串相关算法
1. 数组去重
方法一:使用 ES6 的 Set 和扩展运算符
function removeDuplicates(arr) {
return [...new Set(arr)];
}
方法二:遍历数组并使用对象存储已出现的元素
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = [];
const seen = {};
for (const item of arr) {
if (!seen[item]) {
uniqueArray.push(item);
seen[item] = true;
}
}
console.log(uniqueArray); // [1, 2, 3, 4, 5]
2. 查找数组中的最大值和最小值
方法一:循环遍历
const arr = [5, 3, 8, 4, 2];
let min = arr[0];
let max = arr[0];
for (const item of arr) {
if (item < min) {
min = item;
}
if (item > max) {
max = item;
}
}
console.log(`最小值:${min},最大值:${max}`); // 最小值:2,最大值:8
方法二:使用Math.min()和Math.max()结合扩展运算符
const arr = [5, 3, 8, 4, 2];
const minValue = Math.min(...arr);
const maxValue = Math.max(...arr);
console.log(`最小值:${minValue},最大值:${maxValue}`); // 最小值:2,最大值:8
3. 字符串反转
方法一:使用循环遍历和拼接。
function reverseString(str) {
let reversed = '';
for (let i = str.length - 1; i >= 0; i--) {
reversed += str[i];
}
return reversed;
}
方法二:使用数组的reverse()方法和join()方法
const str = "hello";
const reversedStr = str.split("").reverse().join("");
console.log(reversedStr); // "olleh"
4. 判断一个字符串是否为回文字符串
方法一:使用循环遍历比较字符
const str = "racecar";
let isPalindrome = true;
for (let i = 0; i < str.length / 2; i++) {
if (str[i]!== str[str.length - 1 - i]) {
isPalindrome = false;
break;
}
}
console.log(isPalindrome); // true
方法二:使用字符串反转后比较
const str = "racecar";
const reversedStr = str.split("").reverse().join("");
const isPalindrome = str === reversedStr;
console.log(isPalindrome); // true
5. 数组转字符串
方法一:使用join()方法
join()方法将数组的所有元素连接成一个字符串,并可以指定连接的分隔符。如果不指定分隔符,默认使用逗号,连接。
const arr = ['apple', 'banana', 'cherry'];
const str = arr.join();
console.log(str); // "apple,banana,cherry"
const strWithSeparator = arr.join('-');
console.log(strWithSeparator); // "apple-banana-cherry"
方法二:使用toString()方法
toString()方法也可以将数组转换为字符串,它的效果和使用默认分隔符的join()方法类似。
const arr = [1, 2, 3];
const str = arr.toString();
console.log(str); // "1,2,3"
6. 字符串转数组
方法一:使用split()方法
split()方法可以将字符串分割成数组,根据指定的分隔符进行分割。如果不指定分隔符,将字符串分割成单个字符的数组。
const str = "apple,banana,cherry";
const arr = str.split(',');
console.log(arr); // ["apple", "banana", "cherry"]
const str2 = "hello";
const arr2 = str2.split('');
console.log(arr2); // ["h", "e", "l", "l", "o"]
方法二:使用扩展运算符[…string]
ES6 的扩展运算符可以将字符串转换为字符数组。
const str = "world";
const arr = [...str];
console.log(arr); // ["w", "o", "r", "l", "d"]
7. 数组扁平化
将一个多维数组转变为一个一维数组
const arr = [1, [2, [3, [4, 5]]], 6];
// => [1, 2, 3, 4, 5, 6]
方法一: 使用es6新方法flat()
depth指定要提取嵌套数组的结构深度,默认值为1.
const res1 = arr.flat(depth);
方法二:利用正则
转成字符串再替换中括号再转成数组,但数据类型都会变为字符串。
const res2 = JSON.stringify(arr).replace(/[|]/g, '').split(',');
方法三:JSON方法加正则
转成字符串再替换中括号再转成数组,全程使用JSON,保证了数据格式不变。
const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/[|]/g, '') + ']');
方法四:使用reduce和concat
const flatten = arr => {
return arr.reduce((pre, cur) => {
return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
}, [])
}
const res4 = flatten(arr);
方法五:函数递归
const res5 = [];
const fn = arr => {
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
fn(arr[i]);
} else {
res5.push(arr[i]);
}
}
}
fn(arr);
8. 数组交集
由所有属于集合A且属于集合B的元素所组成的集合,叫做集合A与集合B的交集
利用循环和indexOf、include
- 利用indexOf
const intersection1 = (arr1, arr2) => {
const res = [];
for (let i = 0; i < arr1.length; i++) {
if (arr2.indexOf(arr1[i]) !== -1) res.push(arr1[i]);
}
return res;
};
- 利用include
const intersection2 = (arr1, arr2) => {
const res = [];
for (let i = 0; i < arr1.length; i++) {
if (arr2.includes(arr1[i])) res.push(arr1[i]);
}
return res;
};
9. 数组差集
差集:A和B是两个集合,则所有属于A且不属于B的元素构成的集合,叫做集合A和集合B的差集。
利用循环和indexOf、include
- 利用indexOf
const intersection1 = (arr1, arr2) => {
const res = [];
for (let i = 0; i < arr1.length; i++) {
if (arr2.indexOf(arr1[i]) == -1) res.push(arr1[i]);
}
return res;
};
- 利用include
const intersection2 = (arr1, arr2) => {
const res = [];
for (let i = 0; i < arr1.length; i++) {
if (!arr2.includes(arr1[i])) res.push(arr1[i]);
}
return res;
};
10. 数组并集
给定两个集合A,B,把它们所有的元素合并在一起组成的集合,叫做集合A与集合B的并集
方法一:利用concat并结合Set
先将两个数组连成一个数组,然后去重
const res1 = Array.from(new Set(arr1.concat(arr2)));
方法二:利用循环和indexOf、include
- 利用indexOf
const union1 = (arr1, arr2) => {
const res = [];
for (let i = 0; i < arr1.length; i++) {
if (arr2.indexOf(arr1[i]) == -1) res.push(arr1[i]);
}
return arr2.concat(res);
};
- 利用include
const union2 = (arr1, arr2) => {
const res = [];
for (let i = 0; i < arr1.length; i++) {
if (!arr2.includes(arr1[i])) res.push(arr1[i]);
}
return arr2.concat(res);
};
11. 列表转成树结构
例如:
[
{
id: 1,
text: "节点1",
parentId: 0, //这里用0表示为根节点
},
{
id: 2,
text: "节点1_1",
parentId: 1, // 通过这个字段来确定子父级
},
// ...
]
// 转成
[
{
id: 1,
text: "节点1",
parentId: 0,
children: [
{
id: 2,
text: "节点1_1",
parentId: 1,
},
],
}
// ...
];
实现方法:
function listToTree(data) {
let temp = {};
let treeData = [];
// 放到临时对象,方便获取
for (let i = 0; i < data.length; i++) {
temp[data[i].id] = data[i];
}
// 遍历
for (let i in temp) {
// 不是根节点
if (+temp[i].parentId != 0) {
第一次添加就先初始化children数组
if (!temp[temp[i].parentId].children) {
temp[temp[i].parentId].children = [];
}
temp[temp[i].parentId].children.push(temp[i]);
} else {
// 根节点直接放到数组
treeData.push(temp[i]);
}
}
return treeData;
}
12. 树形结构转成列表
例如:
[
{
id: 1,
text: "节点1",
parentId: 0,
children: [
{
id: 2,
text: "节点1_1",
parentId: 1,
},
],
},
// ...
]
// 转成
[
{
id: 1,
text: "节点1",
parentId: 0, //这里用0表示为根节点
},
{
id: 2,
text: "节点1_1",
parentId: 1, //通过这个字段来确定子父级
}
];
实现方法:
function treeToList(data) {
let res = [];
// 定义转换方法
const tl = (tree) => {
tree.forEach((item) => {
if (item.children) {
// 递归
tl(item.children);
delete item.children;
}
res.push(item);
});
};
tl(data);
return res;
}