用js实现常见的排序算法和斐波那契数列

一、排序算法:

1. 冒泡排序

  • 空间复杂度: O(1)
  • 时间复杂度: O(n^2)

思路:外层循环管趟数,内层循环管每一趟交换次数。

function bubbleSort(arr) {
    for (i = 0; i < arr.length - 1; i++) {
        for (j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
            }
        }
    }
    return arr
}

优化的冒泡排序(记忆化:下一次比较的终点为上一次发生变化的位置)


function BubbleSortBetter(arr) {
    let i = array.length - 1
    while(i > 0) {
        // 定义发生变化位置 即下一次遍历起始位置
        let position = 0;
        for(let j = 0; j < i; j++) {
            if (array[j] > array[j + 1]) {
                // 定义发生变化的位置 确定下一次变化的重点位置
                //后面未发生变化的位置上的元素一定比前面还未排序的元素大
                position = j;
                [array[j], array[j+1]] = [array[j+1], array[j]];
            }
        }
        // 定义下一次遍历的终点,避免重复遍历
        i = position;
    }
    return array;
}

2. 插入排序

  • 空间复杂度: O(1)
  • 时间复杂度: O(n^2)

思路:将左侧序列看为一个有序的序列,每次都将要插入的数字插入到合适的位置。插入时,从有序序列的最右侧开始比较,若比较的数较大,比较的数后移一位,要插入的数继续向前比较知道找到要插入的位置。(与冒泡的思路类似,冒泡是向后找位置,插入是向前找位置)


function insertSort(arr) {
    for (let i = 1; i < arr.length; i++) {
        let target = i
        for (let j = i - 1; j >= 0; j--) {
            if (arr[target] < arr[j]) {
                [arr[target], arr[j]] = [arr[j], arr[target]]
                target = j
            } else {
                break
            }
        }
    }
    return arr
}

3. 选择排序

  • 空间复杂度:O(1)
  • 时间复杂度:O(n^2)

思路:每次循环都找到一个最小的值放在左侧的有序序列中,每找到一个下次循环的序列长度就向后减一即可

function selectSort(arr) {
    for (i = 0; i < arr.length; i++) {
        let minIndex = i;
        for (j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        [arr[minIndex], arr[i]] = [arr[i], arr[minIndex]]
    }
    return arr
}

4. 快速排序

  • 空间复杂度:O(logn)
  • 时间复杂度:O(n^2)

思路:每一趟排序将要排序的数据分割成独立的两部分,同时找到middle元素的最终位置,然后通过递归排序分割出的部分,完成排序.

function quickSort(arr) {
    //临界值判断
    if (arr.length < 2) {
        return arr
    }
    let middle = arr[0];
    let left = [];
    let right = [];
    for (let i = 1; i < arr.length; i++) {
        if (arr[i] < middle) {
            left.push(arr[i])
        } else {
            right.push(arr[i])
        }
    }
    return quickSort(left).concat([middle], quickSort(right))
}
//这种快排时间复杂度较高,但比较容易理解和记忆

优化的快速排序(首尾双指针)

  • 空间复杂度:O(1)
  • 时间复杂度:O(nlogn)

使用索引记录的方法,不需要额外空间

(1)记录一个索引l从数组最左侧开始,记录一个索引r从数组最右侧开始

(2)先找到右侧第一个小于target的值array[r],并将其赋值到array[l],l+1

(3)再找到左侧第一个大于target的值array[l],并将其赋值到array[r], r-1

(4)循环2和3步骤,直到l=r时,左侧的值全部小于target,右侧的值全部小于target,l即为target最终位置

(5)递归左边序列和右边序列,完成排序。

 function QuickSort2(array, start, end) {
    // 当要排序段为0
   if (end - start < 1) {
       return null
   }
   // 选择中介值 即第一个值
   const target = array[start]
       // 左、右索引
   let l = start
   let r = end
   while (l < r) {
       // 先从后向前找小于中介值的结点
       while (l < r && array[r] >= target) {
           r--
       }
       // 交换位置
       if (l < r) {
           array[l++] = array[r]
       }
       // 从前向后找大于中介值的结点
       while (l < r && array[l] < target) {
           l++
       }
       // 交换位置
       if (l < r) {
           array[r--] = array[l]
       }
   }
   // 当结束时 l = r 此时此处即为中介值位置
   array[l] = target
       // 之后递归排序左边和右边数组
   QuickSort2(array, start, l - 1)
   QuickSort2(array, l + 1, end)
   return array
}

5. 归并排序

  • 时间复杂度: O(nlogn)
  • 空间复杂度: O(n)

思路:归并排序一般分为两步:分离和合并(分而治之的思想)

分离:将数组从中点进行分割,分为左、右两个数组,之后递归分割直到长度小于2
合并:合并时创建一个临时数组,比较两数组(此时通过前面的递归数组均已有序)第一个元素的大小,将较小的插入临时数组,之后直到一方为空,将另一个不为空的数组全部插入临时数组。


function MergeSort(array) {
    if (array.length < 2) {
        return array
    }
    // 获取中点索引
    const mid = Math.floor(array.length / 2)
    // 分为前半段和后半段
    let front = array.slice(0, mid)
    let end = array.slice(mid)
    return merge(MergeSort(front), MergeSort(end))
}
//归并函数
function merge(front, end){
    // 创建一个临时存储数组
    const temp = []
    while(front.length && end.length) {
        // 比较两数组第一个元素,将较小的元素取出并加入到临时数组
        if (front[0] < end[0]) {
            temp.push(front.shift())
        } else {
            temp.push(end.shift())
        }
    }
    // 若左右数组有一个为空,那么此时另一个数组一定大于temp中的所有元素,直接将所有元素加入temp
    while(front.length) {
        temp.push(front.shift())
    }
    while(end.length) {
        temp.push(end.shift())
    }

    return temp
}

二、斐波那契数列

1. 斐波那契数列 (递归)


function Fibonacci(n) {
    if (n < 2) {
        return n;
    } else {
        return Fibonacci(n - 1) + Fibonacci(n - 2);
    }
}

2. 斐波那契数列(记忆化)


function Fibonacci(n, memory) {
    if (!memory) {
        memory = [];
    }
    if (n <= 2) {
        return n;
    }
    if (!memory[n]) {
        memory[n] = Fibonacci(n - 1, memory) + Fibonacci(n - 2, memory);
    }
    return memory[n];
}

3. 斐波那契数列(动态规划)


function Fibonacci(n) {
    let arr = [1, 1];
    for (let i = 2; i <= n; i++) {
        arr[n] = arr[n - 1] + arr[n - 2];
    }
    return arr[n];
}

(推荐,简单好记)

参考个人博客文档:极力推荐 数据结构和算法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值