第五章 排序
1. 何谓排序
1.1排序的意义
所谓排序是将一组数据依照一定的顺序排列起来。最常见的排序是“从小到大”的“递增排序”和“从大到小”的“递减排序”。
以下列数组为例进行说明
1.2排序的特性
稳定性: 排序过后能使值相同的数据保持原顺序中的相对位置。
不稳定性:排序过后不能使值相同的数据保持原顺序中的相对位置。
例如:
稳定排序的结果:
排序后7(1)仍旧在7(2)之前,二者相对位置不变
不稳定排序的结果:
排序后7(1)则在7(2)之后,二者相对位置发生了改变
1.3排序的分类
排序的分类大致上可分为两种:
内部排序: 将欲处理的数据整个存放到内部存储器中排序,数据可被随机存取.
2.交换式排序:
内部排序中的交换式排序,是运用数据值比较后,以判断规则对数据位置进行交换,已达到排序的目的。
`冒泡排序法:
从数组第一个元素开始,将第一个元素a[i]同下一个元素a[i+1]进行比较,如果a[i]大于a[i+1]则将两者相交换。直到比较完最后一个元素。这时数组中最小的元素会被交换成为数组首端。
由于该比较法每次可以将最大或者最小的元素以交换的方式移动到数组首或数组为,就像气泡从水底浮向水面一样,到水面时气泡最大,故称该排序法为冒泡排序法。
如数组:
int[]a={6,5,8,3,7};
该数组中一共有5个数据,所以要比较4趟,每趟相互比较4次。
第一趟:(1)a[0] VS a[1],因为a[0]>a[1],所以交换a[0]和a[1]
(2)a[1] VS a[2],因为a[1]<a[2],所以不能交换a[1]和a[2]。数组不变。
(3)a[2] VS a[3],因为a[2]>a[3],所以交换a[2]和a[3]
(4)a[3] VS a[4],因为a[3]>a[4],所以交换a[3]和a[4]。
这样第一趟就比较完了,数组中最大的8也到了最后一位,成为第一个吐出的泡泡。
按照这样的步骤继续循环直到所有元素都排序完成为止。
int[]sz={1,2,4,5,6,8,7,3,9};
intn=sz.length;
for(int i=0;i<n;i++){
for(intj = 0;j <n-1;j++)
{
if(sz[j]>sz[j+1])
{
intt=sz[j];
sz[j]=sz[j+1];
sz[j+1]=t;
}
}
}
for(int i=0;i<n;i++)
{
System.out.print(sz[i]+"\t");
}
冒泡排序的优点和缺点:
• 优点: 若数据已有部分排好序,则可以 很快的完成排序。
• 缺点: 会反复扫描数据,比较相邻的两 个数据,速度不快也没有效率。
冒泡排序属于稳定性排序法:
最佳状况:数据的顺序恰与排序后的顺序相同
如:1 2 3 4 5
最坏状况:数据的顺序恰与排序后的顺序相反
如:5 4 3 2 1
快速排序法 :
快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。
分治法的基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。
分解:
在未排序的数组中任选一个记录作为基准,将数组分为左右两个较小的子数组。左边的数组元素都小于基准,右边的数组元素都大于基准,而该基准则位于正确的位置上。然后将两个子数组再使用递归进行分解。这样不断的分解最后得到正确的排序。
具体的算法:
假设有n个数据a[0]~a[n-1]
设立标志L=a[0]=m;指向第1个位置a[0],i=0
标志R=a[n-1];只想第n个位置a[n],j=n
步骤1 :L往右找,直到找到比L值大时停止,假设停止于a[i]
R往左找,直到找到比R值小时停止,假设停止于a[j]
此时可能有两种状况:
如果(i<j)那么将a[i]与a[j]的内容相交换
如果(i≥j)那么将此数组的第一个元素m和a[j]相交换,交换后的m已经找到其所在的位置,并将数组切割成两部分, 其左边数据均小于m,其有边数据均大于m。
步骤2 :每次被分割的分区再分别设立L及R标志,重复步骤1。直到 所有分区排序完成。
例如:
public class Test2 {
static int[] sz={11,1,9,3,7,4};
static int n=sz.length;
public static void main(String[] args){
quickSort(0,n-1);
for (int i = 0; i <n; i++){
System.out.print(sz[i]+"\t");
}
}
static void quickSort(int left,int right){
if(right-left<1)return;
int p=part(left,right);
quickSort(left,p-1);
quickSort(p+1,right);
}
static int part(int left, int right) {
int t=sz[right];
int rightp=right; //right=t
int leftp=left-1; //-1
while(true){
while(sz[++leftp]<t);
while(rightp>0 && sz[--rightp]>t);
if(leftp>=rightp)break;
else{
int tmp=sz[leftp];
sz[leftp]=sz[rightp];
sz[rightp]=tmp;
}
}
该排序方法为不稳定排序,即排序后数值相同的元素之间相对位置会发生改变。
此方法是所有排序方法中速度最快的。
1. 选择式排序
选择排序法排序的方法是:
先在n个元素中找出最小的元素。
然后将此元素和数组第一个元素交换。
再从剩下的(n-1)个元素中找出最小的和第二个元素交换。
这样不断循环,直到所有元素均已排序完成,从而达到排序的目的。
下面我们来看一个选择式排序的例子:
int[] sz={3,7,4,5,6,2,8,9};
intn=sz.length;
//外循环 下标自增
for(inti =0;i<n-1;i++){
intmin=i;
内循环、第二个开始
for(intj=i+1;j<n;j++){
// 如果内循环j=i+1跟i比,也就是第二个跟第一个比/
if(sz[j]<sz[min]){
//就把第二个给到第一个位置
min=j;
}
}
// 交换,定义一个容器t,互换
intt =sz[i];
sz[i]=sz[min];
sz[min]=t;
}
for(int i=0;i<n;i++){
System.out.print(sz[i]+"\t");
}
2. 插入式排序
插入式排序属于内部排序,是对于欲排序的元素以插入的方式寻找该元素的适当位置,以达到排序的目的。
1.排序思想
假设待排序的记录存放在数组R[1..n]中。初始时,R[1]自成1个有序区,无序区为R[2..n]。从i=2起直至i=n为止,依次将R[i]插入当前的有序区R[1..i-1]中,生成含n个记录的有序区。
2.第i-1趟直接插入排序:
通常将一个记录R[i](i=2,3,…,n-1)插入到当前的有序区,使得插入后仍保证该区间里的记录是按关键字有序的操作称第i-1趟直接插入排序。
排序过程的某一中间时刻,R被划分成两个子区间R[1..i-1](已排好序的有序区)和R[i..n](当前未排序的部分,可称无序区)。
直接插入排序的基本操作是将当前无序区的第1个记录R[i]插人到有序区R[1..i-1]中适当的位置上,使R[1..i]变为新的有序区。因为这种方法每次使有序区增加1个记录,通常称增量法。
例如:
int[]sz={3,7,4,5,6,11,8,9};
int n=sz.length;
for(int i =1;i<n;i++)
{
int t=sz[i];
intj =i-1;
while(j>=0&& t<sz[j])
{
sz[j+1]=sz[j];
j--;
}
sz[j+1]=t;
}
for(int i =0;i<n;i++)
{
System.out.print(sz[i]+"\t");
}
}
}