• 作者:陈大鱼头

  • github:https://github.com/KRISACHAN

算法是什么?

算法(Algorithm) 已经是一个老生常谈的概念了,最早来自于数学领域。

算法(Algorithm) 代表着用系统的方法描述解决问题的策略机制,可以通过一定规范的 输入,在有限时间内获得所需要的 输出

如下图示便是算法:

算法的好坏

一个算法的好坏是通过 时间复杂度 与 空间复杂度 来衡量的。

举个?:

鱼头跟方勤一起去同一家公司面试,面试官让他们实现同一个功能,巴拉巴拉大半天,两个人终于交付了代码。

面试官一运行,发现:

方勤的代码运行一次要花 100ms ,占用内存 5MB 。

而鱼头的代码运行一次要花 100s ,占用内存 500MB 。

好了,鱼头面试又失败了!

以上所花的 时间 与 占用内存 便是衡量一个 算法好坏的标准。

简单来说,时间复杂度 就是执行算法的 时间成本 ,空间复杂度 则是执行算法的 空间成本 。

复杂度

时间复杂度 与 空间复杂度 都是用 “大O” 来表示,写作 O(*)。有一点值得注意的是,我们谈论复杂度,一般谈论的都是时间复杂度。

常见时间复杂度的 “大O表示法” 描述有以下几种:

时间复杂度 非正式术语
O(1) 常数阶
O(n) 线性阶
O(n2) 平方阶
O(log n) 对数阶
O(n log n) 线性对数阶
O(n3) 立方阶
O(2n) 指数阶

一个算法在N规模下所消耗的时间消耗从大到小如下:

O(1) < O(log n) < O(n) < O(n log n) < O(n2) < O(n3) < O(2n)

上面括号的数据是啥意思?别问,问就让你回去看数学书。

以下便为不同时间复杂度的资源消耗增长图示:

常见概念:

  1. 最好时间复杂度: 在最理想情况下执行代码的时间复杂度,它花的时间最短;

  2. 最坏时间复杂度: 最糟糕情况下执行代码的时间复杂度,它花的时间最长;

  3. 平均时间复杂度: 执行代码时间的平均水平,这个值就是概率论中的加权平均值,也叫期望值。

常见的排序算法

在生活中,我们离不开排序。例如体育课上按身高排的队;又如考试过后按成绩排的名次。

在编程中也是如此,例如当开发一个学生管理系统,需要按照学好从小到大进行排序;开发一个平台,需要把同类商品按价格从高到低排序。(当然,一般前端不负责处理业务逻辑。)

有此可见,排序无处不在。

排序看似简单,但是背后却隐藏了多种多样的算法与思想。

概述

根据时间复杂度的不同,常见的算法可以分为3大类。

1.O(n²) 的排序算法

2.O(n log n) 的排序算法

3.线性的排序算法

各种排序的具体信息

图片解释:

冒泡排序(Bubble Sort)

冒泡排序(Bubble Sort) 是一种基础的 交换排序

冒泡排序之所以叫冒泡排序,是因为它每一种元素都像小气泡一样根据自身大小一点一点往数组的一侧移动。

算法步骤如下:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个;

  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数;

  3. 针对所有的元素重复以上的步骤,除了最后一个;

  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

图示如下:

具体实现如下:

const bubbleSort = arr => {const len = arr.length - 1for (let i = 0; i < len; ++i) { /* 外循环为排序趟数,len个数进行len-1趟 */for (let j = 0; j < len - i; ++j) { /* 内循环为每趟比较的次数,第i趟比较len-i次 */if (arr[j] > arr[j + 1]) { /* 相邻元素比较,若逆序则交换(升序为左大于右,逆序反之) */[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]}}}return arr
}

选择排序(Selection Sort)

选择排序(Selection sort) 是一种简单直观的排序算法。

选择排序的主要优点与数据移动有关。

如果某个元素位于正确的最终位置上,则它不会被移动。

选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对 n 个元素的表进行排序总共进行至多 n - 1 次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。

选择排序的算法步骤如下:

  1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置;

  2. 然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾;

  3. 以此类推,直到所有元素均排序完毕。

图示如下:

具体实现如下:

