選擇排序(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次比較後就可以知道這個數組已經是有序的了。