一.冒泡排序
基本思想:通過對待排序序列此前向后,依次比較相鄰元素的值,若發現逆序則進行交換,使得較大的值從前面移動到后面,
類似於水下的氣泡一樣(是所有排序算法中效率最低的)

public static void BobbleSort(int[] arr){ /*冒泡排序,時間復雜度為O(n^2)*/ if (arr == null || arr.length == 0){ return; } int temp = 0; // 臨時變量,用於存放大的數 boolean flag = false; // 是否進行過交換,默認為false for (int i = 0; i < arr.length-1;i++){ //需要遍歷的次數 for (int j = 0; j < arr.length-1-i;j++){ //遍歷數組中的值,來比較 if (arr[j] > arr[j+1]){ // 如果后面的數比前面的數要大,則進行交換 flag = true;// temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } if (!flag){ // 如果flag==false,表示沒有進行過交換,直接退出即可 break; }else{ flag = false; // 要將flag重置,進行下一次的判斷 } } } }
測試,使用80000個隨機數來進行測試
public static void main(String[] args) { int[] array = createRandomArr(80000); showTime("排序開始時間"); BobbleSort(array); showTime("排序結束時間"); } public static void showTime(String str){ Date d = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dStr = sdf.format(d); System.out.println(str+": "+dStr); } public static int[] createRandomArr(int n){ int[] arr = new int[n]; for (int i = 0; i < n; i++) { arr[i] = (int)(Math.random() * 800000); } return arr; }
執行結果: 排序耗時9秒

二.選擇排序
基本思想:第一次從arr[0]~arr[n-1]中選出最小值和arr[0]進行交換,第二次從a[1]~a[n-1]選出最小值和a[1]進行交換,第三次從a[2]~a[n-1]選出
最小值和a[2]進行交換,直到執行n-1次,得到一個排序碼從小到大的有序序列
public static void selectSort(int[] arr){ /*選擇排序*/ if (arr.length == 0 || arr == null){return;} int minIndex = 0; int minValue = 0; for (int i = 0; i < arr.length-1; i++) { minIndex = i; // 記錄最小值的下標,從0開始 minValue = arr[i]; // 記錄最小值,假設是a[0]開始 for (int j = i+1; j < arr.length;j++){ // 從i后開始循環 if (minValue > arr[j]){ // 如果最小的值,並不是a[i],重置minIndex和minValue minValue = arr[j]; // 獲取最小值,和最小值的下標 minIndex = j; } } // 將最小的值放在a[i],比較並進行交換 if (minIndex != i){ arr[minIndex] = arr[i]; // 把a[0]第一個值先放在a[minIndex]處 arr[i] = minValue; // 把保存下來的最小值回填到a[0],即找到了全局的最小值 } } }
測試,使用80000個隨機數來進行測試
執行結果: 排序耗時2秒

三.插入排序
基本思想:把n個待排列的元素看成一個有序表和一個無序表,開始時有序表中只包含一個元素,無序表中包含n-1個元素,排序過程中每次從無序表中抽取
第一個元素,將它的排序碼依次與有序表元素的排序碼進行比較,將其插入到有序表中的適當的位置,成為新的有序表
public static void insertSort(int[] array){ /*插入排序*/ if (array.length == 0 || array == null){return;} int insertVal = 0; int insertIdx = 0; for (int i = 1; i < array.length; i++) { // 定義待插入的數 insertVal = array[i];// 從第二個數開始和第一個數進行比較 insertIdx = i -1; // 第一個數的下標 // 給insertVal找到合適的位置 // 1.insertIdx >=0保證給insertVal插入的位置不越界 // 2.insertVal < array[insertIdx] 找到了待插入的數 // 3.需要將arr[insertIdx]后移 while (insertIdx >=0 && insertVal < array[insertIdx]){ array[insertIdx+1] = array[insertIdx]; insertIdx--; } // 退出while循環時候說明數已經找到,只要把保留下來的數放到前面即可 if (insertIdx+1 != i){ array[insertIdx+1] = insertVal; } } }
測試,使用80000個隨機數來進行測試
執行結果: 排序耗時不到1秒

四.希爾排序
基本思想:當插入排序插入的數較小時,會導致頻繁移動數組導致執行效率低下,希爾排序是一種優化的插入排序.它將數據按下標的一定增量分組
對每組直接進行簡單插入排序,隨着增量逐漸減少,每組包含的關鍵詞就越多,當增量減少到1時,所有的數據都已排序完畢
public static void shellSort2(int[] arr){ /*使用移位法進行希爾排序*/ if (arr.length == 0 || arr == null){ return; } int minVal = 0; // 記錄最小值 int minIdx = 0; // 記錄最小值的下標 for (int gap = arr.length/2; gap >0 ; gap/=2) { // 每次縮小遍歷次數增量 每次折半,縮小增量 for (int i = gap; i < arr.length; i++) { // 從gap個位置,逐步對其所在的元素進行插入排序 minVal = arr[i]; // 假定最小值是arr[i] minIdx = i; // 記錄i //1.minIdx-gap >=0 確保插入的位置不會越界 //2.minVal < arr[minIdx-gap] 找到了待插入的數 while (minIdx-gap >=0 && minVal < arr[minIdx-gap]){ // 同插入排序 arr[minIdx] = arr[minIdx-gap]; minIdx-=gap; } arr[minIdx] = minVal; } } }
測試,使用80000個隨機數來進行測試
執行結果: 排序耗時不到1秒

五.快速排序
基本思想:通過一趟排序將要排序的數據分割成獨立的兩個部分,其中一部分的數據比另一部分的數據都要小,然后在按照此方法對這兩部分數據
進行快速排序,整個排序通過遞歸實現,以此達到整個數據排列有序
/** * 快速排序 * @param arr 需要排序的數組 * @param left 左索引 * @param right 右索引 */ public static void quickSort(int[] arr,int left,int right){ if (arr.length == 0 || arr == null){return;} int l = left; int r = right; int temp = 0; // 作為交換的臨時變量 int pivot = arr[(left+right)/2]; // 找到數組中間的值 //while循環讓比pivot值小的放在左邊,比pivot值大的放在右邊 while (l<r){ //在pivot的左邊一直找,直到找到比pivot大的值就退出 while (arr[l] < pivot){ l+=1; } //在pivot的右邊一直找,直到找到比pivot小的值就退出 while (arr[r] > pivot){ r-=1; } // 如果l>=r說明左右兩邊的值已經按照左邊全是小於pivot,右邊都是大於pivot的值來存放 if (l >= r){ break; } //交換 temp = arr[l]; arr[l] = arr[r]; arr[r] = temp; // 如果交換完成后,發現pivot == arr[l],需要將r--,前移 if (arr[l] == pivot){ r-=1; } // 如果交換完成后,發現pivot == arr[r],需要將l++,后移 if (arr[r] == pivot){ l+=1; } } // 如果出現 l==r,必須要把l++,r--,否則會出現棧溢出 if (l == r){ l+=1; r-=1; } // 向左遞歸 if (left < r){ quickSort(arr,left,r); } // 向右遞歸 if (right > l){ quickSort(arr,l,right); } }
測試,使用80000個隨機數來進行測試
執行結果: 排序耗時不到1秒

六.歸並排序
基本思想:是利用歸並的思想實現的排序的算法,該算法采用經典的分治策略.

/** * 分割 * @param arr 原數組 * @param left 左下標 * @param right 右下標 * @param temp 臨時數組 */ public static void mergeSort(int[] arr,int left,int right,int[] temp){ if (left < right){ int mid =( left+right )/2; // 向左遞歸 mergeSort(arr,left,mid,temp); // 向右遞歸 mergeSort(arr,mid+1,right,temp); // 合並 merge(arr,left,mid,right,temp); } } /** * 合並 * @param arr 需要排序的原始數組 * @param left 左邊有序序列的初始索引 * @param mid 中間索引 * @param right 右邊有序序列的初始索引 * @param temp 臨時數組 */ public static void merge(int[] arr,int left,int mid,int right,int[] temp){ int i = left; // 初始化i,左邊有序序列的初始索引 int j = mid + 1; // 初始化j,右邊有序序列的初始索引 int t = 0; // 臨時數組的索引 /*1.將左右兩邊的有序數據按照規則填充到temp中,直到左右兩邊的有序序列有一邊處理完為止*/ while (i <= mid && j <= right){ // 繼續 if (arr[i] <= arr[j]){ //如果左邊的有序序列當前的元素小於右邊有序序列當前的元素 temp[t] = arr[i]; // 將左邊有序序列的元素填充到temp中 t+=1; i+=1; }else{ //如果右邊的有序序列當前的元素小於左邊有序序列當前的元素 temp[t] = arr[j];// 將右邊有序序列的元素填充到temp中 t+=1; j+=1; } } /*2.把剩余的數據的一邊依次全部填充到temp中*/ while (i<=mid){ // 左邊的有效數據還有剩余就全部填充到temp中 temp[t] = arr[i]; t+=1; i+=1; } while (j <= right){ // 右邊的有效數據還有剩余就全部填充到temp中 temp[t] = arr[j]; t+=1; j+=1; } /*3.將temp數組的元素拷貝到arr中,每次都要拷貝*/ t = 0; // t要清空 int tempLeft = left; while (tempLeft <= right){ arr[tempLeft] = temp[t]; t+=1; tempLeft+=1; } }
測試,使用80000個隨機數來進行測試
執行結果: 排序耗時不到1秒

七.基數排序
基本思想:將所有待比較的數統一為同樣的數位長度,數位較短的數前補零.然后從最低位開始,依次進行一次排序.這樣從最低位排序一直到最高位排序完成后
數列就成了有序序列了.
public static void radixSort(int arr[]){ /*基數排序算法*/ if (arr.length == 0 || arr == null ){ return; } /*1.找出數組中的最大的數*/ int max = arr[0]; for (int ele : arr) { if(ele > max){ max = ele; } } /*2.確定最大數的長度,確定桶排序的循環次數*/ int maxLength = (max+"").length(); /*定義一個二維數組表示10個桶,每個桶就是一個一維數組 * 1.二維數組中包含10個一維數組 * 2.為了防止放數據導致數據溢出,則每一個一維桶的長度是arr.length * */ int[][] bucket = new int[10][arr.length]; /*記錄每個桶中存放的有效數據的個數 * 比如:bucketElementCount[0],記錄的就是bucket[0]中元素的個數 * */ int[] bucketElementCount = new int[10]; // n表示對數據的位數進行處理,第一次個位,第二次十位,第三次百位,以此類推 for (int i = 0,n=1; i < maxLength; i++,n*=10) { for (int j = 0; j < arr.length; j++) { // 取出每個數對應的位數 int digitOfElement = arr[j] / n % 10; // 放入到對應的桶中 bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[j]; bucketElementCount[digitOfElement]++; // 每一個桶的記錄數遞增 } /* 按照桶的順序,放回到原來的數組*/ int index = 0; //遍歷每一個桶,並將桶的數據放回到原數組 for (int k = 0; k < bucketElementCount.length; k++) { // 如果桶中有數據,才放入 if (bucketElementCount[k] != 0){ // 循環第k個桶,放入 for (int l = 0; l < bucketElementCount[k]; l++) { // 取出元素放入到原數組中 arr[index++] = bucket[k][l]; } } // 每次處理完成后,一定要記得將bucketElementCount[k]清空 bucketElementCount[k] = 0; } } }
測試,使用80000個隨機數來進行測試
執行結果: 排序耗時不到1秒

八.堆排序
基本思想:將待排序的序列構造成一個大頂堆.此時整個序列的最大值就是堆頂的根節點,把它和末尾的元素進行交換.此時末尾的元素就成了最大值.然后將剩余n-1個元素重新構造成一個
堆.如此反復執行就能到一個有序的序列
/** * 把一個二叉樹,調整為大頂堆 * @param array 待調整的數組 * @param i 表示非葉子節點在數組中的索引 * @param length 表示有多少個元素要調整,length逐漸在減少 */ public static void adjustHeap(int[] array,int i,int length){ /*取出當前元素的值,保存在臨時變量中*/ int temp = array[i]; /*開始進行調整*/ /* k=2*i+1,k是i的左子節點*/ for (int k = 2*i+1;k < length; k = k*2+1){ /*如果左子節點的值小於右子節點的值*/ if (k+1 < length && array[k] < array[k+1]){ k++; // k指向右子節點 } /*如果子節點大於父節點*/ if (temp < array[k]){ /*把較大的值賦給當前節點*/ array[i] = array[k]; /*i指向k,繼續循環比較*/ i = k; }else { break; } } /*當循環結束后,已經把i為父節點的數的最大值放在了頂部,所以此時把temp的值放在調整后的位置*/ array[i] = temp; }
public static void heapSort(int[] array){ if (array.length == 0 || array == null){ System.out.println("數組為空,不能排序"); return; } int temp = 0; /*把一個無序的序列構建成一個堆,根據升序和降序選擇大頂堆還是小頂堆*/ for (int i = array.length/2-1; i >= 0; i--) { adjustHeap(array,i,array.length); } /** * 1.將堆頂的元素和堆底的元素進行交換,把最大的元素放在數組的尾部 * 2.重新調整結構,然后繼續交換堆頂元素和當前末尾元素,反復執行,使得數組有序 */ for (int j = array.length-1; j > 0; j--) { /*交換*/ temp = array[j]; array[j] = array[0]; array[0] = temp; adjustHeap(array,0,j); } }
測試,使用80000個隨機數來進行測試
執行結果: 排序耗時不到1秒

八大排序算法的時間復雜度,空間復雜的比較