const selectionSort = arr => {const len = arr.lengthlet minfor (let i = 0; i < len - 1; ++i) {min = i /* 初始化未排序序列中最小数据数组下标 */for (let j = i + 1; j < len; ++j) { /* 访问未排序的元素 */if (arr[j] < arr[min]) { /* 找到目前最小值 */min = j /* 记录最小值 */}}[arr[i], arr[min]] = [arr[min], arr[i]] /* 交换位置 */}return arr
}

插入排序(Insertion Sort)

插入排序(Insertion sort) 是一种简单直观的排序算法。

它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

插入排序的算法步骤如下:

  1. 从第一个元素开始,该元素可以认为已经被排序;

  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;

  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;

  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;

  5. 将新元素插入到该位置后;

  6. 重复步骤2~5。

图示如下:

具体实现如下:

const insertionSort = arr => {const len = arr.lengthlet j, tempfor (let i = 0; i < len; ++i) {j = i - 1temp = arr[i]while (j >= 0 && arr[j] > temp) {arr[j + 1] = arr[j]j--}arr[j + 1] = temp}return arr
}

希尔排序(Shell Sort)

希尔排序,也称 递减增量排序算法,是 插入排序 的一种更高效的改进版本。希尔排序是非稳定排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  1. 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到 线性排序 的效率;

  2. 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。

步长的选择是希尔排序的重要部分。

只要最终步长为1任何步长序列都可以工作。

算法最开始以一定的步长进行排序。

然后会继续以一定步长进行排序,最终算法以步长为1进行排序。

当步长为1时,算法变为普通插入排序,这就保证了数据一定会被排序。

插入排序的算法步骤如下:

  1. 定义一个用来分割的步长;

  2. 按步长的长度K,对数组进行K趟排序;

  3. 不断重复上述步骤。

图示如下:

具体实现如下:

const shellSort = arr => {let len = arr.lengthfor (let g = 0, gLen = gaps.length; g < gaps.length; ++g) {for (let i = gaps[g]; i < len; ++i) {let temp = arr[i], jfor (j = i; j >= gaps[g] && arr[j - gaps[g]] > arr[i]; j -= gaps[g]) {arr[j] = arr[j - gaps[g]]}arr[j] = temp}}return arr
}

快速排序(Quick Sort)

快速排序(Quicksort),又称 划分交换排序(partition-exchange sort) 。

快速排序(Quicksort) 在平均状况下,排序 n 个项目要 O(n log n) 次比较。在最坏状况下则需要 O(n2) 次比较,但这种状况并不常见。事实上,快速排序 O(n log n) 通常明显比其他算法更快,因为它的 内部循环(inner loop) 可以在大部分的架构上很有效率地达成。

快速排序使用 分治法(Divide and conquer) 策略来把一个序列分为较小和较大的2个子序列,然后递归地排序两个子序列。

快速排序的算法步骤如下:

  1. 挑选基准值:从数列中挑出一个元素,称为 “基准”(pivot) ;

  2. 分割:重新排序序列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成;

  3. 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。

递归到最底部的判断条件是序列的大小是零或一,此时该数列显然已经有序。

选取基准值有数种具体方法,此选取方法对排序的时间性能有决定性影响。

图示如下:

具体实现如下:

const quickSort = arr => {const len = arr.lengthif (len < 2) {return arr}const pivot = arr[0]const left = []const right = []for (let i = 1; i < len; ++i) {if (arr[i] >= pivot) {right.push(arr[i])}if (arr[i] < pivot) {left.push(arr[i])}}return [...quickSort(left), pivot, ...quickSort(right)]
}

除了常规的快速排序之外,还有一个快速排序的优化版本,叫 三路快排

当面对一个有大量重复的数据的序列时,选取 pivot 的快速排序有可能会退化成一个 O(n²) 的算法

基于这种情况,就有了 三路快排(3 Ways Quick Sort)

三路快排就是将序列分为三部分:小于pivot,等于 pivot 和大于 pivot,之后递归的对小于v和大于v部分进行排序。

具体实现如下:

const quickSort = arr => {const len = arr.lengthif (len < 2) {return arr}let left = []let center = []let right = []let pivot = arr[0]for (let i = 0; i < len; ++i) {      if (arr[i] < pivot) {left.push(arr[i])} else if (arr[i] === pivot) {center.push(arr[i])} else {right.push(arr[i])}}return [...quickSort(left), ...center, ...quickSort(right)]
}

