一、交换排序
交换排序包括气泡排序和快速排序两种。
1、气泡排序
气泡排序又称为冒泡排序,它也是一种简单的排序方法。它通过相邻元素之间的比较和交换使排序码较小的元素逐渐从底层移向顶层,即从下标较大的单元移向下标较小的单元,就像水底下的气泡一样逐渐向上冒。当然,随着排序码较小的元素逐渐上移,排序码较大的元素也逐渐下移。气泡排序的过程为:首先将a[n-1]元素的排序码同a[n-2]的元素的排序码进行比较,若a[n-1].stn<a[n-2].stn,则交换两元素的位置,使轻者(即排序码较小的元素)上浮,重者下沉,依次类推,直到比较a[1]同a[0]元素的排序码,并使轻者上浮重者下沉后,第1趟排序结束,此时a[0]为具有最小排序码的元素;然后在a[n-1]~a[1]排序区间内进行第二趟排序,使次最小排序码的元素被上浮到a[1]中;至多重复进行n-1趟后,整个气泡排序结束。
气泡排序的算法描述为:
//气泡排序的算法描述为
public static void bubbleSort(Object a[],int n)
{
//采用气泡排序的方法对数组a中的n的元素排序
if(n>a.length)
{
System.out.println("n值有误,停止执行!");
System.exit(1);
}
else
{
for(int i=1;i<=n-1;i++)
{
boolean flag=false;
for(int j=n-1;j>=i;j--)
{
if(((Comparable)a[j]).compareTo(a[j-1])<0)
{
Object x=a[j];
a[j]=a[j-1];
a[j-1]=x;
flag=true;
}
}
if(flag==false)
{
return;
}
}
}
}
气泡排序算法的时间复杂度为O(n^2)。由于气泡排序通常比直接插入排序和直接选择排序需要移动较多次数的记录,所以它是3种简单排序方法中速度最慢的一个。另外,气泡排序是稳定的。
2、快速排序
快速排序又称划分排序。它是目前所有排序方法中速度最快的一种。快速排序是对气泡排序的一种改进方法,在气泡排序中,元素(记录)的比较和交换是在相邻单元中进行的,记录每次交换只能上移或下移一个相邻位置,因而总的比较和移动次数最多;在快速排序中,记录的比较和交换是从两端向中间进行的,排序码较大的记录一次就能够交换到后面单元,排序码较小的记录一次就能够交换到前面单元,记录每次移动的距离较远,因而总的比较次数较少。
快速排序的过程为:首先从待排序区间(开始时为a[0]~a[n-1])中选取第一个元素作为比较的基准元素,通过从区间两端向中间顺序进行比较和交换,把在前面向后扫描到的大于基准元素排序码的元素同在后面向前扫描碰到的小于基准元素排序码的元素交换位置,当所有元素的排序码都比较一遍后,把基准元素交换到前后两部分单元的交界处,结果是前面单元中所有元素的排序码均小于等于基准元素的排序码,后面单元中所有元素的排序码均大于等于基准元素的排序码,基准元素的当前位置就是排序后的最终位置,然后再对基准元素的前后两个子区间分别进行快速排序,即重复上述过程,当一个区间为空或只包含一个元素时,就结束该区间上的快速排序过程。
在快速排序中,把待排序区间按照第一个元素(即基准元素)的排序码分为前后(或称左右)两个子区间的过程叫做一次划分。设待排序区间为a[s]~a[t],其中,s为区间下限,t为区间上限,s<t,a[s]为该区间的基准元素。为了实现一次划分,首先让i从s+1开始,依次向后取值,并使每一元素a[i]的排序码同x的排序码(x暂存基准元素a[s]的值)进行比较,当碰到a[i].stn大于x.stn时止,再让j从t开始,依次向前取值,并使每一元素a[j]的排序码同x的排序码进行比较,当碰到a[j].stn大于x.stn时止,再让j从t开始,依次向前取值,并使每一元素a[j]的排序码同x的排序码进行比较,当碰到a[j].stn小于x.stn时止,接着交换a[i]与a[j]的值,再让i继续向后取值,让j继续向前取值,直到i大于j为止。此时a[s]~a[j]元素的排序码必然小于等于基准元素a[s]的排序码,a[j+1]~a[t]元素的排序码必然大于等于基准元素a[s]的排序码,a[j+1]~a[t]元素的排序码必然大于等于基准元素a[s]的排序码。把a[s]同a[j]交换后,就完成了一次划分,得到了前后两个子区间,分别为a[s]~a[j-1]和a[j+1]~a[t],其中,前一区间元素的排序码均小于等于基准元素的排序码,后一区间元素的排序码均大于等于基准元素的排序码,基准元素被调整到下标为j的中间位置。(分析参考博客:https://blog.youkuaiyun.com/adusts/article/details/80882649)
根据以上分析,编写出进行快速排序中完成一次划分排序的算法描述为:
//进行快速排序中完成一次划分排序的算法描述
public static int partition(Object a[],int s,int t)
{
//对数组a中下标从s到t区间内的元素进行一次划分,返回中间位置j
int i=s,j=t; //给i和j赋初值
Object x=a[i++]; //把基准元素的值暂存x中,并使i增1
while(i<=j)
{
//从前向后顺序查找一个大于基准值需向后半段交换的元素
while(i<=j && ((Comparable)a[i]).compareTo(x)<=0)
{
i++;
}
//从后向前顺序查找一个小于基准值需向前不区间交换的元素
while(i>=j && ((Comparable)a[j]).compareTo(x)<=0)
{
j--;
}
//当条件成立时交换a[i]和a[j]的值
if(i<j)
{
Object temp=a[i];
a[i]=a[j];
a[j]=temp;
i++;
j--;
}
}
a[s]=a[j]; //交换a[s]和a[j]的值,得到前后两个子区间
a[j]=x;
return j; //返回中间元素的下标位置
}
通过调用partition算法进行快速排序的递归算法描述为:
//通过调用parttion算法进行快速排序的递归算法描述
public static void quickRecursion(Object a[],int s,int t)
{
//采用快速排序方法对数组a中从下标s到t区间内的元素进行递归排序
int j=partition(a,s,t); //对当前排序区间进行一次划分
if(s<j-1)
{
quickRecursion(a,s,j-1); //在左区间内超过一个元素时递归排序左区间
}
if(j+1<t)
{
quickRecursion(a,j+1,t); //在右区间内超过一个元素时递归排序右区间
}
}
调用快速排序递归算法对数组a中n个元素进行快速排序的驱动方法为:
//调用快速排序递归算法对数组a中n个元素进行快速排序的驱动方法为:
public static void quickSort(Object a[],int n)
{
//对数组a[0]至a[n-1]中的n个元素进行快速排序
if(n>a.length)
{
System.out.println("n值有误,停止执行!");
System.exit(1);
}
quickRecursion(a,0,n-1); //调用上面递归算法进行快速排序
}
快速排序是不稳定的。在由快速排序得到的是一棵理想平衡树的情况下,其算法的时间复杂度为O (nlog2n)。当然这是最好的情况。在一般情况下,由快速排序得到的是一棵随机的二叉搜索树,树的具体结构与每次划分时选取的基准元素有关。理论上已经证明,在平均情况下,快速排序的比较次数是最好情况下的2ln2倍,约1.39倍。所以在平均情况下快速排序算法的时间复杂度仍为O(nlog2n)。