scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序

插入排序:

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第1张图片

时间复杂度O(n^2)

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第2张图片

代码很短,解释一下,l是要排序的部分的最左一位,r是最右一位,为了方便理解,这里l=1,r=10。

插入排序的主要思想是每次往已经有序的序列中插入一个数,这里可以把i左边的看成已经有序的,i右边的看为正在排序的序列,第i位即是当前所需插入的数。

我们逐句分析:i设定为1+1,也就是第二位,重复执行直到i>10。

这里可以看成for(int i=1+1;i<=10;i++)也就是从第2位扫到第10位。

然后j设定为1,重复执行知道j>i-1也就是从第1位扫到第i-1位,直到找到一个比当前需要插入的数还要大的数,将数插入。

假设有一个序列{1,3,5,2,8,4,9,6,10,7}

我们来模拟一下原程序。

{1,3,5,2,8,4,9,6,10,7}

——i—————————————-

{1,3,5,2,8,4,9,6,10,7}

———–i———————————–

{1,3,5,2,8,4,9,6,10,7}

—————-i——————————

{1,2,3,5,8,4,9,6,10,7}

——————–i————————–

{1,2,3,5,8,4,9,6,10,7}

————————-i———————

{1,2,3,4,5,8,9,6,10,7}

——————————i—————-

{1,2,3,4,5,8,9,6,10,7}

———————————-i————

{1,2,3,4,5,6,8,9,10,7}

—————————————-i——

{1,2,3,4,5,6,8,9,10,7}

———————————————i-

{1,2,3,4,5,6,7,8,9,10}

———————————————–i 这时i>10跳出循环。

插入排序是一种很容易理解的排序,但是速度较慢。



冒泡排序:

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第3张图片

时间复杂度O(n^2)

代码也很短。

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第4张图片

冒泡排序的思想是重复走这个序列,每次比较两个相邻的元素,如果发现他们不符合顺序便交换两者。这个很好理解。就不过多解释了,另外提一下,如果比较的时候用的是>或<小于,冒泡就是稳定的,但如果用的是≥或≤,冒泡便变成了不稳定的算法。



选择排序:

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第5张图片

顾名思义,每次找到未排序部分最小的数,放到已排序序列的末尾。

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第6张图片

本来有个模拟的。。。不小心刷新网页了

不想再写一遍

其实很好理解的啦,仔细看看



归并排序(开始比较精彩的了):

程序:

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第7张图片

归并排序是一个采用分治法的一个非常经典的运用,没错,用到了递归

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第8张图片

我们来看一张图:

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第9张图片

这个程序先将这个序列拆分成很多个小序列(程序上面一部分),拆分到无法拆分时便开始合并。

合并过程很简单(程序下面一部分),其实就是二路归并,这里贴个链接,讲的很详细:https://www.cnblogs.com/chengxiao/p/6194356.html)

因为每一边的序列都是有序的,所以可以用这种二路归并的方式。

归并排序的缺点就是空间占用大,我们需要另一个数组来储存归并后的序列,然后再复制到原数组。


 快速排序(喜闻乐见):

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第10张图片
scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第11张图片

气死我了,写了一大堆全没了

https://www.cnblogs.com/MOBIN/p/4681369.html这个链接讲的挺好的,对应着看看吧

堆排序:

堆排序对于新手来说可能会很懵逼(我不就是新手吗),所以程序放后面。

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第12张图片

堆排序需要涉及到很多个概念。(都知道可以跳过)

接下来的资料来源于百度百科,一些教程与理解是我自己写的。

树:

树状图是一种数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:

每个结点有零个或多个子结点;没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;除了根结点外,每个子结点可以分为多个不相交的子树。

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第13张图片

(这就是一颗典型的树)

二叉树:

在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。(摘自百度百科)(比如上面那个树就是个二叉树)

完全二叉树:如果一棵具有n个结点的深度为k的二叉树,它的每一个结点都与深度为k的满二叉树中编号为1~n的结点一一对应,这棵二叉树称为完全二叉树。

完全二叉树有一个很方便的性质,假设有一个完全根结点编号为1的二叉树,有一个结点的编号为i,那么它的左儿子为2*i,右儿子为2*i+1,而它的父亲即为i/2(因为在c++中,当“/”用于两整型操作数相除时,其结果取商的整数部分,小数部分被自动舍弃,也就是向下取整(i/2))

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第14张图片

堆:

堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:

堆中某个节点的值总是不大于或不小于其父节点的值;

堆总是一棵完全二叉树。

将根节点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。

堆的定义如下:n个元素的序列{k[1],k[2],k[i],…,k[n]}当且仅当满足下关系时,称之为堆。

(k[i] <= k[2*i],ki <= k[2*i+1])或者(k[i] >= k[2*i],k[i] >= k[2*i+1]), (i = 1,2,3,4…n/2)

上面讲过了,2*i是这个结点的左儿子,2*i+1是这个结点的右儿子。

若将和此次序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非叶结点的值均不大于(或不小于)其左、右孩子结点的值。由此,若序列{k[1],k[2],…,k[n]}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。

如果你理解了完全二叉树,堆将会很好理解:

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第15张图片

(配合图片食用更佳)

那么接下来终于到正题了。

堆排序:

scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序_第16张图片

可以看到,我们先建立了一个大根堆,向下取整(n/2)代表的是最后一个父结点(也就是最后一个有儿子的节点),然后枚举之前的所有点,因为完全二叉树的性质,之前的所有点一定都是有儿子的。然后将这些点调整(下面那个自定义程序),我们来理解一下,i表示当前要调整哪个点,j表示要调整的点i中较大的儿子(后面那个判断即是为了找出较大的儿子)。

如果发现i这个节点不符合当前构造的堆的规律(它较大的那个儿子比自身大),那么便交换这个结点与它的儿子结点j。由于交换后可能导致以下的部分混乱,于是我们将i设定为j,继续交换,直到符合堆的规律或者j比n大了。

那么这样一个堆就建好了,那排序怎么做呢?还记得吗一个大根堆(或小根堆)的根一定是整个序列中最大(或最小)的,堆排序便是利用了这个特点。

一个堆的根结点必定是整个序列最大的。于是我们交换根结点和最后一个结点,将最后一个结点排除在堆外,然后重建这个堆(这时候堆的大小-1)重复这个过程直到堆中没有结点了。这时候我们的排序就排好了。

总结一下:

a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

b.将堆顶元素与末尾元素交换,将最大元素”沉”到数组末端;

c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

你可能感兴趣的:(scratch图解排序算法:插入排序、冒泡排序、选择排序、归并排序、快速排序、堆排序)