前端算法入门三:5大排序算法&2大搜索&4大算法思想

系列文章目录

这是前端算法入门第三篇,介绍数据结构与算法中的排序算法,搜索算法,以及常见的算法面试题,总结常见的解题思路,让你事半功倍。


文章主要包含内容:

  • 排序算法
  1. 冒泡排序
  2. 快速排序
  3. 插入排序
  4. 归并排序
  5. 选择排序
  • 搜索算法

  • 顺序算法

  • 二分搜索

  • 算法思想

  • 分而治之

  • 动态规划

  • 贪心算法

  • 回溯算法

文章目录

  • 系列文章目录
  • 一、基础排序算法
    • 1️⃣ 冒泡排序
    • 2️⃣快速排序
    • 3️⃣ 插入排序
    • 4️⃣ 归并排序
    • 5️⃣ 选择排序
  • 二、 搜索
    • 1️⃣ 顺序搜索
    • 2️⃣ 二分搜索
  • 三、算法思想
    • 1️⃣ 分而治之
    • :fire: leetcode
    • 2️⃣ 动态规划
    • 3️⃣ 贪心算法
    • 4️⃣ 回溯算法
    • 什么问题适合用回溯算法解决?
  • 总结



一、基础排序算法

1️⃣ 冒泡排序

⭐️⭐️⭐️⭐️⭐️
原理:

  1. 比较相邻的元素,如果第一个比第二个大,就交换,如果不是相等就跳过对比下一个元素,这样依次循环下去,直到所有元素都比较完成才结束。
  2. 针对所有的元素重复以上的步骤,除去最后一个。
  3. 持续每次对越来越少的元素重复以上的步骤。
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
			}
		}
    }
}

2️⃣快速排序

⭐️⭐️⭐️⭐️⭐️
通过一趟排序将要需要排序的数据分割成独立两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按这方法对这两部分数据分别进行快速排序,整个排序可以进行递归进行,达到整个数据变成有序序列。


/**
 * 快速排序 (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))

3️⃣ 插入排序

⭐️⭐️⭐️
插入排序是一种最简单直观的排序算法,工作原理是通过构建有序序列,对于未排序数据,在已经排序序列中从后向前扫描,找到相应位置并插入。

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]

4️⃣ 归并排序

⭐️⭐️⭐️⭐️
分两步:

  • 分割:将待排序的线性表不断切分成诺干个子表,直到每个子表只包含一个元素,可以认为包含一个元素的子表是有序表
  • 归并:将子表两两合并,合并一次,会产生一个新的更长的有序表,重复这一步,直到最后只剩下一个子表,这个子表就是排好序的线性表
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]

5️⃣ 选择排序

⭐️⭐️
基本思想:

  • 首先在未排序的数列中找到最小 或 最大元素,然后将其存放到数列起始位置
  • 然后,再从剩余未排序的元素中继续寻找最小 或 最大,放在已经排序的末尾
  • 以此类推
// 接收一个数组作为参数
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]

简单来说,该函数实现了选择排序算法,其基本思想是从未排序的数据中选择最小值,然后将其放到已排序数据的末尾。具体实现时,内层循环用于找到最小值的下标,外层循环则用于遍历整个数组并交换元素。

二、 搜索

1️⃣ 顺序搜索

⭐️⭐️⭐️

 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 

2️⃣ 二分搜索

⭐️⭐️⭐️⭐️⭐️
二分搜索,又叫折半搜索,一种有序数组中查找特定元素的搜索算法。
所以是用二分查找的前提数组是有序的

 /**
 * 凡是有序,就是二分
 * 凡是二分,时间复杂度必包含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')
 

三、算法思想

1️⃣ 分而治之

分而治之是算法设计中的一种方法。它将一个问题分成多个和原问题相似的小问题,递归解决小问题,再将结果合并以解决原来的问题。

  • 场景一:归并排序

分:把数组从中间一分为二
解:递归的对两个子数组进行归并排序
合:合并有序子数组

  • 场景二:快速排序

分:选基准,按基准把数组分成两个子数组
解:递归的对两个子数组进行快速排序
合:合并两个子数组

leetcode

  • 猜数字大小
  • 翻转二叉树
  • 添加链接描述
  • 对称二叉树

2️⃣ 动态规划

动态规划是算法设计中的一种方法。它将一个问题分解成相互重叠的子问题,通过反复求解子问题,来解决原来的问题。

场景一:斐波那契数列

  • 定义子问题:F(n) = F(n - 1) + F(n - 2)
  • 反复执行:从2循环到n,执行上述公式

动态规划和分而治之区别

  • 区别在于子问题是否独立
  • 动态规划的子问题是重叠的
  • 分而治之的子问题是独立的

leetcode

  • 爬楼梯
  • 打家劫舍

3️⃣ 贪心算法

贪心算法是算法设计中的一种方法。期盼通过每个阶段的局部最优选择,从而达到全局最优,但是结果并不一定是最优的。常见的反面例子如:零钱兑换问题。

leetcode

  • 分发饼干
  • 买卖股票的最佳时机

4️⃣ 回溯算法

回溯算法是算法设计中的一种方法。回溯算法是一种渐进式寻找并构建问题解决方式的策略。回溯算法会先从一个可能的动作开始解决问题,如果不行,就回溯并选择另一个动作,直到将问题解决。

什么问题适合用回溯算法解决?

  • 有很多路

  • 这些路,有思路,也有出路

  • 通常需要递归来模拟所有的路

  • 全排列

  • 子集


总结

提示:这里对文章进行总结:
您的点赞和评论是我持续更新的动力,感谢关注。❤️

你可能感兴趣的:(面试算法练习,算法,排序算法,前端)