选择排序(Selection sort)
它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完一次循环,他的平均时间复杂度是О(n²)
以一次循环为例:arr[0]中的值分别与arr[1]~arr[3]中的值比较,则进行三次比较,结果流程如下:
/ | arr[0] | arr[1] | arr[2] | arr[3] |
---|---|---|---|---|
第一次 | 3 | 4 | 2 | 1 |
第二次 | 2 | 4 | 3 | 1 |
第三次 | 1 | 4 | 3 | 2 |
这里由于是从小到大,所以选取了数组里最小的元素出来,排在了起始位置。
那么,假设数组arr.length=n,只要唯一确定前n-1或后n-1个数就可以完成对数组的排序。那么最外层循环可以确定:n-1次外层。
内层循环确定:
第一次比较要(n-1)次
第二次比较要(n-2)次
………………..
倒数第二次要2次
最后一次需要1次,因为最后一个数不需要自己和自己比较。
1.若从前往后排序
要排好arr[0]~arr[n-2]共n-1个数就可以确定这个有序数组了。
public class SelectSort {
public static void selectSort1(int a[]){
//两层循环,最外层确定n-1次即可完成排序
for(int i=0;i<a.length-1;i++){
//内层排序,次数从n-1到1
for(int j=i+1;j<a.length;j++){
if(a[i]>a[j]){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
System.out.println("********");
for(int m=0;m<a.length;m++){
System.out.print(a[m]+" ");
}
System.out.println();
}
}
}
public static void main(String args[]){
//测试实例
int []arr1 = {4,3,2,1,0};
selectSort1(arr1);
System.out.print("结果是");
for(int a=0;a<arr1.length;a++){
System.out.print(arr1[a]+" ");
}
}
}
运行过程及结果:
2.若从后往前排序
/**
*可以从前排,也就可以从后排
*/
public class SelectSort {
public static void selectSort2(int a[]){
//外层循环,从n-1到1
for(int i=a.length-1;i>0;i--){
//内层循环,还是从n-1次到1次,只不过是排最大值而已
for(int j=i-1;j>=0;j--){
if(a[i]<a[j]){
int temp = a[j];
a[j] = a[i];
a[i] = temp;
}
System.out.println("********");
for(int m=0;m<a.length;m++){
System.out.print(a[m]+" ");
}
System.out.println();
}
}
}
public static void main(String args[]){
//测试实例
int []arr1 = {4,3,2,1,0};
selectSort2(arr1);
System.out.print("结果是");
for(int a=0;a<arr1.length;a++){
System.out.print(arr1[a]+" ");
}
}
}
运行结果:
虽然从前从后排序是一样的,但是这样能让我对选择排序理解更深刻吧!
3.选择排序的优化
不论是从前往后排序还是从后往前排,其实都是一样的,只要给好内外循环的次数就可以了,但是上面的排序是很冗余的,每次都要不停的交换。需要优化:
/**
*选择排序优化
*/
public class selectsort2{
public static void selectSort(int a[]){
for(int i=0;i<a.length-1;i++){
//每次循环取出余下数组中最小值的下标
int index = i;
for(int j=i+1;j<a.length;j++){
if(a[index]>a[j]){
index = j;
}
}
//与对应头部进行交换
if(index!=i){
int temp = a[i];
a[i] = a[index];
a[index] = temp;
}
System.out.println("********");
for(int m=0;m<a.length;m++){
System.out.print(a[m]+" ");
}
System.out.println();
}
}
//测试
public static void main(String []args){
int arr[] = {2,9,5,4,8,1};
selectSort(arr);
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]);
}
}
}
结果:
总结:这种取索引的方式还是很不错的,减少了很多不必要的交换,直接对每一次大循环的结果交换。
时间复杂度,在最坏情况下:次数=n(n-1),时间复杂度为O(n^2)
在最好情况下,仍然需要进行所有循环才可以知道是否排序完成,但是他的移动次数可能是0,但它的时间复杂度还是O(n^2)。
冒泡排序(Bubble Sort)
冒泡排序算法需要遍历几次数组。在每次遍历中,比较连续相邻的元素。如果某一对元素是降序,则互换他们的值;否则保持不变。由于较小的值像“气泡”一样逐渐浮向顶部,而较大的值沉向底部,所以称之为冒泡排序或下沉排序。
同选择排序类似,最外层循环需要n-1次循环,内层循环需要从n-1到1次,平均时间复杂度为О(n²)
原理图(从小到大排序):
相邻元素做比较,那么,从前往后排,大的元素会沉向底部;
从后往前排,小的元素会浮向顶部。
1.大数向下沉
public class BubbleSort{
//测试
public static void main(String []args){
int arr[] = {4,3,2,1,0};
bubbleSort1(arr);
for(int j=0;j<arr.length;j++){
System.out.print(arr[j]);
}
}
public static void bubbleSort1(int a[]){
//外层循环,唯一确定n-1个数就可以确定这个有序序列
for(int k=1;k<a.length;k++){
//内层循环,邻间比较,同时除去以排好的数
for(int i=0;i<a.length-k;i++){
if(a[i+1]<a[i]){
int temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
System.out.println("********");
for(int m=0;m<a.length;m++){
System.out.print(a[m]+" ");
}
System.out.println();
}
}
}
}
}
运行结果:
4先向后沉底,然后是3,2,1,0
2.再写一个小数向上浮的冒泡排序
大数向下沉,因为我从前往后比较,那么大的数就会逐渐被置换到后面;所以小的数要想往前冒泡,就必须从后往前比较。
/**
*冒泡排序,小数往上浮
*/
public class BubbleSort{
public static void bubbleSort2(int a[]){
//外层循环不变
for(int k=0;k<a.length-1;k++){
/***********************
内层循环从后往前比较,则从arr[arr.length-1]到arr[k+1]
遍历,并每次与前一个数比较
************************/
for(int i=a.length-1;i>k;i--){
if(a[i] < a[i-1]){
int temp = a[i-1];
a[i-1] = a[i];
a[i] = temp;
}
System.out.println("********");
for(int m=0;m<a.length;m++){
System.out.print(a[m]);
}
System.out.println();
}
}
}
//测试
public static void main(String []args){
int arr[] = {4,2,3,1,0};
bubbleSort2(arr);
for(int j=0;j<arr.length;j++){
System.out.print(arr[j]);
}
}
}
运行结果:
可见小的数是往上冒出去的。
3冒泡排序优化:
与选择排序不同,冒泡排序是相邻数比较,那么一次循环比较就可以知道每个相邻数值之间的大小关系了。所以冒泡排序最好情况是只经历一次循环,时间复杂度最小为O(n);
最大是O(n^2)。
那么这样的话就可以根据这一轮排序是否进行了数值交换来判断排序是否完成排序。
//冒泡排序的优化
public class BubbleSort{
public static void bubbleSort2(int a[]){
/**********
boolean b = true;
优化开始点,定义他的意义:if(a[i] < a[i-1])未
触发,说明这个数组有序,不需要在进行遍历了。
************/
boolean b = true;
for(int k=0;k<a.length-1&&b;k++){
b= false;
for(int i=a.length-1;i>k;i--){
if(a[i] < a[i-1]){
int temp = a[i-1];
a[i-1] = a[i];
a[i] = temp;
b = true;
}
//当前循环后打印
System.out.println("********");
for(int m=0;m<a.length;m++){
System.out.print(a[m]);
}
System.out.println();
}
}
}
public static void main(String []args){
int arr[] = {1,2,3,4,5};
bubbleSort2(arr);
for(int j=0;j<arr.length;j++){
System.out.print(arr[j]);
}
}
}
运行结果(包括优化过的和未优化的结果分析)
总结:可以看出,如果数组{1,2,3,4,5}排序不进行优化时,需要(4+3+2+1)共十次排序;优化后,进行了前4次比较后就可以知道这个数组已经是有序的了。