一 插入排序(直接插入,折半插入,希尔排序)
直接插入
以升序排列为例,将待排序的数组从0号和1号开始比较,若0号大于1号,则用一个变量临时存储1号元素,然后将0号元素前移一个单位,再将临时变量的值插入到0号元素的位置。如果是扫描到3号,4号元素发现3号大于4号,则用一个变量临时存储4号元素,因为前面3,2,1,0号元素已经是升序排列了,再依次扫描3,2,1,0号元素,比较它们和4号元素的大小关系,如比4号大则将其前移,如果发现有比4号小的元素则停止扫描。将临时变量的值插入到此时空出的位置上。
时间复杂度:主要来自对比关键字和移动元素,若有n个元素,则需要n-1趟处理。若要得到升序排列但原本是降序排列,时间复杂度将会达到O(n平方)平均时间复杂度也是如此。对于链表来说,移动元素的次数减少,但是关键词对比的次数可达O(n平方) 空间复杂度:O(1)
稳定性:此算法是稳定的,如果有两个相等元素的话不会改变他们原本的顺序。
折半插入
此算法需要定义low,high,mid三个类似于指针的变量,mid=(low+high)/2,此算法的思路是先用折半查找找到应该插入的位置,在移动元素,low指向0号元素,high指向i号元素(即为所查找元素的前一位),用临时变量存储i号元素,如果mid下标指向的元素大于temp,则让high=mid-1反之(小于)则让low=mid+1,一直到low>high时查找结束,应将[low,i-1]之间的元素全部右移,并将临时变量的值插入到low所指的位置
稳定性:为了保证此算法的稳定性,当mid所指元素等于临时变量的值时应在其右边寻找插入位置。
空间复杂度:O(1)时间复杂度:对比直接插入来说,比较关键字的次数更少,但是移动元素的次数没有变,整体来看时间复杂度仍为O(n平方)
希尔排序
希尔排序相比于以上两种插入算法来说最大的不同是它将整个待排序序列划分成若干个子序列,利用插入排序原理使得子序列内有序,然后逐渐减少子序列个数使得整体有序
空间复杂度:O(1)时间复杂度:采用不同的增量序列时候需要进行排序的趟数和在每一趟中元素的对比和进行的移动次数都有区别,最坏时间复杂度在O(n平方),直接使增量等于一的话等同于插入排序,当n在某个范围时,可达到O(n的1.3次方)
稳定性:是不稳定的,而且不适用于链表,仅仅适用于顺序表,因为需要有随机访问的特性,而不是依次访问。
二.交换排序(冒泡排序,快速排序(qsort方法))
冒泡排序(可用于链表)
如果想得到一个升序序列,从后往前(或者从前往后)两两比较相邻元素的值,如果为逆序,则交换他们,直到序列比较完,这样为一趟冒泡排序。下一趟冒泡排序由于前一趟中最前面(后面)的位置已经确定,所以无需再比较。如果在某一趟中发现没有发生交换,则说明序列已经有序(可以通过设定一个标志变量flag来实现此功能)
空间复杂度:O(1) 时间复杂度:最好:比较n-1次,交换0次;最坏(如要升序而原本为降序)比较次数=交换次数=n(n-1)/2
稳定性:稳定
快速排序
此算法的核心思想是在待排序的序列中选一个元素pivot作为基准(通常用首元素),通过一趟排序将序列分为独立的两部分,使得一部分所有元素小于pivot,一部分所有元素大于等于pivot。然后分别递归地对两个子表重复上述过程,直到每部分中只有一个元素或者空为止,此时所有元素都放在了最终位置上。
此算法为以空间换时间的排序算法 时间复杂度:对于每一层快速排序,只需要处理剩下的待排序元素,时间复杂度不会超过O(n),最好时间复杂度为O(nlog2n)(越均匀越好)(注意基准元素的选择),最坏时间复杂度O(n的平方) (当所给的元素本来就有序时)空间复杂度:O(递归层数),把n个元素组织成二叉树,二叉树的层数就是递归调用的层数,对于n个节点的二叉树,最小高度=[log2n]+1(用两个方程组法),最大高度为n
快排是所有算法中平均性能:最优秀的算法!!!
稳定性:不稳定
-
三.选择排序(简单选择排序,堆排序)
简单选择排序
每一趟在待排序元素中选择关键字最小(最大)的元素加入子序列
时间复杂度:无论怎样都需要n-1趟处理,时间复杂度为O(n方) 空间复杂度:O(1)
稳定性:不稳定
堆排序
堆(heap)的定义:
- L(i)>=L(2i)且L(i)>=L(2i+1) 大根堆
- L(i)<=L(2i)且L(i)<=L(2i+1) 小根堆
可以将其理解为顺序存储的完全二叉树(根>=左右孩子)
以大根堆为例,关于大根堆的建立,采用的是小元素下坠的方法,从最底层的分支节点开始处理,然后逐渐上移。
基于大根堆进行选择排序就是每一趟将堆顶元素加入有序子序列(与待排序序列最后一个元素交换)
时间复杂度:一个节点,每下坠一层,最多需要对比关键字2次,若树高为h,结点在第i层,则将这个节点向下调整最多只需要下坠h-i层,关键字对比次数不超过2(h-i)次
将整棵树调整为大根堆,关键字对比次数不超过4n次
所以建立堆的时间复杂度为O(n),每一趟堆顶与堆底交换时间复杂度为O(log2n)数量级,n-1趟则为O(nlog2n)数量级,相加即为整体的时间复杂度
空间复杂度:O(1)
稳定性:不稳定 建立大根堆时先与左孩子交换,但是在交换时可能与右孩子交换,导致了原先顺序发生改变
插入:以大根堆为例:插入节点与父节点比较,一路上升
删除:以大根堆为例:删除结点用堆底元素代替,一路下坠
-
四.归并排序
所谓归并排序,就是把两个或多个已经有序的序列合并成一个。如果进行m路归并,每选出一个元素需要对比关键字m-1次。实现此排序算法,首先需要封装一个归并操作,需要建立一个辅助数组,将原数组的所有元素复制到辅助数组中去,然后在辅助数组中声明low,mid,high变量,对比从low到mid和从mid到high两个区间的元素大小,根据需要将较大(小)的元素复制到原数组中,如果其中一方有剩余的话则将其全部复制到原数组中。然后为了实现归并排序,需要分别对左右两部分递归的进行归并排序,当low>=high时,便停止递归,进行归并操作。
算法效率分析:2路归并的归并书,形态上就是一颗倒立的二叉树。
空间复杂度:O(n)(来自于辅助数组)+O(log2n)(来自于递归调用)
稳定性:稳定。当出现相等元素时,优先让靠前的元素先复制进原数组
-
五.基数排序
基数排序并不是基于比较的算法,而是根据各个关键字位的权重递增次序对各个关键字位分别作分配和收集。因为先对个位进行分配收集后可以保证在对十位进行处理时个位大的先进入队列。
关于为什么一趟收集是O(r)
每收集一个队列只需要将链表的p指针指向该队列的头指针
稳定性:稳定
应用:
- 数据元素关键字可以方便拆分为d组,且d比较小
- 每组关键字不能太大,r较小
- 数组元素n较大,此时线性n优势就大于log2n或者是n的指数级