常见算法

111 阅读4分钟

冒泡排序

  • 描述: 循环遍历, 每次遍历后都会确定一个数据的排序位置, 只需要按照规则遍历, 就可以将所有的数据排序
  • 理解: 有两层循环, 第一层, 确保我们能够遍历每一个数据, 第二层, 确保完成循环后有一个数据被正确排序

function popSort (arr) {
    // 第一层遍历的次数和数组的长度相同即可
    for (let i = 0; i < arr.length - 1; i++) { // 最后一个数不用排
        for (let j = 0; j < arr.length - 1 - i; j++ ) // -i 的原因是每次排序后都可以确定最后一个数据是正确排序的, 且是最大值, 因为每循环一次, 对应位置的较大值都会往后移动
           if (arr[j] > arr[j+1]) {
               // 真正排序的地方
               const temp = arr[j]
               arr[j] = arr[j+1]
               arr[j+1] = temp
           }
    }
    return arr
}

const arr = [23, 12, 24, 21, 13, 12]

console.log(popSort(arr))

插入排序

  • 描述:假定一部分数据是排好序的, 一般是第一个数据, 然后从后面的数据中插入这些排好序的部分
  • 理解:分成两块, 一部分是排好序的部分, 另一部分是没排好序的, 将没排好序的数据,依次插入排好序的部分
function insertSort (arr) {
    // 第一层循环, 确保每一个元素都能够被遍历
    for (let i = 1; i < arr.length; i++) {
        // 记录未排序部分第一项的值
        const temp = arr[i]
        // 找到排好序的最后一个元素
        let j = i - 1  // 真正排序的索引
        // 用未排序部分第一项的值去遍历排好序的部分, 找到这个值正确的位置
        while (j >= 0 && arr[j] > temp) {
            // 将排好的部分依次向后移动
            arr[j+1] = arr[j]
            j--
        }
        arr[j+1] = temp
    }
   return arr
}
const arr = [23, 12, 24, 21, 13, 12]
console.log(insertSort(arr))

选择排序

  • 描述:选择一个数据作为最小值,一般是第一项, 遍历,如果有比他小的就替换
  • 理解: 有两层循环, 第一层循环, 确定哪个值是最小值, 第二层循环将真的最小值放到这个设定的最小值的位置
function choseSort (arr) {
    // 以下标的方式记录
    let minValueIndex = 0
    // 最后一个不用排序, 前面的排好序后, 最后一个就有序
    for (let i = 0; i < arr.length - 1; i++) {
        minValueIndex = i
        for (let j = i + 1; j < arr.length; j++) {
            // 真正排序的地方, 可以确定最小值的位置
            if (arr[minValueIndex] > arr[j]) {
                minValueIndex = j
            }
        }
        const temp = arr[i]
        arr[i] = arr[minValueIndex]
        arr[minValueIndex] = temp
    }
    return arr
}
const arr = [23, 12, 24, 21, 13, 12]
console.log(choseSort(arr))

快速排序

  • 描述:分治思想, 递归调用
  • 理解, 将数据从中间拆分, 分成左右两个部分, 将大于中间的值放右边,小于中间值的放左边, 左右合并起来
function quickSort (arr) {
    if (arr.length <= 1) {
        reurn arr
    }
    
    const mid = Math.floor(arr.length / 2)
    // 从这个中间位置拆分
    const pivot = arr.splice(mid, 1)[0]
    const left = []
    const right = []
    
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] < pivot) {
            left.push(arr[i])
        } else {
            right.push(arr[i])
        }
    }
    
    return quickSort(left).concat([pivot],  quickSort(right))  
}
const arr = [23, 12, 24, 21, 13, 12]
console.log(quickSort(arr))

归并排序

  • 描述:分治思想, 排序一个数组,先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。
  • 理解: 根据中心点进行拆分, 拆成不能再拆的最小单元, 将左右两部分进行排序, 这样每个层级合并时都是有序的

image.png

function mergeSort (arr) {
    if (arr.length < 2) return arr
    // 分
    const mid = Math.floor(arr.length / 2)
    const left = arr.slice(0, mid)
    const right = arr.slice(mid)
    // 合, 将分开的两部分合并
    let merge = function (leftArr, rightArr){
        let result = []
        while(leftArr.length && rightArr.length) {
            // 为什么比较第 0 个, 最小单元都是排好序的,只需按次序确定左右两部分之间的大小关系
            result.push(leftArr[0] <= rightArr[0] ? leftArr.shift() : rightArr.shift())
        }
        return result.concat(leftArr).concat(rightArr)
    }
    return merge(mergeSort(left), mergeSort(right))
}

二分搜索

  • 描述: 前提条件是被搜索的数据是已经排好序的,每次都从中间分, 判断中间值与搜索的目标值大小情况, 然后再进行拆分
  • 理解: 每次查找都猜中间的值, 如果大于目标值, 那么就以这个中间值的下标为尾下标, 再进行拆分, 所以记录下标很重要
const arr = [1, 2, 3, 4, 5, 6, 7, 8]
const target = 6

function search (arr, target) {
    // 记录下标
    let start = 0
    let end = arr.length -1
    
    while (start <= end) {
        // 找到中间位置
        const mid = Math.floor((start + end) >> 1)
        // 拿到这个位置的值, 猜测这个值就是正确的值
        const guess = arr[mid]
        
        if (guess === target) {
            // 返回这个值的位置坐标
            return mid
        }
        
        if (guess < target) {
            start = mid + 1
        }
        
        if (guess > target) {
            end = mid
        }
    }
    return -1 // 搜索不到的情况
}

console.log(search(arr, target))