辛苦你们看到这里了,如果累了可以收藏起来下次再看,不过收藏起来就没下次了吧?所以继续看完吧!加油鸭~

并归排序(Merge Sort)

归并排序(Merge sort) ,是创建在归并操作上的一种有效的排序算法,时间复杂度为 O(n log n) 。1945年由约翰·冯·诺伊曼首次提出。该算法是采用 分治法(Divide and Conquer) 的一个非常典型的应用,且各层分治递归可以同时进行。

其实说白了就是将两个已经排序的序列合并成一个序列的操作。

并归排序有两种实现方式

第一种是 自上而下的递归 ,算法步骤如下:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

  4. 重复步骤3直到某一指针到达序列尾;

  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

具体实现如下:

const merge = (left, right) => {let resArr = []while (left.length && right.length) {if (left[0] < right[0]) {resArr.push(left.shift())} else {resArr.push(right.shift())}}return resArr.concat(left, right)
}const mergeSort = arr => {if (arr.length <= 1) {return arr}let middle = Math.floor(arr.length / 2)let left = arr.slice(0, middle)let right = arr.slice(middle)return merge(mergeSort(left), mergeSort(right))
}

第二种是 自下而上的迭代 ,由于 分治法 的具体算法基本都能用 递归 跟 迭代 来实现,所有才有这种写法,其主要步骤如下:

  1. 将序列每相邻两个数字进行 归并操作 ,形成 ceil(n / 2) 个序列,排序后每个序列包含两/一个元素;

  2. 若此时序列数不是1个则将上述序列再次归并,形成 ceil(n / 4)  个序列,每个序列包含四/三个元素;

  3. 重复步骤2,直到所有元素排序完毕,即序列数为1。

具体实现如下:

const merge = (arr, startLeft, stopLeft, startRight, stopRight) => {/* 建立左右子序列 */let rightArr = new Array(stopRight - startRight + 1)let leftArr = new Array(stopLeft - startLeft + 1)/* 给左右序列排序 */let k = startRightfor (let i = 0, len = rightArr.length; i < len - 1; ++i) {rightArr[i] = arr[k]++k}k = startLeftfor (let i = 0, len = leftArr.length; i < len - 1; ++i) {leftArr[i] = arr[k]++k}//设置哨兵值,当左子列或右子列读取到最后一位时,即Infinity,可以让另一个剩下的列中的值直接插入到数组中rightArr[rightArr.length - 1] = InfinityleftArr[leftArr.length - 1] = Infinitylet m = 0let n = 0// 比较左子列和右子列第一个值的大小,小的先填入数组,接着再进行比较for (let c = startLeft; c < stopRight; ++c) {if (leftArr[m] <= rightArr[n]) {arr[c] = leftArr[m]m++} else {arr[c] = rightArr[n]n++}}
}
const mergeSort = arr => {if (arr.length <= 1) {return arr}//设置子序列的大小let step = 1let leftlet rightwhile (step < arr.length) {left = 0right = stepwhile (right + step <= arr.length) {merge(arr, left, left + step, right, right + step)left = right + stepright = left + step}if (right < arr.length) {merge(arr, left, left + step, right, arr.length)}step *= 2}return arr
}

鱼头注:迭代比起递归还是安全很多,太深的递归容易导致堆栈溢出。

图示如下:

堆排序(Heap Sort)

堆排序(Heapsort) 是指利用 二叉堆 这种数据结构所设计的一种排序算法。堆是一个近似 完全二叉树 的结构,并同时满足 堆积的性质 :即子节点的键值或索引总是小于(或者大于)它的父节点。

二叉堆是什么?

二叉堆分以下两个类型:

鱼头注:以下图片来自于:漫画:什么是二叉堆?

1.最大堆:最大堆任何一个父节点的值,都大于等于它左右孩子节点的值。

2.最小堆:最小堆任何一个父节点的值,都小于等于它左右孩子节点的值。

堆排序的算法步骤如下:

  1. 把无序数列构建成二叉堆;

  2. 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。

具体实现如下:

