这是前端算法入门第三篇,介绍数据结构与算法中的排序算法,搜索算法,以及常见的算法面试题,总结常见的解题思路,让你事半功倍。
文章主要包含内容:
- 排序算法
- 冒泡排序
- 快速排序
- 插入排序
- 归并排序
- 选择排序
搜索算法
顺序算法
二分搜索
算法思想
分而治之
动态规划
贪心算法
回溯算法
⭐️⭐️⭐️⭐️⭐️
原理:
function bubbleSort(arr){
const len = arr.length
if(len<=1)return
for(let i = 0;i< len - 1 ;i++){
for(let j = 0;i<len - i -1;j++){
if(arr[j]>arr[i+1]){
const temp =arr[i]
arr[i] = arr[j+1]
arr[j+1] = temp
}
}
}
}
⭐️⭐️⭐️⭐️⭐️
通过一趟排序将要需要排序的数据分割成独立两部分
,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按这方法对这两部分数据分别进行快速排序
,整个排序可以进行递归进行,达到整个数据变成有序序列。
/**
* 快速排序 (splice)
* @param arr
* @returns
*/
function quickSort(arr:number[]){
const lent =arr.length
if(len === 0 )return arr
const midIndex = Math.floor( len / 2)
const midValue = arr.splice(midIndex,1)[0]
//splice()方法会返回被删除的元素组成的数组,因此我们使用[0]获取这个数组中的第一个(也是唯一一个)元素,即中间值,将其赋给midValue
const left :number[] = []
const rigth :number[]=[]
// splice 会修改原数组,用arr.length
for(let i =0 ;i<arr.length; i++){
const n = arr[i]
if(n<midValue){
left.push(n)
}else{
right.push(n)
}
}
return quickSort(left).concat([midValue],quickSort(right))
}
/**
* 快速排序 (slice)
* @param arr
* @returns
*/
function quickSort2(arr:number[]):number[]{
const len = arr.length
if(len === 0 )return arr
const midIndex = Math.floor(len / 2)
const midValue = arr.slice(midIndex,midIndex + 1)[0]
const left:number[] = []
const right :number[] = []
for(let i = 0 ;i < arr.length ; i++){
if(n<midValue){
left.push(n)
}else{
right.push(n)
}
}
return quickSort(left).concat([midValue],quickSort(right))
}
const testArr= [3,2,4,34,5,54]
console.info('quickSort2',quickSort2(testArr))
⭐️⭐️⭐️
插入排序是一种最简单直观的排序算法,工作原理是通过构建有序序列,对于未排序数据,在已经排序序列中从后向前扫描
,找到相应位置并插入。
function insertionSort(arr) { // 定义插入排序函数
for (let i = 1; i < arr.length; i++) { // 遍历数组中未排序部分
const temp = arr[i]; // 缓存当前元素的值
let j = i; // 初始化指针j为当前位置i
while (j > 0) { // 在已排序部分从后往前查找
if (arr[j - 1] > temp) { // 如果前一个元素大于当前元素
arr[j] = arr[j - 1]; // 将前一个元素往后移动一位
} else { // 如果前一个元素小于或等于当前元素,则跳出循环
break;
}
j--; // 将指针向前移动一位
}
arr[j] = temp; // 将当前元素插入到正确的位置
}
}
const arr = [4, 3, 6, 2, 5, 7, 9, 8, 1]
insertionSort(arr)
console.log(arr) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
⭐️⭐️⭐️⭐️
分两步:
function mergeSort(arr){
if(arr.length===1) return arr
let mid = Math.floor(arr.length / 2)
let left = arr.slice(0,mid)
let right = ar.slice(mid)
return merge(mergeSort(left),mergeSort(right))
}
function merge(a,b){
let res = []
while(a.length && b.length){
if(a[0] < b[0]){
res.push(a[0])
a.shift()
}else {
res.push(b[0])
b.shift()
}
}
if(a.lenght){
res = res.concat(a)
}else{
res = res.concat(b)
}
return res
}
// 功能测试
const arr = [4, 3, 6, 2, 5, 7, 9, 8, 1]
console.log(mergeSort(arr)) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
⭐️⭐️
基本思想:
// 接收一个数组作为参数
function selectionSort(arr){
// 外层循环,遍历整个数组
for(let i = 0 ;i<arr.length -1 ;i++){
// 假设当前位置就是最小值
let indexMin = i;
// 内层循环,与当前位置后面所有元素比较大小,找到最小值的下标
for(let j = i ;j<arr.length ;j++){
if(arr[j]< arr[indexMin]){
indexMin = j
}
}
// 如果最小值不是当前位置,就将它和当前位置交换
if(indexMin != i){
const temp = arr[i]
arr[i] = arr[indexMin]
arr[indexMin] = temp
}
}
}
// 功能测试
const arr = [4, 3, 6, 2, 5, 7, 9, 8, 1]
selectionSort(arr)
console.log(arr) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
简单来说,该函数实现了选择排序算法,其基本思想是从未排序的数据中选择最小值,然后将其放到已排序数据的末尾。具体实现时,内层循环用于找到最小值的下标,外层循环则用于遍历整个数组并交换元素。
⭐️⭐️⭐️
function sequentialSearch(arr, target) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) {
return i;
}
}
return -1;
};
const arr = [4, 3, 6, 2, 5, 7, 9, 8, 1]
console.log(sequentialSearch(arr, 8)) // 7
⭐️⭐️⭐️⭐️⭐️
二分搜索,又叫折半搜索,一种有序数组
中查找特定元素的搜索算法。
所以是用二分查找
的前提数组是有序的
。
/**
* 凡是有序,就是二分
* 凡是二分,时间复杂度必包含o(logn)
*递归代码思路清晰,非递归性能更好
*
*/
/ **
* 二分查找(循环)
* @param arr
* @param target
* @returns
* /
function binarySearch(arr:number[], target : number):number{
const len = arr.length
if(len === 0)return -1
let l = 0
let r = len -1
while(l <= r){
const midIndex = Math.floor(len/2)
const midValue = arr[midIndex]
if(target < midValue){
r = midIndex -1
}else if(target> midValue){
l = midIndex + 1
}else{
return midIndex;
}
}
return -1
}
/**
* 二分查找(递归)
* @param arr
* @param target
*/
function binarySearch02(arr: number[], target: number, startIndex?: number, endIndex?: number): number {
const len = arr.length
if(length === 0) return -1
if(startIndex === null) startIndex = 0
if(endIndex ===null) endIndex = length -1
if(startIndex > endIndex) return -1
// 中间位置
const midIndex = Math.floor((startIndex + endIndex) / 2)
const midValue = arr[midIndex]
if(target < midValue){
return binarySearch02(arr,target,startIndex,midIndex -1)
}else if(target >midValue){
return binarySearch02(arr,target,midIndex + 1,endIndex)
}else
{
return midIndex
}
}
// 功能测试
// const testArr = [-20, -10, 30];
// const testTarget = 30;
// console.info(binarySearch02(testArr, testTarget));
// 性能测试
// const testArr = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
// const testTarget = 30;
// console.time('binarySearch01')
// for (let i = 0; i < 100 * 10000; i++) {
// binarySearch01(testArr, testTarget)
// }
// console.timeEnd('binarySearch01')
// console.time('binarySearch02')
// for (let i = 0; i < 100 * 10000; i++) {
// binarySearch02(testArr, testTarget)
// }
// console.timeEnd('binarySearch02')
分而治之
是算法设计中的一种方法。它将一个问题分成多个和原问题相似的小问题,递归解决小问题,再将结果合并以解决原来的问题。
分:把数组从中间一分为二
解:递归的对两个子数组进行归并排序
合:合并有序子数组
分:选基准,按基准把数组分成两个子数组
解:递归的对两个子数组进行快速排序
合:合并两个子数组
动态规划
是算法设计中的一种方法。它将一个问题分解成相互重叠的子问题,通过反复求解子问题,来解决原来的问题。
场景一:斐波那契数列
动态规划和分而治之区别
leetcode
贪心算法是算法设计中的一种方法。期盼通过每个阶段的局部最优选择,从而达到全局最优,但是结果并不一定是最优的。常见的反面例子如:零钱兑换问题。
leetcode
回溯算法是算法设计中的一种方法。回溯算法是一种渐进式寻找并构建问题解决方式的策略。回溯算法会先从一个可能的动作开始解决问题,如果不行,就回溯并选择另一个动作,直到将问题解决。
有很多路
这些路,有思路,也有出路
通常需要递归来模拟所有的路
全排列
子集
提示:这里对文章进行总结:
您的点赞和评论是我持续更新的动力,感谢关注。❤️