java 数据结构和算法 排序
排序算法
- 排序算法的介紹
- 算法的時間復雜度
- **度量一個程序(算法)執行時間的兩種方法**
- **時間頻度**
- **時間復雜度**
- **常見的時間復雜度**
- 平均時間復雜度和最壞時間復雜度
- 算法的空間復雜度
- 基本介紹
- 排序算法
- 冒泡排序
- 選擇排序
- 插入排序
- 希爾排序
- 快速排序
- 歸并排序
- 基數排序
- 相關術語解釋
排序算法的介紹
排序也稱排序算法(Sort Algorithm),排序是將一組數據,依指定的順序進行排列的過程。
排序的分類:
指將需要處理的所有數據都加載到內部存儲器中進行排序。
算法的時間復雜度
度量一個程序(算法)執行時間的兩種方法
這種方法可行, 但是有兩個問題:一是要想對設計的算法的運行性能進行評測,需要實際運行該程序;二是所得時間的統計量依賴于計算機的硬件、軟件等環境因素, 這種方式,要在同一臺計算機的相同狀態下運行,才能比較那個算法速度更快。
問題:可能花費很長時間;電腦不一樣結果可能不一樣
通過分析某個算法的時間復雜度來判斷哪個算法更優.
時間頻度
基本介紹
時間頻度:一個算法花費的時間與算法中語句的執行次數成正比例,哪個算法中語句執行次數多,它花費時間就多。一個算法中的語句執行次數稱為語句頻度或時間頻度。記為T(n)。
例如:
結論: 在統計一個時間復雜度時,常數項可以忽略
結論: 在統計一個時間復雜度時,可以忽略低次項
結論: 在統計一個時間復雜度時,可以忽略系數
時間復雜度
基本介紹
一般情況下,算法中的基本操作語句的重復執行次數是問題規模n的某個函數,用T(n)表示,若有某個輔助函數f(n),使得當n趨近于無窮大時,T(n) / f(n) 的極限值為不等于零的常數,則稱f(n)是T(n)的同數量級函數。記作 T(n)=O( f(n) ),稱O( f(n) ) 為算法的漸進時間復雜度,簡稱時間復雜度。
注意:T(n) 不同,但時間復雜度可能相同。
如:T(n)=n2+7n+6 與 T(n)=3n2+2n+2 它們的T(n) 不同,但時間復雜度相同,都為O(n2)。
計算時間復雜度的方法:
常見的時間復雜度
常見的算法時間復雜度由小到大依次為:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)< Ο(nk) <Ο(2n),隨著問題規模n的不斷增大,上述時間復雜度不斷增大,算法的執行效率越低
從圖中可見,我們應該盡可能避免使用指數階的算法
常見的時間復雜度
無論代碼執行了多少行,只要是沒有循環等復雜結構,那這個代碼的時間復雜度就都是O(1)
上述代碼在執行的時候,它消耗的時候并不隨著某個變量的增長而增長,那么無論這類代碼有多長,即使有幾萬幾十萬行,都可以用O(1)來表示它的時間復雜度。
說明:在while循環里面,每次都將 i 乘以 2,乘完之后,i 距離 n 就越來越近了。假設循環x次之后,i 就大于 2 了,此時這個循環就退出了,也就是說 2 的 x 次方等于 n,那么 x = log2n也就是說當循環 log2n 次以后,這個代碼就結束了。因此這個代碼的時間復雜度為:O(log2n) 。 O(log2n) 的這個2 時間上是根據代碼變化的,i = i * 3 ,則是 O(log3n) .
說明:這段代碼,for循環里面的代碼會執行n遍,因此它消耗的時間是隨著n的變化而變化的,因此這類代碼都可以用O(n)來表示它的時間復雜度
說明:線性對數階O(nlogN) 其實非常容易理解,將時間復雜度為O(logn)的代碼循環N遍的話,那么它的時間復雜度就是 n * O(logN),也就是了O(nlogN)
… … … …
平均時間復雜度和最壞時間復雜度
算法的空間復雜度
基本介紹
排序算法
冒泡排序
基本介紹
冒泡排序(Bubble Sorting)的基本思想是:通過對待排序序列從前向后(從下標較小的元素開始),依次比較相鄰元素的值,若發現逆序則交換,使值較大的元素逐漸從前移向后部,就象水底下的氣泡一樣逐漸
向上冒。
優化:
因為排序的過程中,各元素不斷接近自己的位置,如果一趟比較下來沒有進行過交換,就說明序列有序,因此要在排序過程中設置一個標志flag判斷元素是否進行過交換。從而減少不必要的比較。(這里說的優化,可以在冒泡排序寫好后,再進行)
圖解:
代碼:
package sort; /** @Author: Min* @Date: 2021/10/3* @description*/import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date;public class BubbleSort {public static void main(String[] args) {//測試冒泡排序的速度//給定80000個數據測試int[] arr = new int[80000];for (int i = 0; i < 80000; i++) {arr[i] = (int) (Math.random() * 8000000);//生成[0,8000000)的隨機數組}Date start = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String s = sdf.format(start);bubbleSort(arr);Date end = new Date();String e = sdf.format(end);System.out.println("排序前:" + s);System.out.println("排序后:" + e);// int arr[] = {3,9,-1,10,-2}; // //為了容易理解,如下是冒泡排序的演變過程 // //第一趟排序.最大的數排在最后 // int temp = 0;//臨時變量 // for (int i = 0; i < arr.length-1; i++) { // //如果前面的數比后面的數大,則交換 // if (arr[i] > arr[i+1]) { // temp = arr[i]; // arr[i] = arr[i+1]; // arr[i+1] = temp; // } // } // System.out.println("第一趟排序后的數組:"); // System.out.println(Arrays.toString(arr)); // // //第二趟排序 // for (int i = 0; i < arr.length-1-1; i++) { // //如果前面的數比后面的數大,則交換 // if (arr[i] > arr[i+1]) { // temp = arr[i]; // arr[i] = arr[i+1]; // arr[i+1] = temp; // } // } // System.out.println("第二趟排序后的數組:"); // System.out.println(Arrays.toString(arr)); // // //第三趟排序 // for (int i = 0; i < arr.length-1-2; i++) { // //如果前面的數比后面的數大,則交換 // if (arr[i] > arr[i+1]) { // temp = arr[i]; // arr[i] = arr[i+1]; // arr[i+1] = temp; // } // } // System.out.println("第三趟排序后的數組:"); // System.out.println(Arrays.toString(arr)); // // //第四趟排序 // for (int i = 0; i < arr.length-1-3; i++) { // //如果前面的數比后面的數大,則交換 // if (arr[i] > arr[i+1]) { // temp = arr[i]; // arr[i] = arr[i+1]; // arr[i+1] = temp; // } // } // System.out.println("第四趟排序后的數組:"); // System.out.println(Arrays.toString(arr));// //代碼優化 // int temp = 0; // for (int j = 0; j < arr.length-1; j++) { // // for (int i = 0; i < arr.length-1-j; i++) { // //如果前面的數比后面的數大,則交換 // if (arr[i] > arr[i+1]) { // temp = arr[i]; // arr[i] = arr[i+1]; // arr[i+1] = temp; // } // } // System.out.println("第"+(j+1)+"趟排序后的數組:"); // System.out.println(Arrays.toString(arr)); // } // // int arr2[] = {3,9,-1,10,20}; // //算法優化 // boolean flag = false;//標識符---表示是否進行過交換 // for (int j = 0; j < arr2.length-1; j++) { // // for (int i = 0; i < arr2.length-1-j; i++) { // //如果前面的數比后面的數大,則交換 // if (arr2[i] > arr2[i+1]) { // flag = true; // temp = arr2[i]; // arr2[i] = arr2[i+1]; // arr2[i+1] = temp; // } // } // System.out.println("第"+(j+1)+"趟排序后的數組:"); // System.out.println(Arrays.toString(arr2)); // if (!flag) { // //某一趟排序中一次都沒有交換過 // break; // } else { // flag = false;//很重要 // //重置,進行下一次的判斷 // } // } // /* // 解析:最后有三趟 // int arr2[] = {3,9,-1,10,20}; // 第1趟排序后的數組:[3, -1, 9, 10, 20]——有交換 // 第2趟排序后的數組:[-1, 3, 9, 10, 20]——有交換 // 第3趟排序后的數組:[-1, 3, 9, 10, 20]——無交換,代碼停止 // */// //測試封裝后的冒泡排序 // int arr3[] = {3,9,-1,10,20}; // bubbleSort(arr3);}//將冒泡排序優化算法封裝public static void bubbleSort(int[] arr) {int temp = 0;boolean flag = false;//標識符---表示是否進行過交換for (int j = 0; j < arr.length-1; j++) {for (int i = 0; i < arr.length-1-j; i++) {//如果前面的數比后面的數大,則交換if (arr[i] > arr[i+1]) {flag = true;temp = arr[i];arr[i] = arr[i+1];arr[i+1] = temp;}} // System.out.println("第"+(j+1)+"趟排序后的數組:"); // System.out.println(Arrays.toString(arr));if (!flag) {//某一趟排序中一次都沒有交換過break;} else {flag = false;//很重要//重置,進行下一次的判斷}}System.out.println(Arrays.toString(arr));} }選擇排序
基本介紹
選擇式排序也屬于內部排序法,是從欲排序的數據中,按指定的規則選出某一元素,再依規定交換位置后達到排序的目的。
基本思想:
圖解:
代碼
插入排序
基本介紹:
插入式排序屬于內部排序法,是對于欲排序的元素以插入的方式找尋該元素的適當位置,以達到排序的目的。
插入排序法思想:
插入排序的基本思想是:把n個待排序的元素看成為一個有序表和一個無序表,開始時有序表中只包含一個元素,無序表中包含有n-1個元素,排序過程中每次從無序表中取出第一個元素,把它的排序碼依次與有序表元素的排序碼進行比較,將它插入到有序表中的適當位置,使之成為新的有序表。
圖解:
代碼:
希爾排序
基本介紹:
希爾排序是希爾(Donald Shell)于1959年提出的一種排序算法。希爾排序也是一種插入排序,它是簡單插入排序經過改進之后的一個更高效的版本,也稱為縮小增量排序。
基本思想:
希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序算法排序;隨著增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,算法便終止。
圖解:
代碼:
- 交換法:
- 移動法
快速排序
快速排序(Quicksort)是對冒泡排序的一種改進。基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列
示意圖:
代碼:
歸并排序
歸并排序介紹:
歸并排序(MERGE-SORT)是利用歸并的思想實現的排序方法,該算法采用經典的分治(divide-and-conquer)策略(分治法將問題分(divide)成一些小的問題然后遞歸求解,而治(conquer)的階段則將分的階段得到的各答案"修補"在一起,即分而治之)。
思想示意圖
說明:
可以看到這種結構很像一棵完全二叉樹,本文的歸并排序我們采用遞歸去實現(也可采用迭代的方式去實現)。分階段可以理解為就是遞歸拆分子序列的過程。
代碼:
package sort; /** @Author: Min* @Date: 2021/10/7* @description*/import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date;public class MergetSort {public static void main(String[] args) { // int arr[] ={8,4,5,7,1,3,6,2}; // int temp[] = new int[arr.length];//歸并排序需要一個額外的空間 // mergeSort(arr,0,arr.length-1,temp); // System.out.println(Arrays.toString(arr));//給定80000個數據測試int[] arr = new int[80000];int temp[] = new int[arr.length];//歸并排序需要一個額外的空間for (int i = 0; i < 80000; i++) {arr[i] = (int) (Math.random() * 8000000);//生成[0,8000000)的隨機數組}Date start = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String s = sdf.format(start);mergeSort(arr,0,arr.length-1,temp );System.out.println(Arrays.toString(arr));Date end = new Date();String e = sdf.format(end);System.out.println("排序前:" + s);System.out.println("排序后:" + e);}//分+合的方法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;//指向temp數組的當前索引//先把左右兩邊(有序)的數據按照規則填充到temp數組//直到左右兩邊的有序數組序列右一邊全部處理完畢為止while (i <= mid && j <= right) {//如果左邊的有序序列的當前元素,小于等于右邊有序序列的當前元素//即將左邊的當前元素,//然后t++,i++if (arr[i] <= arr[j]) {temp[t] = arr[i];t ++;i ++;} else {temp[t] = arr[j];t ++;j ++;}}//把剩余數據的一邊的數據依次全部填充到tempwhile (i <= mid) {//左邊的有序序列還有剩余元素,就全部填充到temptemp[t] = arr[i];t ++;i ++;}while (j <= right) {//右邊的有序序列還有剩余的元素,就全部填充到temptemp[t] = arr[j];t ++;j ++;}//將temp數組的一邊數據一次全部填充到temp//注意:并不是每次都拷貝所有t = 0;int tempLeft = left;while (tempLeft <= right) {//arr[tempLeft] = temp[t];t ++;tempLeft ++;}}}基數排序
基數排序(桶排序)介紹:
基本思想:
將所有待比較數值統一為同樣的數位長度,數位較短的數前面補零。然后,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以后, 數列就變成一個有序序列。
示意圖:
第一輪:
第二輪:
第三輪:
一共要多少輪?取決于數據里面最大數的位數
代碼:
package sort; /** @Author: Min* @Date: 2021/10/9* @description*/import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date;import static java.lang.Math.pow;public class RadixSort {public static void main(String[] args) { // int arr[] = {53,3,542,748,14,214,8452,2366,961212545,45461321}; // radixSort(arr); // System.out.println(Arrays.toString(arr));//給定80000個數據測試int[] arr = new int[80000];int temp[] = new int[arr.length];for (int i = 0; i < 80000; i++) {arr[i] = (int) (Math.random() * 8000000);//生成[0,8000000)的隨機數組}Date start = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String s = sdf.format(start);radixSort(arr);System.out.println(Arrays.toString(arr));Date end = new Date();String e = sdf.format(end);System.out.println("排序前:" + s);System.out.println("排序后:" + e);}//基數排序public static void radixSort(int[] arr) {//代碼簡化int[][] bucket = new int[10][arr.length];//為了記錄每個桶中實際存放了多少數據,定義一個一維數組來記錄各個桶每次放入的數據是個數//*原來桶中的數據不會刪除,新數據覆蓋.....int[] bucketElementCounts = new int[10];//得到數組中的最大位數的值int max = arr[0];for (int i = 1; i < arr.length; i++) {if (arr[i] > max) {max = arr[i];}}//得到最大位數int length = (max + "").length();for (int j = 0; j < length; j++) {for (int i = 0;i < arr.length;i ++) {int digitOfElement = arr[i] / (int)(pow(10,j)) % 10 ;//放入到對應的桶中bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];bucketElementCounts[digitOfElement] ++ ;//bucketElementCounts[digitOfElement]初始化為0} // for (int i = 0,n = 1; i < arr.length; i++,n *= 10) { // int digitOfElement = arr[i] / (int)(pow(10,j)) % 10 ; // //放入到對應的桶中 // bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i]; // bucketElementCounts[digitOfElement] ++ ;//bucketElementCounts[digitOfElement]初始化為0 // }//按照桶的順序,將數據放回數組int index = 0;//遍歷每一個桶,并將桶中的數據,放入到原數組for (int k = 0;k < bucketElementCounts.length;k ++) {//如果桶中有數據才放入到原數組if (bucketElementCounts[k] != 0) {//說明有數據//此時循環放入數據for (int l = 0;l < bucketElementCounts[k];l ++) {//取出元素放到arrarr[index] = bucket[k][l];//第k個桶的l個元素index++;}}bucketElementCounts[k] = 0;//置0} // System.out.println("第"+j+"輪:" + Arrays.toString(arr));}// //定義一個二維數組,表示10個桶,每個桶就是一個一維數組 // //*為了防止溢出,每個一維數組的大小為 arr.length // int[][] bucket = new int[10][arr.length]; // //為了記錄每個桶中實際存放了多少數據,定義一個一維數組來記錄各個桶每次放入的數據是個數 // //*原來桶中的數據不會刪除,新數據覆蓋..... // int[] bucketElementCounts = new int[10]; // // //第一輪 ——對個位進行排序 // for (int i = 0;i < arr.length;i ++) { // //取出個位 // int digitOfElement = arr[i] % 10;//得到元素個位的值 // //放入到對應的桶中 // bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i]; // bucketElementCounts[digitOfElement] ++ ;//bucketElementCounts[digitOfElement]初始化為0 // } // //按照桶的順序,將數據放回數組 // int index = 0; // //遍歷每一個桶,并將桶中的數據,放入到原數組 // for (int k = 0;k < bucketElementCounts.length;k ++) { // //如果桶中有數據才放入到原數組 // if (bucketElementCounts[k] != 0) {//說明有數據 // //此時循環放入數據 // for (int l = 0;l < bucketElementCounts[k];l ++) { // //取出元素放到arr // arr[index] = bucket[k][l];//第k個桶的l個元素 // index++; // } // } // bucketElementCounts[k] = 0;//置0 // } // System.out.println("第一輪:" + Arrays.toString(arr)); // //第二輪 ——對個位進行排序 // for (int i = 0;i < arr.length;i ++) { // //取出十位 // int digitOfElement = arr[i] /10 % 10;//得到元素十位的值 // //放入到對應的桶中 // bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i]; // bucketElementCounts[digitOfElement] ++ ;//bucketElementCounts[digitOfElement]初始化為0 // } // //按照桶的順序,將數據放回數組 // index = 0; // //遍歷每一個桶,并將桶中的數據,放入到原數組 // for (int k = 0;k < bucketElementCounts.length;k ++) { // //如果桶中有數據才放入到原數組 // if (bucketElementCounts[k] != 0) {//說明有數據 // //此時循環放入數據 // for (int l = 0;l < bucketElementCounts[k];l ++) { // //取出元素放到arr // arr[index] = bucket[k][l];//第k個桶的l個元素 // index++; // } // } // bucketElementCounts[k] = 0;//置0 // } // System.out.println("第二輪:" + Arrays.toString(arr)); // //第三輪 ——對個位進行排序 // for (int i = 0;i < arr.length;i ++) { // //取出百位 // int digitOfElement = arr[i] /100 % 10;//得到元素百位的值 // //放入到對應的桶中 // bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i]; // bucketElementCounts[digitOfElement] ++ ;//bucketElementCounts[digitOfElement]初始化為0 // } // //按照桶的順序,將數據放回數組 // index = 0; // //遍歷每一個桶,并將桶中的數據,放入到原數組 // for (int k = 0;k < bucketElementCounts.length;k ++) { // //如果桶中有數據才放入到原數組 // if (bucketElementCounts[k] != 0) {//說明有數據 // //此時循環放入數據 // for (int l = 0;l < bucketElementCounts[k];l ++) { // //取出元素放到arr // arr[index] = bucket[k][l];//第k個桶的l個元素 // index++; // } // } // // } // System.out.println("第三輪:" + Arrays.toString(arr));} }相關術語解釋
總結
以上是生活随笔為你收集整理的java 数据结构和算法 排序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华为云鲲鹏云服务器系列的规格,#化鲲为鹏
- 下一篇: Untiy导入package时报错