/* 堆下沉调整 */
const adjustHeap = (arr, parentIndex, length) => {let temp = arr[parentIndex] /* temp保存父节点值,用于最后赋值 */let childIndex = 2 * parentIndex + 1 /* 保存子节点位置 */while (childIndex < length) {/* 如果有右子节点,且右子节点大于左子节点的值,则定位到右子节点 */if (childIndex + 1 < length && arr[childIndex + 1] > arr[childIndex]) {childIndex++}/* 如果父节点小于任何一个子节点的值,直接退出循环 */if (temp >= arr[childIndex]) {break;}/* 无序交换,单向赋值即可 */arr[parentIndex] = arr[childIndex]parentIndex = childIndexchildIndex = 2 * childIndex + 1}arr[parentIndex] = temp
}
const heapSort = arr => {/* 把无序数列构建成最大堆 */for (let i = Math.floor(arr.length / 2); i >= 0; --i) {adjustHeap(arr, i, arr.length - 1)}for (let i = arr.length - 1; i > 0; --i) {/* 交换最后一个元素与第一个元素 */[arr[i], arr[0]] = [arr[0], arr[i]]/* 调整最大堆 */adjustHeap(arr, 0, i)}return arr
}

图示如下:

图片来源于:CSDN

计数排序(Counting Sort)

计数排序(Counting sort) 是一种稳定的线性时间排序算法。该算法于1954年由 Harold H. Seward 提出。计数排序使用一个额外的数组来存储输入的元素,计数排序要求输入的数据必须是有确定范围的整数。

当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 O(n + k) 。计数排序不是比较排序,排序的速度快于任何比较排序算法。

计数排序的算法步骤如下:

  1. 找出待排序的数组中最大和最小的元素;

  2. 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项;

  3. 对所有的计数累加(从数组 C 中的第一个元素开始,每一项和前一项相加);

  4. 反向填充目标数组:将每个元素 i 放在新数组的第 C[i] 项,每放一个元素就将 C[i] 减去1。

具体实现如下:

const countSort = arr => {const C = []for (let i = 0, iLen = arr.length; i < iLen; ++i) {const j = arr[i]if (C[j] >= 1) {C[j]++} else {C[j] = 1}}const D = []for (let j = 0, jLen = C.length; j < jLen; ++j) {if (C[j]) {while (C[j] > 0) {D.push(j)C[j]--}}}return D
}

图示如下:

桶排序(Bucket Sort)

桶排序(Bucket Sort) 跟 计数排序(Counting sort) 一样是一种稳定的线性时间排序算法,不过这次需要的辅助不是计数,而是桶。

工作的原理是将数列分到有限数量的桶里。每个桶再个别排序。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间 O(n)

桶排序的算法步骤如下:

  1. 设置一个定量的数组当作空桶子;

  2. 寻访序列,并且把项目一个一个放到对应的桶子去;

  3. 对每个不是空的桶子进行排序;

  4. 从不是空的桶子里把项目再放回原来的序列中。

具体实现如下:

const bucketSort = arr => {let bucketsCount = 10 /* 默认桶的数量 */const max = Math.max(...arr) /* 序列最大数字 */const min = Math.min(...arr) /* 数列最小数字 */const bucketsSize = Math.floor((max - min) / bucketsCount) + 1 /* 桶的深度 */const __buckets = [] /* 空桶 */for (let i = 0, len = arr.length; i < len; ++i) {const index = ~~(arr[i] / bucketsSize) /* 骚操作,取数列中最大或最小的序列 */if (!__buckets[index]) {__buckets[index] = [] /* 创建子桶 */}__buckets[index].push(arr[i])let bLen = __buckets[index].lengthwhile (bLen > 0) { /* 子桶排序 */if (__buckets[index][bLen] < __buckets[index][bLen - 1]) {[__buckets[index][bLen], __buckets[index][bLen - 1]] = [__buckets[index][bLen - 1], __buckets[index][bLen]]}bLen--}}let buckets = [] /* 真实序列 */for (let i = 0, len = __buckets.length; i < len; ++i) {if (__buckets[i]) {buckets.push(...__buckets[i])}}return buckets
}

图示如下:

图片来源于:CSDN

基数排序(Radix Sort)

基数排序(Radix sort) 是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

工作原理是将所有待比较数值(正整数)统一为同样的数字长度,数字较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

基数排序的方式可以采用 LSD(Least significant digital) 或 MSD(Most significant digital) 。

LSD 的排序方式由键值的 最右边(最小位) 开始,而 MSD 则相反,由键值的 最左边(最大位) 开始。

