例如:待排序序列 5,4,3,7,2,7,(目的是升序排列)
1,冒泡排序:
排n趟,每趟只是将原序列变得相对有序,随着趟数的增加,有序性也得到提升,最终完全有序
5,4,3,7,2,7
第一趟,5>4,交换---》 4,5,3,7,2,7
5>3 ,交换---》 4,3,5,7,2,7
5<=7,不交换
7>2 ,交换,---》4,3,5,2,7,7
第一趟冒泡结果:4,3,5,2,7,7 , (其中 4,5,7,7有序)
第二趟冒泡结果(重复第一次的比较方式):3,4,2,5,7,7(其中3,4,5,7,7有序)
第三趟冒泡结果:3,2,4,5,7,7(其中3,4,5,7,7和2,4, 有序)
第四趟冒泡结果:2,3,4,5,7,7(其中2,3,4,5,7,7有序)
第五趟冒泡:没有发生交换,冒泡到此结束
规律:每趟比较都是将大的元素交换到后面
第一趟比较,就能将最大元素交换到最后
第二趟比较,就能将第二大元素交换到倒数第二个位置
******
那么每趟比较的元素数量是呈现 --1的趋势,这和插入排序有点像
java代码实现:
public int [] testBubbleSort(int []data){
int maxPos=0;
for(int i=0;i<data.length;i++)
{
// data.length-1-i及其之后的元素是排好序的
for(int j=0;j<data.length-i-1;j++)
{
if(data[j]>data[j+1])
{
//将最大位置与i交换
int temp =data[j];
data[j]=data[j+1];
data[j+1]=temp;
}
}
}
return data;
}
2,选择排序:
使用一个单位的空间来存放当前比较对象序列中的第一个元素,该趟的目标是找出这段比较序列中的最小元素及其下标,将其交换到这段比较序列头部
第一趟:比较序列是 5,4,3,7,2,7
初始minPos=0;
minVal=array[0]=5
在这一趟比较中,会出现 4<minVal=5 需要更新最小值:minVal=4,minPos=1
3<minVal=4 需要更新最小值:minVal=3,minPos=2
2<minVal=3 需要更新最小值:minVal=2,minPos=4
第一趟比较完之后,将第一趟找出的最小元素交换到序列头部
也就是 第一趟比较之前: 5,4,3,7,2,7,
第一趟比较之后交换结果:2,4,3,7,5,7
第二趟比较序列是:2, 4,3,7,5,7
比较交换后结果: 2,3,4,7,5,7
第三趟比较序列是:2,3,4,7,5,7
第i趟比较序列是:
array[i-1],array[i],....,array[array.length-1]
每比较完一趟,下一趟比较的元素数量比上一趟少一个
java代码实现
private int [] selectSort(int []array){
// 额外空间 1,这里未开辟新数组
if(null==array||1==array.length){
return array;
}
int minPos=0;
int minVal=array[0];
for(int i=0;i<array.length-1;i++){
for(int j=i+1;j<array.length;j++){
if(array[j]<=minVal){
minVal=array[j];
minPos=j;
}
}
//找到这一次的最小值,此趟的第一个元素交换 i
array[minPos]=array[i];
array[i]=minVal;
//更新最小值为下一趟的第一个元素 i+1
minPos=i+1;
minVal=array[i+1];
}
return array;
}
3,插入排序:
模拟摸牌的过程,手中的牌始终是有序的,需要申请空间array.length
java代码实现:
//插入排序:相当于摸牌
private int[] insertSort(int [] array){
if(null==array||0==array.length){
return array;
}
int []sorted=new int[array.length];//记录有序数组
sorted[0]=array[0];
int itemSortedCount=1;//记录已经排好序的元素个数
for(int i=1;i<array.length;i++){
if(sorted[itemSortedCount-1]<=array[i]){
sorted[itemSortedCount]=array[i];
itemSortedCount++;
}
else{
//将排好序的数组元素往后挪动,插入新元素
int j=itemSortedCount;
while(j>0 &&(sorted[j-1]>array[i]) ){
sorted[j]=sorted[j-1];
j--;
}
sorted[j]=array[i];
itemSortedCount++;
}
}
return sorted;
}
生成随机数组的代码:
public static int []getArrayRand(int length)
{
int []result=new int[length];
for(int i=0;i<result.length;i++)
{
result[i]=(int)(Math.random()*10000);
}
return result;
}
此时速率比较:快排>插入排序>选择排序>冒泡排序
20万条元素排序时间(毫秒):
插入: 5593
选择:1,6547(冒泡的改良,避免了大量无效的交换)
冒泡:8,2772(慢如蜗牛,主要原因是大量无效的交换)
快速:47(飞一样的感觉)
4,快速排序:
两个标记i,和j分别指向数组两端
选取一个k值,表示比较的对象
目的是为了将比k小的元素,放到k的左侧,比k大的元素放在k的右侧
待排序数组:
i | j | ||||||||
下标 | 0 | 1 | 2 | 3 | 4 | 5 | |||
元素 | 5 | 4 | 3 | 7 | 2 | 7 |
i=0,j=5
k=array[i]=5;
part1 从右往左比较:
j=5时,array[ j ]=7>k,不交换
j--;
j=4时,array[ j ]=2<k=5,交换
目前数组:
i | j | ||||||||
下标 | 0 | 1 | 2 | 3 | 4 | 5 | |||
元素 | 2 | 4 | 3 | 7 | 5 | 7 |
k=array[ j ] =5;
part2 从左往右比较
此时:i=0;j=4
k=5>array[i]=2,不交换
i++
k=5>array[i]=array[1]=4,不交换
i++
k=5>array[i]=array[2]=3,不交换
i++
k=5<=array[i]=array[3]=7,交换
交换结果:
i | j | ||||||||
下标 | 0 | 1 | 2 | 3 | 4 | 5 | |||
元素 | 2 | 4 | 3 | 5 | 7 | 7 |
现在从右往左比较,回到part1
k=array[i]=array[3]=5
k=5<array[j]=7,不交换
j--
出现i和j碰头,结束以k=5的比较,对k=5这一元素的左右两侧分别执行上述操作,part1+part2
第一次快排的结果:
i | j | ||||||||
下标 | 0 | 1 | 2 | 3 | 4 | 5 | |||
元素 | 2 | 4 | 3 | 5 | 7 | 7 |
数组1:2,4,3
数组2:7,7
5,4,3,7,2,7,
java代码实现
public void quickSort(int []array,int from,int to){
if(from<0||to>=array.length){
return;
}
int kVal=array[from];
int kPos=from;
int low=from;
int high=to;
while(low<high){
//从右往左找比kVal小的元素
while(low<high && array[high]>=kVal ){
high--;
}
//从右往左出现比kVal小的元素了,交换
if(high>low ){
array[kPos]=array[high];
array[high]=kVal;
kPos=high;
low++;
}
//从左往右,找比kVal大的元素
while(low<high && array[low]<=kVal ){
low++;
}
//找到比kVal大的元素,交换
if(low<high ){
array[kPos]=array[low];
array[low]=kVal;
kPos=low;
high--;
}
}
//到此时,kPos左边全是比kVal小的元素
// 右边全是比kVal大的元素
//怎样判断是否需要继续对子分组是否进行快排呢?如果low与high还是在数组的两端,说米此次快排,未发生排序,自然就是有序了
if(low>from){
quickSort(array,from,kPos-1);
}
if(high<to){
quickSort(array,kPos+1,to);
}
}