冒 泡 排 序 详 细 解 析

合集下载
相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

js实现冒泡排序,快速排序,堆排序【解析时间空间复杂度】

文章目录冒泡排序(Bubble Sort)快速排序堆排序

冒泡排序(Bubble Sort)

时间复杂度

最好的情况:数组本身是顺序的,外层循环遍历一次就完成O(n)

最坏的情况:,O(n2)数组本身是逆序的,内外层遍历O(n2)

空间复杂度

开辟一个空间交换顺序O(1)

稳定,因为if判断不成立,就不会交换顺序,不会交换相同元素

冒泡排序它在所有排序算法中最简单。然而,从运行时间的角度来看,冒泡排序是最差的一个,它的复杂度是O(n2)。

冒泡排序比较任何两个相邻的项,如果第一个比第二个大,则交换它们。元素项向上移动至正确的顺序,就好像气泡升至表面一样,冒泡排序因此得名。

交换时,我们用一个中间值来存储某一交换项的值。其他排序法也会用到这个方法,因此我们声明一个方法放置这段交换代码以便重用。使用ES6(ECMAScript 2015)**增强的对象属性——对象数组的解构赋值语法,**这个函数可以写成下面这样:

[array[index1], array[index2]] = [array[index2], array[index1]];

具体实现:

function bubbleSort(arr) {

for (let i = 0; i arr.length; i++) {--外循环(行{2})会从数组的第一位迭代至最后一位,它控制了在数组中经过多少轮排序 for (let j = 0; j arr.length - i; j++) {--内循环将从第一位迭代至length - i位,因为后i位已经是排好序的,不用重新迭代 if (arr[j] arr[j + 1]) {--如果前一位大于后一位

[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];--交换位置

return arr;

快速排序

时间复杂度

最好的情况:每一次base值都刚好平分整个数组,O(nlogn)

最坏的情况:每一次base值都是数组中的最大-最小值,O(n2)

空间复杂度

快速排序是递归的,需要借助栈来保存每一层递归的调用信息,所以空间复杂度和递归树的深度一致

最好的情况:每一次base值都刚好平分整个数组,递归树的深度O(logn)

最坏的情况:每一次base值都是数组中的最大-最小值,递归树的深度O(n)

快速排序是不稳定的,因为可能会交换相同的关键字。

快速排序是递归的,

特殊情况:leftright,直接退出。

(1) 首先,从数组中选择中间一项作为主元base,一般取第一个值。

(2) 创建两个指针,左边一个指向数组第一个项,右边一个指向数组最后一个项。移动右指针直到找到一个比主元小的元素,接着,移动左指针直到我们找到一个比主元大的元素,然后交换它们,重复这个过程,直到左指针遇见了右指针。这个过程将使得比主元小的值都排在主元之前,而比主元大的值都排在主元之后。这一步叫作划分操作。

(3)然后交换主元和指针停下来的位置的元素(等于说是把这个元素归位,这个元素左边的都比他小,右边的都比他大,这个位置就是他最终的位置)

(4) 接着,算法对划分后的小数组(较主元小的值组成的子数组,以及较主元大的值组成的子数组)重复之前的两个步骤(递归方法),递归的出口为left-right=i,也就是:

lefti-1 - i+1right

此时,子数组数组已排序完成。

归位示意图:

具体实现:

function quicksort(arr, left, right) {

if (left right) {

var i = left,

j = right,

base = arr[left]; --基准总是取序列开头的元素

-- var [base, i, j] = [arr[left], left, right]; --以left指

针元素为base

while (i != j) {

--i=j,两个指针相遇时,一次排序完成,跳出循环

-- 因为每次大循环里面的操作都会改变i和j的值,所以每次循环-操作前都要判断是否满足ij

while (i j arr[j] = base) {

--寻找小于base的右指针元素a,跳出循环,否则左移一位

while (i j arr[i] = base) {

--寻找大于base的左指针元素b,跳出循环,否则右移一位

[arr[i], arr[j]] = [arr[j], arr[i]]; --交换a和b

[arr[left], arr[j]] = [arr[j], arr[left]]; --交换相遇位置元素和base,base归位

-- let k = i;

quicksort(arr, left, i - 1); --对base左边的元素递归排序

quicksort(arr, i + 1, right); --对base右边的元素递归排序 return arr;

堆的概念

堆是一个完全二叉树。

完全二叉树:二叉树除开最后一层,其他层结点数都达到最大,最后一层的所有结点都集中在左边(左边结点排列满的情况下,右边才能缺失结点)。

大顶堆:根结点为最大值,每个结点的值大于或等于其孩子结点的值。

小顶堆:根结点为最小值,每个结点的值小于或等于其孩子结点的值。

堆的存储:堆由数组来实现,相当于对二叉树做层序遍历。如下图:时间复杂度

总时间为建堆时间+n次调整堆—— O(n)+O(nlogn)=O(nlogn)

建堆时间:从最后一个非叶子节点遍历到根节点,复杂度为O(n)

n次调整堆:每一次调整堆最长的路径是从树的根节点到叶子结点,也就是树的高度logn,所以每一次调整时间复杂度是O(logn),一共是O(nlogn)

空间复杂度

堆排序只需要在交换元素的时候申请一个空间暂存元素,其他操作都是在原数组操作,空间复杂度为O(1)

堆排序是不稳定的,因为可能会交换相同的子结点。

步骤一:建堆

以升序遍历为例子,需要先将将初始二叉树转换成大顶堆,要求满足:树中任一非叶子结点大于其左右孩子。

实质上是调整数组元素的位置,不断比较,做交换操作。

找到第一个非叶子结点——Math.floor(arr.length - 2 - 1),从后往前依次遍历

对每一个结点,检查结点和子结点的大小关系,调整成大根堆

-- 建立大顶堆

function buildHeap(arr) {

--从最后一个非叶子节点开始,向前遍历,

相关文档
最新文档