MSD 方式适用于位数多的序列。

LSD 方式适用于位数少的序列。

基数排序 、 桶排序 、 计数排序 原理都差不多,都借助了 “桶” 的概念,但是使用方式有明显的差异,其差异如下:

LSD 图示如下:

LSD 实现如下:

const LSDRadixSort = arr => {let digit = `${max}`.length /* 获取最大值位数 */let start = 1 /* 桶编号 */let buckets = [] /* 空桶 */while (digit > 0) {start *= 10/* 入桶 */for (let i = 0, len = arr.length; i < len; ++i) {const index = (arr[i] % start)if (!buckets[index]) {buckets[index] = []}buckets[index].push(arr[i]) /* 往不同桶里添加数据 */}arr = []/* 出桶 */for(let i = 0; i < buckets.length; i++) {if (buckets[i]) {arr = arr.concat(buckets[i])}}buckets = []digit --}return arr
}

特别的冒泡排序——鸡尾酒排序(Cocktail Sort)

鸡尾酒排序,是 冒泡排序 的一种变形。此算法与 冒泡排序 不同的地方在于从低到高然后从高到低,而 冒泡排序 则仅从低到高去比较序列里的每个元素。它可以得到比 冒泡排序 稍微好一点的性能,原因是 冒泡排序 只从一个方向进行比对(由低到高),每次循环只移动一个项目。

算法步骤如下:

  1. 步骤跟冒泡算法差不多,区别在于从起点到终点遍历完之后会进行一次终点到起点的遍历。

图示如下:

具体实现如下:

const cocktailSort = arr => {let ilet left = 0let right = arr.length - 1while (left < right) {for (i = left; i < right; ++i)if (arr[i] > arr[i + 1]) {[arr[i], arr[i + 1]] = [arr[i + 1], arr[i]]}right--for (i = right; i > left; --i)if (arr[i - 1] > arr[i]) {[arr[i], arr[i - 1]] = [arr[i - 1], arr[i]]}left++}return arr
}

不正经的排序——睡眠排序(Sleep Sort)

这种排序算法是基于定时器来实现的,时间复杂度Mmmmmmmmmmm,空间复杂度Mmmmmmmmm,没有图示,具体实现如下:

