数据结构:计算机组织存储数据的方式
算法:作用于特定数据集上的算法流程
如何衡量一个排序算法的优劣?
1.算法的执行效率
-要看最好、最坏、平均时间复杂度
-有时候个数n太少也要考虑时间复杂度的系数、低阶、常数(冒泡和插入)
-比较或交换的次数
2.算法的内存消耗
-通过空间复杂度来衡量
-原地排序:特指空间复杂度为O(1)的排序算法(就是给定有限个数的空间)
3.算法的稳定性【重点】
-稳定性:若待排序的集合中存在值相等的元素,经过排序之后,相等元素之间原有的顺序是否改变,若未改变则稳定性排序。
//也就是看两个值相等的话,要不要进行排序,或是不改变它们跳到下一次比较。
栗子:订单排序中,如何按照金额排序后,相同金额的时间也是按顺序的?
解决:先按照时间排序,再按照金额稳定性排序
动图解释原理[这里]
内部排序(默认)
排序过程无需借助外部存储器(磁盘),所有操作均在内存中完成。
外部排序
若参与排序的元素过多,数据量过大,内存放不下,需要借助外部存储器进行。(桶排序)
【无论是内部排序还是外部排序,最终数据的排序一定在内存中进行。】
只操作相邻的两个元素,每次对相邻的两个元素做大小比较,看是否满足大小关系,不满足则交换,每次操作都保证至少有一个元素移动到最终位置
优化:设置标志位,在n次循环并没有元素交换,表示排序完毕跳出循环。
时间复杂度:
最好情况:o(n)
最坏情况:O(n^2)
平均复杂度:O(n^2)
空间复杂度:O(1)
-没有开辟新的空间
-所以冒泡排序是一个原地排序
稳定性:稳定
基于有序数组元素的插入而来。
适用:近乎有序的集合。
思想:将待排序数据分为两个区间,已排序区间和待排序区间。
步骤:一开始已排序区间只有一个元素
首先找到要插入的位置,再将数据插入到特定位置,保持数组的有序性。
从后向前,若是比插入数大,则往后退一位。若比插入数小,则直接插入后面。
直到找到比它小的,插入到其后。
时间复杂度:
最好情况:o(n)
最坏情况:O(n^2)
平均复杂度:O(n^2)
空间复杂度:O(1)
-没有开辟新的空间
-所以冒泡排序是一个原地排序
稳定性:稳定
优化:折半插入排序,时间减少一半。
冒泡排序和直接插入排序的复杂度一样
为什么实际上用直接插入更多?
在万数量级 冒泡:100ms+ 插入:10ms+
在十万数量级 冒泡:15000ms+ 插入:800ms+
原因:交换次数
冒泡-交换3次
插入-交换1次
相比直插,优化了搬运的速率
类似于直插排序
找出未排序集合最小值,放到第一位
时间复杂度:
最好情况:o(n)
最坏情况:O(n^2)
平均复杂度:O(n^2)
空间复杂度:O(1)
稳定性:不稳定
所以插入和选择优先插入,因为其稳定性,且二者效率相差不大
分治思想: 将一个大的问题分成n个足够小的问题,当所有小问题解决后,将结果合并起来就是整个问题的求解。
所有能使用分治思想解决的问题均可利用递归的技巧完美解决。
时间复杂度:
平均复杂度:O(nlogn)
O(n)是merge函数的 O(logn)是分组函数的
空间复杂度:O(n)
临时数组在合并后空间会释放
稳定性:取决于合并函数的写法 arr[i] <= arr[j]
最重要的算法之一
基于分治思想
思想 : 从待排序的数组中任取一个任意元素,称为分区点,开始遍历过程,每当发现小于分区点的元素放到分区点左边,反之。每遍历一遍会把基准值放在最终位置。
最坏情况:
当待排序元素近乎有序时,若选取的元素恰好为最大值则整个树只有左子树,即成了一个O(n),此时时间复杂度退化为O(n^2)
最好情况:
每次的分区点都是中间元素,即左右子树差不多,O(nlogn)
平均:
O(nlogn)
空间复杂度:O(1)【原地排序】
稳定性:不稳定算法
优化:
问题:基准值的选取每次都选最大值或最小值,都大于或者小于基准值,整个树只有左子树。O(n^2)
解决:随机选取基准值
int random = (int)(Math.random()*(end-start+1)+start);
swap(array,random,start);
问题:当待排序集合包含大量重复元素时,与基准值相等的元素太多,整个数又只有右子树。O(n^2)
解决:二路快排。将大于和小于key的元素放在数组两端,i不断向后,j不断向前。i小于key,i++。j小于等于key,j–。否则和两端交换。直到i>j停止。
思考:
在O(n)之内找到一个无序数组的第k大值?
若走完一遍后i+1 = k;那么array[i+1]就是第k大的数。
若是 i+1 < k,在i+1右边找。
若 i+1 >k,在i+1左边找。
思想 : 遍历未排序区域选择一个最小的值和区域中第一个元素交换,并把第一个元素放到已排序区域中,直到未排序区域没有元素。
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:不稳定
思想 : 先取基准数建立一个堆,取堆顶元素后将剩余元素组成一个新堆,重复直到取出所有元素。
注意这个堆构成完全二叉树,且这个树所有非终端点的值不大于或者不小于其左右子树。也就是说根节点是当时的最大值或最小值。
也是利用了递归思想,先排再取。
时间复杂度:O(nlogn)
空间复杂度:O(1)
稳定性:不稳定
O(n^2) : 插排、冒泡、选择
运用递归 : 归并、快排、堆排
O(nlogn) : 归并、堆排、快排(时间复杂度不稳定:当快排最坏的情况,时间复杂度变为O(n^2))
不稳定排序 : 希尔、选择、快排、堆排
稳定排序 : 直插 、 冒泡 、 归并