1.冒泡排序
每一次相邻的两个数比较,大数沉底,小数上升,完成一趟,可以让最小的数到达顶端,之后对剩下的【0,i-1】进行相同的排序过程,直到最后排序完成
void BetterBubble(int[] Array,int Size){
int i,j,tmp;
for(i=0;i<Size-1;i++){
for(j=0;j<Size-1-i;j++){
if(小) 交换;
}
}
}
优化:双向冒泡排序,一边排最大,一边排最下,直到两个指针 low 大于 high
最佳情况:输入是一个正序
最差情况:输入是一个反序
2.选择排序
无论什么数据都是一个 n 平方的复杂度
工作原理是:首先在未排序的序列中寻找一个最大或者最小的元素,存放到排序序列的起始位置,然后再从剩余的未排序元素中继续寻求最大或者最下的元素,然后放到已经排序好的元素的后边,直到排序完毕
void SelectSort(int Size, int[] Array){
int i,j;
for(i=0;i<Size-1;i++){
for(j=i+1;j<Size){
if(Array[i]>Array[j]) { 记住小的索引}
}
交换最小索引和 i 的数据;
}
}
3.插入排序
直接插入排序是一种最简单的排序方法,基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的,记录增 1 的有序表
编程语言
假设有一组无序的序列 R0,R1,...,Rn-1
1)将 R0 视为已经排好序的序列
2)依次把 R1 到 Rn-1 插入到已经排好队的序列中,所以需要一个外部循环,从 1 扫描的 n-1
3)插入的过程。从后往前在排好序的序列中查找合适的位置,需要一个内部循环,从 i-1 到 0
核心代码
public void insertSort(int[] list){
//从第二个数开始遍历
for(int i = 1;i<n;i++){
int tmp = list[i];
int j = 0;
for( j =i-1;j>=0&&tmp<list[j];j-- ){
list[j+1] = list[j];
}
list[j+1] = tmp;
}
}
时间复杂度
数据正序时,复杂度为 O(N)
数据反序时,复杂度为O(N2)
平均为 O(N2)
空间复杂度
需要一个临时变量来存放要插入的值,所以为O(1)
算法稳定性
不需要改变相等数值元素的位置,所以是稳定的算法
4.希尔排序
又称缩小增量排序,是一种插入排序
基本思想是把记录按照步长分组,对每组记录采用直接插入排序的方法进行排序,随着步长逐渐减小,分成的组包含的信息越多,当步长的值减小到 1 时,整个数据合成为一组,构成一组有序记录,排序完成,如下为示例图
核心代码
public void shellSort(int[] list){
int gap = list.length/2;
while( 1 <= gap){
//把距离为 gap 的元素编为一个组,扫描所有的组
for(int i = gap,i<list.length;i++){
int j = 0;
int temp = list[i];
//对距离为 gap 的元素组进行排序
for(j=i-gap;j>=0 && temp<list[j];j=j-gap){
list[j+gap]=list[j];}
list[j+gap]=temp;
}
gap=gap/2;
}
}
时间复杂度
步长的选择影响复杂度
最好的步长是(1,5,19,41,109,...)
用这样的步长希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快,但是在涉及大量数据时,希尔排序还是比快速排序慢
直接插入排序和希尔排序比较
直接插入是稳定的,希尔是不稳定的
直接插入排序更适合原始记录是基本有序的集合
希尔排序的比较次数和移动次数都比直接插入排序少,当 N 越大时,效果越明显
希尔排序中,增量序列 gap 最后一个步长必须是 1
直接插入排序适用于链式存储结构,希尔排序不适用链式存储结构
5.快速排序
基本思想是通过一趟排序将要排序的数据分割成独立的两部分,分割点左边是比它小的数,右边是比它大的数。然后按照这个方法对着两部分数据分别进行快速排序,递归进行。如下图所示。
核心代码
public int division(int[] list,int left,int right){
int base = list[left];
while(left < right){
//序列右端开始,向左遍历,直到找到小于base的数
while(left<right && list[right]>=base)
right--;
list[left] = list[right];
while(left<right && list[left]>=base)
left++;
list[right] = list[left];
}
list[left] = base;
return left;
}
private void quickSort(int[] list,int left,int right){
//左下标一定小于右下标
if(left<right){
int base = division(list,left,right);
quickSort(list,left,base-1);
quickSort(list,base+1,right);
}
}
时间复杂度
数据越随机分布,快速排序性能越好,数据越接近有序,快速排序性能越差
平均时间复杂度为 Nlog2(N)
空间复杂度
每次分割时需要一个空间存储基准值,大概需要 Nlog2(N)次的分割,所以需要相同的空间
6.堆排序
堆是一颗顺序存储的完全二叉树
其中每一个节点的关键字都不大于其孩子节点的关键字,叫小根堆
每个节点的关键字都不小于其孩子节点的关键字,叫大根堆
设一个元素在数组中以 R[i] 表示,那么它的左孩子节点为 R[ 2 * i + 1 ],右孩子节点为 R [ 2 * i + 2 ] ,父 节点是 R[ (i-1)/2]
一个完全二叉树的最后一个非终端节点是第 n/2 个元素
思想:根据初始数组构造初始堆(构造一个完全二叉树,所有的父节点都比孩子节点值大),每次交换第一个和最后一个元素(最大值),然后把剩下的元素重新调整为大根堆
核心代码
public void HeapAdjust(int[] array,int parent,int length){
int temp = array[parent];//temp 保存当前父节点
int child = 2*parent + 1;// 先获得左孩子
while(child<length){
// 如果有右孩子节点,并且右孩子节点比左孩子大,则选择右孩子节点
if(child + 1 < lendth && array[child]<array[child + 1])
child ++;
// 如果父节点的值大于孩子节点,直接结束
if( temp > array[child])
break;
// 把孩子节点的值赋给父节点
array[parent] = array[child];
// 选取孩子节点的左孩子节点,继续向下筛选
parent = child;
child = 2*parent + 1 ;
}
array[parent] = temp;
}
public void heapSort(int[] list){
//循环建立初始堆
for(i = length/2 ;i>=0 ; i-- ){
HeapAdjust(list,i,list.length);
}
//进行 n-1 次循环,完成排序
for(int i = list.length -1 ; i>0; i--){
//最后一个元素和第一个元素进行交换
int temp = list[i];
list[i] = list[0];
list[0] = temp;
//筛选 R[0] 节点,得到 i-1 个节点的堆
HeadAdjust(list,0,i);
}
}
时间复杂度
堆的存储是顺序的。因为堆所对应的二叉树是完全二叉树,完全二叉树通常采用顺序存储方式。
当想得到一个序列中第 k 个最小元素之前的部分排序序列,最好采用堆排序
平均情况为 nlog2(n)
7.归并排序
基本思想是将数组分成两组,依次类推,当分出来的小组只有一个数据时,可以认为这个小组达到有序,然后再依次合并相邻的两个小组就可以了。
时间复杂度为 O(nlogn)
class MergeSort:
#auxarray 用来中间排序的存储
def sort2(self,array,length):
auxarray = [0]*length
self.mergesort(array,auxarray,0,length-1)
#进行归并排序
def mergesort(self,array,auxarray,begin,end):
if begin < end:
mid = (begin+end) // 2
#对左边递归进行归并排序
self.mergesort(array,auxarray,begin,mid)
#对右边递归进行归并排序
self.mergesort(array,auxarray,mid+1,end)
#归并左边和右边的排好序的数组
self.merge(array,auxarray,begin,end,mid)
#合并两个排序数组
def merge(self,array,auxarray,begin,end,mid):
i = begin
j = mid + 1
k = 0
while i <= mid and j <=end:
if array[i] <= array[j]:
auxarray[k] = array[i]
i = i + 1
k = k + 1
else:
auxarray[k] = array[j]
j = j + 1
k = k + 1
while i <= mid:
auxarray[k] = array[i]
k = k + 1
i = i + 1
while j <= end:
auxarray[k] = array[j]
k = k + 1
j = j + 1
i = 0
j = begin
#将合并后的数组复制到之前的数组中
while j != (end+1):
array[j] = auxarray[i]
i = i + 1
j = j + 1
if __name__ == '__main__':
sort1 = MergeSort()
sort1.sort2([1,7,3,2,4,7,3],7)