const list = [3, 4, 5, 8, 9, 7, 1, 3, 4, 3, 6]const newList = []list.forEach(item => {
const newList = []
list.forEach(item => {setTimeout(function () {newList.push(item)}, item * 100)
})

实用性为0,纯属娱乐,哈哈哈哈~

后记

其实排序算法不只有以上几种,如果全部列出就不是一篇文章可以写得完的,具体的还需要各位自己去学习,探索。

算法的重要性是不言而喻的,虽然我们在日常生活中不一定会用得上怎样深奥的算法,但是或多或少都会接触到有,而且对于一些复杂的业务,算法思维往往能给我们不一样的灵感,更重要一点就是现在如果我们出去面试,算法也是一个绕不开的考点。

本篇内容只是算法这个话题的入门知识点,更多的欢迎大家深入探索,有兴趣的也可以加鱼头的微信 “krisChans95” 一起探讨。

如果你喜欢探讨技术,或者对本文有任何的意见或建议,非常欢迎加鱼头微信好友一起探讨,当然,鱼头也非常希望能跟你一起聊生活,聊爱好,谈天说地。
鱼头的微信号是:krisChans95
也可以扫码添加好友,备注“csdn”就行

前端进阶必备 — 手撕排序算法相关推荐

  1. [ 数据结构 -- 手撕排序算法第四篇 ] 选择排序

    手撕排序算法系列之第四篇:选择排序. 从本篇文章开始,我会介绍并分析常见的几种排序,大致包括直接插入排序,冒泡排序,希尔排序,选择排序,堆排序,快速排序,归并排序等. 大家可以点击此链接阅读其他排序算 ...

  2. [ 数据结构 -- 手撕排序算法第二篇 ] 冒泡排序

    手撕排序算法系列之:冒泡排序. 从本篇文章开始,我会介绍并分析常见的几种排序,大致包括插入排序,冒泡排序,希尔排序,选择排序,堆排序,快速排序,归并排序等. 大家可以点击此链接阅读其他排序算法:排序算 ...

  3. [ 数据结构 -- 手撕排序算法第三篇 ] 希尔排序

    手撕排序算法系列之:希尔排序. 从本篇文章开始,我会介绍并分析常见的几种排序,大致包括插入排序,冒泡排序,希尔排序,选择排序,堆排序,快速排序,归并排序等. 大家可以点击此链接阅读其他排序算法:排序算 ...

  4. 前端进阶必备技能:Vue中如何定制动画效果

    作为前端程序员,前端火起来的短短几年里技术更新迭代特别快,不仅是新的框架繁多,Vue,React,Angular轮番上场,各种工具,插件,库也是琳琅满目,就连基础的JavaScript语法的更新也是年 ...

  5. python手撕分水岭算法

    python手撕分水岭算法 1 分水岭算法实现 主要思路就是: 利用一个优先队列与有序队列(有序队列其实可以不用).优先队列是按像素的灰度值排列的,灰度值低的先被淹. 通过统计像素的附近的点的标记种类 ...

  6. java手撕KMeans算法实现手写数字聚类(失败案例)

    最近几天刚刚接触机器学习,学完K-Means聚类算法.正好又赶上一个课程项目是识别"手写数字",因为KMeans能够实现聚类,因此自然而然地想要通过KMeans来实现. 前排提示: ...

  7. 程序员必备十大排序算法

    程序员必备十大排序算法 常见排序算法 基本概念 插入排序 直接插入排序 排序思路 排序过程 代码实现 算法分析 折半插入排序 排序思路 排序过程 代码实现 算法分析 希尔排序 排序思路 排序过程 代码 ...

  8. 【Java进阶营】各种排序算法的深入分析及java实现

    排序一直以来都是让我很头疼的事,以前上<数据结构>打酱油去了,整个学期下来才勉强能写出个冒泡排序.由于下半年要准备工作了,也知道排序算法的重要性(据说是面试必问的知识点),所以又花了点时间 ...

  9. [数据结构 -- 手撕排序第三篇] 冒泡排序

    目录 1.常见的排序算法 1.1 交换排序基本思想 2.冒泡排序的实现 2.1 基本思想 2.2 单趟排序 2.2.1 单趟排序分析 2.2.2 单趟排序实现代码 3.冒泡排序完整代码实现 3.1 思 ...

最新文章

  1. 爬虫之Xpath详解
  2. 【c语言】求n个整数的和
  3. iphone录屏怎么录声音_ev录屏怎么录制声音 ev录屏声音设置教程
  4. Vue-Router API参考
  5. 新内容、新交互、新增长:视频云为短视频及电商直播行业高效赋能
  6. C# 基础知识 (一).概念与思想篇
  7. 展望2021,Java、Go、.NET,谁主沉浮?
  8. HALCON标定板制作、标准文件输出方法、算子讲解
  9. 猎鹰spacex_我从SpaceX中学到的关于开源的一切
  10. PingCAP联合创始人兼CTO黄东旭:致力于打造全球最好的分布式数据库
  11. 存储系统的实现-探析存储的机制和原理
  12. 网络摄像头ip php,C#实现IP摄像头的方法
  13. 现浇板弹性计算还是塑性计算_板塑性与弹性.doc
  14. WebWork深入浅出 (转贴)http://www.blogjava.net/moxie/archive/2006/10/20/76375.html
  15. macd的python代码同花顺_史上最全MACD攻略讲解:这一篇足够
  16. 实现ftpserver
  17. 股骨截骨php钢板,最新综述:股骨远端截骨治疗膝关节畸形进展(下)
  18. 华硕笔记本刷BIOS
  19. 如何增加微信朋友圈分享链接的小图片
  20. Word 打开WPS文档成乱码的解决方法(转)

热门文章

  1. 电脑登录qq但是打不开网页的解决办法(转载)
  2. byr_filesys图床
  3. 【python】小游戏-下载即可玩
  4. 郑清 - 日常作息表 - August - 2019
  5. python爬取数据实践,以及趟过的坑
  6. hive comand
  7. 计算机谱子远走高飞,远走高飞(金志文版乐队总谱)
  8. java linux tracert_Linux系统中tracert命令使用详解
  9. Hu矩---OpenCV-Python开发指南(26)
  10. 10分钟!一键部署Oracle 11GR2单机