程序员内功修炼

# 算法

一.冒泡排序 buddle sort

冒泡排序是一种交换排序

两两比较待排序的关键字,并交换不满足次序要求的那对数,直到整个表都满足次序要求为止。

算法思想:它重复地走访要排列的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来

走访数列的工作是重复地进行直到没有再需要交换,也就是说数列已经排序完成。

冒泡排序就是要每趟排序过程中通过两两比较相邻元素,将小的元素放到前面,大的数字放在后面

 

时间复杂度:冒泡排序的平均时间复杂度O(N^2),冒泡排序的平均时间复杂度O(N)

当数据越接近正序时,冒泡排序性能越好

 

算法稳定性:冒泡排序就是将小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换

也发生在这两个元素之间。

 

优化:对冒泡排序常见的改进方法是加入标志性变量exchange,用于标记某一趟排序过程中是否有数据交换。

如果某一趟排序时并没有进行数据交换,则说明所有数据已经有序,可立即结束排序,避免不必要的比较

 

二.直接插入排序(Insertion Sort)

直接插入排序是一种最简单的插入排序

算法思想:每一趟将一个待排序的记录,按照其关键字的大小插入到有序队列的合适位置里,知道全部插入完成

1.我们先将这个序列中下标为0的元素视为元素个数为1的有序序列

2.然后,我们要依次把R1,R2,....RN-1插入有序序列中,所以我们需要一个外部循环从下标1扫描到N-1

3.接下来描述插入过程。假设这是要将Ri插入到前面有序的序列中,插入到Ri时,前i-1个数肯定已经是有序了

 

时间复杂度

1.当数据正序时,执行效率最好,每次插入都不用移动前面的元素,时间复杂度为O(N)

2.当数据反序时,执行效率最差,每次插入都要前面的元素后移,时间复杂度为O(N^2)

数据越接近正序,直接插入排序的算法性能就越好

 

空间复杂度

由直接插入算法可知,我们在排序过程中,需要一个临时变量存储要插入的值,所以空间复杂度为1

 

优化

因为在一个有序序列中查找一个插入位置,以保证有序序列不变,所以使用二分法查找,减少元素比较次数提高效率

二分查找是对于有序数组而言的,二分法查找算法就是不断对数组进行对半分割,每次拿中间元素和目标数字进行比较,

如果中间元素小于目标数字,则说明目标数字应该在右侧被分割的数组中,相反则位于左侧

 

三 希尔排序

希尔排序又称为缩小增量排序,它是一种插入排序。它是直接插入排序算法的一种威力加强版

希尔排序,也称递减增量排序算法

可想而知,步长的选择是希尔排序的重要部分。算法最开始以一定步长进行排序,然后会继续以更小的步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变成直接插入排序,这就保证了数据一定会被全部排序

 

时间复杂度

步长序列的不同,会导致最坏的时间复杂度为N^2

用这样步长序列的希尔排序比插入排序要快,甚至在小数组中比快速排序和椎排序快,

但是在涉及到大量数据时希尔排序还是比快速排序慢

 

算法稳定性

希尔排序中相等数据可以会交换数据,所以希尔排序是不稳定的算法

直接插入排序是稳定的,而希尔排序是不稳定的

直接排序更适合于原始记录基本有序的集合

希尔排序的比较次数和移动次数都要比直接插入排序少,N越大,效果越明显

直接插入排序也适用于链式存储结构;希尔排序不适用于链式结构

 

 

 

 

四 快速排序

快速排序是一种交换排序:通过一趟排序将要排序的数据分割成独立的两部分:分割点左边都是比它小的数,右边都是比它大的数

然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

步骤:设置两个指针left和right,通过偏移right指针,寻找比基准数小的元素,我们将其赋值给left指针所在的位置 偏移左指针 不断重复知道left,right指针重合 递归过程直到结束

 

时间复杂度:

数据越随机分布时,快速排序性能越好;数据越接近有序,快速排序性能越差

空间复杂度:

