初级排序算法
模板:定义了基本的辅助函数
传入了实现了Comparable接口的数据类型的数组
class Sort{
public void sort(Comparable [] a){}
public static boolean less(Comparable v,Comparable w){
return v.compareTo(w)<0;
}
public static void exch(Comparable[] a,int i ,int j){
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
public static void show(Comparable []a ){
for (int i = 0; i < a.length; i++) {
System.out.print(a[i]+" ");
}
System.out.println();
}
}
冒泡排序
实现:
重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
时间复杂度分析:
若数组的初始状态是正序的,一趟扫描既可以完成排序,所需的比较次数C和记录移动次数M均达到最小值,Cmin = n-1,Mmin=0,因此冒泡排序的时间复杂度为O(n)
若数组的初始状态为反序的,需要进行n-1趟排序,每趟排序要进行n-i次关键字的比较,且每次都需要三次对记录的交换来交换位置,这种情况下,比较和移动的次数均达到最大值
Cmax = n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n−1) = O(n2)
Mmax = 3 n ( n − 1 ) 2 \frac{3n(n-1)}{2} 23n(n−1) = O(n2)
冒泡排序的最坏时间复杂度为O(n2)
稳定性:由于不换交换相等的数据,因此冒泡排序是稳定的排序
class BubbleSort extends Sort{
public void sort (Comparable [] a) {
int N = a.length;
boolean flag = false;//判断本次冒泡是否有交换数据
for(int i = 0;i<N;i++){
flag = false;//重置为没有交换
for(int j = 0;j<N-1-i;j++){//每次循环最后一个数据都为最大的数字
if(a[j].compareTo(a[j+1])>0){
exch(a,j,j+1);
flag = true;
}
}
if(flag == false){//如果这次排序没有调换数据,证明序列已经有序
return;
}
}
}
}
选择排序
实现:
第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。
时间复杂度分析:
选择的比较次数O(n2),总的比较次数为N=(n-1)+(n-2)+…+1 = n ∗ ( n − 1 ) 2 \frac{n*(n-1)}{2} 2n∗(n−1)
交换次数是O(n)
最好情况:已经有序,交换0次;最坏情况交换n-1次,逆序交换 n 2 \frac{n}{2} 2n次,交换次数比冒泡排序少
**稳定性:**不稳定排序,在交换的时候,可能将相等的值往后面交换。例如58529,这种排序,选择排序会破坏算法的稳定性。
class Selection extends Sort{
// public Selection(){}
public void sort(Comparable [] a){
int N = a.length;
for (int i = 0; i < N; i++) {
int min = i;
for (int j = i+1; j < N; j++) {
//寻找最小的数
if(less(a[j],a[min]))min = j;
}
//将最小的数与遍历的下标交换
exch(a,i,min);
}
}
}
插入排序
思路:
每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
时间复杂度分析:
如果数组最开始是有序的,那么比较次数为Cmin=n-1,移动次数Mmin=0
如果数组最开始是无序的,那么复制度为O(n2)
Cmax=(n-1)+(n-2)+…+1= n ∗ ( n − 1 ) 2 \frac{n*(n-1)}{2} 2n∗(n−1) = O(n2)
Mmax = 3 n ( n − 1 ) 2 \frac{3n(n-1)}{2} 23n(n−1) = O(n2)
稳定性:
稳定算法,每次的交换都发生在一个数字和一个已经有序的数组中。
class Insertion extends Sort{
public void sort(Comparable [] a) {
int N = a.length;
for (int i = 1; i < N; i++) {//将a[i]插入到前面的排好序的序列中,这个循环只是提供前面排好序的下标,真正进行交换的是下面一个循环
//比较已经排序的前面的数组
for (int j = i; j > 0; j--) {
//从边界开始与已经有序的数组比较,如果比前面的小,一直交换,直到到达指定位置
if(a[j].compareTo(a[j-1])<0)
exch(a,j,j-1);
}
}
}
希尔排序
思路:
对于大规模的乱序数组的插入排序很慢,因为他们只会交换相邻的元素,因此元素只能一点一点的从数组的一端移动到另外一个端。希尔排序为了加快速度简单的改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终插入排序将局部有序的数组排序。希尔排序的思想就是使数组中任意间隔为h的元素都是有序的。算法实现冲N/3开始递减至1
稳定性
不稳定,由于h有序数组,排序后相同的数字的顺序可能会被打乱
class Shell extends Sort{
public void sort(Comparable[] a) {
int N = a.length;
int h = 1;
while(h<N/3) h = h * 3 + 1;//让h从N/3开始递减到1
while (h >= 1) {
//形成h有序数组,做法与插入排序相似,只是以h为间隔
for (int i = h; i < N; i++) {
for (int j = i; j >= h; j -= h) {
if(less(a[j],a[j-1]))
exch(a,j,j-1);
}
}
h = h/3;//循环直到h为1,就是进行最后的插入排序了
}
}
}