考虑几种简单的排序算法
冒泡排序
策略:每次比较相邻两个元素的大小,大的往后放。这样一次循环之后,最大的元素就被放到了最后面,那么下次排序只需要从1~N-1这些元素进行下一次排序
public static void bubble_Sort(int[] A,int N) {
for(int p=N-1;p>0;p--) {
int flag=0;
for(int i=0;i<p;i++) {
if(A[i]>A[i+1]) {
int tmp=A[i];
A[i]=A[i+1];
A[i+1]=tmp;
//Swap(A[i],A[i+1])
flag=1;
}
}
if(flag==0) break;//flag==1 数组排序完成,不需要接下来的操作
}
}
需要注意的地方:如果我们的数组本来就是顺序的,或者说执行几次循环之后成了顺序的,后面的操作就没有必要进行下去。在里面我们设置了一个flag在外循环设置它为0,在同一深度中分别执行 int flag=0;交换操作;以及判定flag,当某一次循环时候如果出现了全部升序的情况,说明数组排序好了,这时候flag也不会变成1,那么接下来的操作就不用继续了所以break
- 稳定性:两个相邻的数如果相等,不会执行交换,相对位置不会破坏,稳定性满足。
- 时间复杂度:最好情况–数组刚好完全正序:T=O(N);最坏情况–数组刚好完全倒序:T=O(N2);
插入排序
策略:类似于我们打牌克的抽牌,每次我们按顺序抽取其中的一张牌,然后将这张牌和前面的所有牌进行比较,直到满足 A[i-1]<tmp<=A[i]=A[i+1];
public static void insertion_Sort(int[] A,int N) {
int i;
for(int p=1;p<N;p++) {
int tmp=A[p];
for(i=p;i>0&&A[i-1]>tmp;i--)
A[i]=A[i-1];
A[i]=tmp;
}
}
需要注意的地方;我们的操作相当于是把某一位置i的牌拿起来把他和前面的牌比较大小,直到满足上面给出的不等式,这时候A[i]覆盖了A[i+1],所以A[i]=A[i+1],而我们要做的就是把我们的临时变量tmp(保存了原来拿起的那张牌的值)的值赋给A[i];
- 稳定性:假设我们原来的数组中有a=b,并且a与b相邻那么当我们插入的时候先插入b,然后插入a 根据上面写出来的不等式必须满足 a<=b;(由于执行循环的时候b>a不满足,所以把a放到b前面),所以稳定性满足。
- 时间复杂度:最好情况–刚好正序;T=O(N);最坏情况–刚好倒序T=O(N2);
逆序对和两个定理
逆序对:对于下标i<j,如果A[i]>A[j],称(i,j)是一对 逆序对
问题:序列{34,8,64,51,32,21}中有多少逆序对?
答案:(34,8)(34,32)(34,21)(64,51)(64,32)(64,21)(51,32)(51,21)刚好是9对,而且会发现使用冒泡排序和插入排序交换次数也刚好是9次,是因为执行冒泡排序的时候每次交换相邻元素就会消去一个逆序对。
- 定理:任意N个不同元素组成的序列平均具有N(N-1)/4个逆序对
- 定理:任意仅以交换相邻元素来排序的算法,其时间复杂度下界 Ω(N2);
希尔排序
原始的希尔排序
原始增量:Dk=└ Dk+1/2┘
public static void shell_Sort(int[] A,int N) {
for(int D=N/2;D>0;D/=2) {/*希尔增量序列--*/
for(int P=D;P<N;P++) {/*插入排序*/
int Tmp=A[P];
int i;
for(i=P;i>=D&&A[i-D]>Tmp;i-=D)
A[i]=A[i-D];
A[i]=Tmp;
}
}
}
- 最坏时间复杂度 :T=θ(N2)
Hibbard增量序列
Hibbard增量:Dk=2k-1 相邻元素互质
- 最坏情况 T=θ(N2/3)
- 猜想:Tavg=O(N5/4)
Sedgewick 增量序列
{1,5,19,41,109} Sedgewick 增量:4i-3*2i+1
- 最坏情况:Tworst=O(N4/3)
- 猜想:Tavg=O(N7/6)
堆排序
堆排序会牵扯到二叉树的知识,并且实际效果不如Sedgewick 增量的希尔排序,这里暂时不写
归并排序–递归算法
策略:分而治之,递归算法会使用大量的额外堆栈空间。
有序子列的归并
public static void merge(int[] A,int[] TmpA,int L,int R,int RightEnd) {
int LeftEnd=R-1;
int Tmp=L;
int num=RightEnd-L+1;
while((L<=LeftEnd)&&(R<=RightEnd)) {
if(A[L]<A[R]) TmpA[Tmp++]=A[L++];
else TmpA[Tmp++]=A[R++];
}
while(L<=LeftEnd)
TmpA[Tmp++]=A[L++];
while(R<=RightEnd)
TmpA[Tmp++]=A[R++];
for(int i=0;i<num;i++,RightEnd--)
A[RightEnd]=TmpA[RightEnd];
}
- L=左边起始位置
- R=右边起始位置
- RightEnd=右边终点位置
- LeftEnd=左边终点位置
- Tmp=TmpA中充当指针
- num=数组中归并的总数
- L不为0是为了后面的操作
递归实现子序列合并
public static void mSort(int[] A,int[] TmpA,int L,int RightEnd) {
int center;
if(L<RightEnd) {
center=(L+RightEnd)/2;
mSort(A,TmpA,L,center);
mSort(A,TmpA,center+1,RightEnd);
merge(A,TmpA,L,center+1,RightEnd);
}
}
需要注意的地方:if(L<RightEnd)是为了判定L到RightEnd之间的元素个数
合并算法–递归
现在我们把算法转化为标准格式X_Sort(ElementType A[],int N)
public static void merge1_Sort(int[] A,int N) {
int[] TmpA=new int[N];
if(TmpA!=null) {
mSort(A,TmpA,0,N-1);
}
}
归并排序–非递归算法
子序列合并
public static void merge_Pass(int[] A,int[] TmpA,int N,int length) {
int i;
int j;
for(i=0;i<N-2*length;i+=2*length)
merge(A,TmpA,i,i+length,i+2*length-1);
if(i+length<N)
merge(A,TmpA,i,i+length,N-1);
else
for(j=i;j<N;j++)
TmpA[j]=A[j];
}
合并算法–非递归
public static void merge2_Sort(int[] A,int N) {
int[] TmpA=new int[N];
int length=1;
if(TmpA!=null) {
while(length<N) {
merge_Pass(A,TmpA,N,length);
length*=2;
merge_Pass(TmpA,A,N,length);
length*=2;
}
}
}
需要注意的地方:两种算法都需要额外的堆栈空间存储,但是两者都是稳定的,并且时间复杂度都是N*logN
快速排序
策略:分而治之 搬运自此处
/*快速排序---分而治之*/
private static void function(int[] arr, int begin, int end) {
if(begin > end) return;
int tmp = arr[begin];
int i = begin;
int j = end;
while (i != j) {
while (arr[j] >= tmp && j > i)
j--;
while (arr[i] <= tmp && j > i)
i++;
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
arr[begin] = arr[i];
arr[i] = tmp;
function(arr, begin, i - 1);
function(arr, i + 1, end);
}
表排序
桶排序
基数排序
多关键字排序
测试程序
public static void print(int[] array) {
for(int tmp:array) {
System.out.print(tmp+" ");
}
System.out.println();
}
public static void main(String[] args) {
int[] A={34,8,64,51,32,21};
//对比查看排序前后数组的变化
print(A);
//insertion_Sort(A,A.length);
//bubble_Sort(A, A.length);
//merge1_Sort(A,A.length);
//merge2_Sort(A,A.length);
//shell_Sort(A,A.length);
print(A);
}