快速排序在每次分割的过程中,需要1个空间存储基准值。而快速排序的大概需要NlogN的分割处理,所以占用空间NlogN个

算法稳定性:

在快速排序中,相等元素可能会因为分区而交换顺序,所以它是不稳定的算法。

 

五 简单选择排序

简单选择排序是一种选择排序

选择排序:每趟从待排序的记录中选出关键字最小的记录,顺序放在已排序的记录序列末尾,直到全部排列结束为止。

 

时间复杂度

比较次数总是为N(N-1)/2

当序列反序时,移动次数最多,为3N(N-1)/2,综合以上,简单排序的时间复杂度为O(N^2)

 

空间复杂度

简单选择排序需要占用一个临时空间,用于保存最小值索引

 

 

六 椎排序

椎排序是一种选择排序

每趟从待排序的记录中选出关键字最小的记录,顺序放在已排序的记录序列末尾,直到全部排列结束为止。

椎是一颗顺序存储的完全二叉树

其中每个节点的关键字都不大于其孩子节点的关键字,这样的椎称为小根椎

其中每个节点的关键字都不小于其孩子节点的关键字,这样的椎称为大根椎

3,8,15,31,25 逻辑结构与存储结构 这是一个典型的小根椎

当前元素在数组中以R[i]表示,那么它的左孩子节点R[2*i+1]

右孩子节点R[2*i+2],父节点R[(i-1)/2]

首先,将椎的定义将数组R[0...N]调整为椎,交换R[0]和R[N],然后将R[0...N-1]调整为椎

交换R[0]和R[1]

思想操作:

根据初始数组去构造初始椎,构建一个完全二叉树,保证所有的父节点都比它的孩子节点数值大

每次交换第一个和最后一个元素,输出最后一个元素,然后把剩下元素重新调整为大根椎

 

操作:

构建初始椎------>初始状态----交换---筛选调整---交换--筛选调整---知道交换R[1]和R[0]

椎排序结束

 

时间复杂度:

首先计算建椎时间,接下来就是排序时间

故排序时间O(NlogN)

椎的存储是顺序的,因为椎所对应的二叉树为完全二叉树,而完全二叉树通常采用顺序存储方式

 

算法稳定性:

椎排序是一种不稳定的排序方法

因为在椎的调整过程中,关键字进行比较和交换所走的是该节点到叶子节点的一条路径,因此对于相同的关键字就可能出现排在后面的关键字被交换到前面来的情况

 

七.归并排序

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法

算法思想:

该算法采用经典的分治策略

可以看到这种结构很像一颗完全二叉树,本文的归并排序我们采用递归去实现

分阶段可以理解为就是递归拆分子序列的过程,递归深度为logn

 

时间复杂度:归并排序的形式就是一颗二叉树,它需要遍历的次数就是二叉树的深度

根据完全二叉树可以得出它的时间复杂度是O(n*log2n)

 

空间复杂度:需要一个大小为n的临时存储空间用以保存合并序列

 

算法稳定性,相等的元素的顺序不会改变,所以它是稳定的算法

 

归并排序和椎排序,快速排序比较

从空间复杂度:首选椎排序,其次是快速排序,最后是归并排序

从稳定性来考虑,应选取归并排序,因为椎排序和快速排序都是不稳定的

从平均情况下的排序速度考虑,应选择快速排序

 

八.基数排序

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较

 

基数排序的性能

时间复杂度:count*length count为数组元素最高位数,length为元素个数

空间复杂度:10 + length

算法稳定性:每次都是将当前位数上相同的数值的元素统一装桶,并不需要交换位置,所以基数排序是稳定的算法

 

小结:排序分为内部排序和外部排序

内部排序分为交换排序,插入排序,选择排序,归并排序,基数排序

 

交换排序-----冒泡排序和快速排序

插入排序----直接插入排序和希尔排序

选择排序----简单选择排序和椎排序

posted @ 2018-08-15 17:35  tryFighting  阅读(514)  评论(0编辑  收藏  举报