日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

6、java中的排序算法

發布時間:2023/12/3 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 6、java中的排序算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1、簡介

排序是將元素按著指定關鍵字的大小遞增或遞減進行數據的排列,排序可以提高查找的效率

2、排序算法的分類

排序算法可大致分為四類七種,具體分類如下:

? ? ? ?插入排序:直接插入排序、希爾排序

? ? ? ?交換排序:冒泡排序、快速排序

? ? ? ?選擇排序:直接選擇排序、堆排序

? ? ? ?歸并排序

3、插入排序

算法思想:每次將一個元素按著關鍵字大小插入到他前面已經排好序的子序列中,重復進行這個操作,直至插入所有數據。

3、1 直接插入排序

算法描述:假設前i個元素構成的子序列是有序的,然后根據關鍵字大小順序將第i+1個元素a[i] 添加到已經排好序的子序列中,使得插入a[i]后的序列仍是一個有序序列,這樣當元素都插入序列時,則排序完成。

算法實現:

/*** 直接插入排序* @author chaizepeng*/private static void directInsertSort1(int[] array) {for (int i = 1; i < array.length; i++) {int temp = array[i];//拿到下一個需要插入的數值for (int j = i-1; j >= 0; j--) {//遍歷已經排好序的集合//將想要插入的元素和已經排好序的集合中的每一個進行比較if (temp < array[j]) {array[j+1] = array[j];}else {array[j+1] = temp;break;}}}}/*** 直接插入排序* @author chaizepeng*/private static void directInsertSort2(int[] array) {for (int i = 1; i < array.length; i++) {int temp = array[i];//拿到下一個需要插入的數值int j ;for (j = i-1; j >= 0 && temp < array[j]; j--) {//遍歷已經排好序的集合//將想要插入的元素和已經排好序的集合中的每一個進行比較array[j+1] = array[j];}array[j+1] = temp;}}

算法分析:

假設一個序列{1,2,3,4,5},使用直接插入排序時,每個個元素需要比較1次,移動兩次(現將第i個給temp,再將第temp給第i個)即可,則完成排序總需比較n-1次,移動2(n-1)次,時間復雜度是O(n)

再假設一個序列{5,4,3,2,1},使用直接插入排序時,第i個元素準備插入時,需要比較i-1次,移動2+(i-1)次(前一個元素往后移動一次,第i元素先給temp,然后temp再給第1個元素)也就是i+1次,所以n個元素總共許比較n(n-1)/2次,移動(n-1)(n+4)/2次,所以時間復雜度為O(n^2)

所以插入排序算法的時間復雜度在O(n)到O(n^2)之間,其排序效率與比較次數和元素長度直接相關。

3、2 希爾排序

算法描述:

代碼實現:

/*** 希爾排序* @author chaizepeng*/private static void shellSort1(int[] array) {int len = array.length;int i,j,gap;//步長每次除以2,使用步長來對數組進行分組//步長的直接意思就是,每隔len個元素則為同一組元素,步長在數值上等于組數for (gap = len / 2 ; gap > 0 ; gap /= 2){//因為每次都是除以2,所以當步長越小時,每組中的元素越多,越接近于有序//這里是遍歷根據步長分割的每一組for (i = 0 ; i < gap ; i++){//將每組中的元素進行排序,j是在i+gap開始的,因為每隔gap個數便是同一組數據//這是遍歷每組中的每一個數據,組內數據進行比較,直接插入排序//i+gap正好是獲取到數組中的每一個數據for (j = i + gap ; j < len ; j += gap){if(array[j] < array[j - gap]){int temp = array[j];int k = j - gap;while (k >= 0 && array[k] > temp){array[k + gap] = array[k];k -= gap;}array[k + gap] = temp;}}}}}/*** 希爾排序* @param array*/private static void shellSort2(int[] array){int len = array.length;int j , gap;for (gap = len / 2 ; gap > 0 ; gap /= 2){for (j = gap ; j < len ; j++){if (array[j] < array[j - gap]){int temp = array[j];int k = j - gap;while (k >= 0 && array[k] > temp){array[k + gap] = array[k];k -= gap;}array[k + gap] = temp;}}}}/*** 希爾排序* @param array*/private static void shellSort3(int[] array) {int i,j,gap;int len = array.length;for (gap = len / 2 ; gap > 0 ; gap /= 2){for (i = gap ; i < len ; i++){for (j = i - gap ; j>= 0 && array[j] > array[j+gap] ; j -= gap){int temp = array[j];array[j] = array[j+gap];array[j+gap] = temp;}}}}

?算法分析:

因為根據之前對直接插入排序的分析可以知道直接插入排序算法的效率與比較次數和元素長度直接相關,可以看出希爾排序是對直接插入排序算法做了優化,針對的就是比較次數和元素的長度,希爾排序講究先分組排序,這樣就見效了移動的次數,隨著元素長度的增加,序列趨于有序,減少了比較的次數,降低了時間復雜度。另外,希爾排序的時間復雜度是O(n*(logn)^2)。

排序算法的穩定性:是對關鍵字相同的元素排序性能的描述,當兩個元素A,B相等,排序之前A在B前邊,如果排完排完序之后,A仍然在B前邊,則說明排序算法是穩定的。自我感覺分析一個算法穩定還是不穩定是可行的,但是如果說一個算法是穩定還是不穩定的應該不準確吧?對于穩定性而言是不是就是對臨界元素的一種處理方式而已呢?不必糾結。

?4、交換排序?

4、1 冒泡排序

算法描述:假設按著升序排序,就是從第一個元素開始比較相鄰兩個元素的值,如果前邊的比后邊的大,則交換兩個值得位置,然后繼續往后進行比較交換操作,一趟下來使得序列中最大的數在最后邊,假設長度是n,則第一趟將最大的數放在第n個位置上,然后再進行第二趟,第二趟下來之后保證除第n個數之外最大數在第n-1的位置,然后繼續下一趟,重復上邊操作,直到不需要繼續元素交換了便排序結束了。

算法實現:

/*** 冒泡排序* @author chaizepeng*/ private static void bubbleSort1(int[] array) {int len = array.length;//控制比較的長度,最長len-1for (int i = len-1; i > 0; i--) {//第二層循環控制比較大小和交換位置for (int j = 0; j < i; j++) {if (array[j] > array[j+1]) {//如果前一個數比后一個數大,則交換位置int temp = array[j];array[j] = array[j+1];array[j+1] = temp;}}System.out.println("共有"+len+"個元素,這是第"+(len - i)+"趟");} }/*** 冒泡排序* @author chaizepeng*/ private static void bubbleSort2(int[] array) {int len = array.length;//外層循環控制遍歷趟數,最多循環len-1次for (int i = 0; i < len-1; i++) {for (int j = 0; j < len - 1 - i; j++) {//第二層循環控制比較大小和交換位置if (array[j] > array[j+1]) {//如果前一個數比后一個數大,則交換位置int temp = array[j];array[j] = array[j+1];array[j+1] = temp;}}System.out.println("共有"+len+"個元素,這是第"+(i+1)+"趟");} }/*** 冒泡排序使用添加標記的方式進行優化,序列越接近有序,效率越高* @author chaizepeng*/ private static void bubbleSort3(int[] array) {int len = array.length;//標記boolean flag;//外層循環控制遍歷趟數,最多循環len-1次for (int i = 0; i < len-1; i++) {flag = true;for (int j = 0; j < len - 1 - i; j++) {//第二層循環控制比較大小和交換位置if (array[j] > array[j+1]) {//如果前一個數比后一個數大,則交換位置int temp = array[j];array[j] = array[j+1];array[j+1] = temp;flag = false;}}System.out.println("共有"+len+"個元素,這是第"+(i+1)+"趟");//如果沒有進行數據交換,也就是flag==trueif (flag) {break;//不再繼續下一趟}} }

算法分析:冒泡排序記住一點,只要沒有元素交換了,則排序完成。

?

例如序列{1,2,3,4,5},第一趟比較4次,沒有元素移動,時間復雜度O(n)

再例如序列{5,4,3,2,1},第一趟比較4次,移動12次,時間復雜度為O(n^2)

所以冒泡排序算法的時間復雜度在O(n)到O(n^2)之間,其時間復雜度與序列是否有序有直接關系,并且算法是穩定的。此算法每次都借助了一個臨時的中間元素,用來交換兩個數。

4、2 快速排序

算法描述:快速排序是對冒泡排序的優化,快速排序第一趟就根據某個元素將序列分成兩部分,一部分中所有數據比此元素小,另一部分的所有數比此元素大或等于此元素;然后再對這兩部分分別進行分割,整個過程可以遞歸進行,直到序列有序。

算法實現:

/*** 看圖理解代碼,一下子就全明白了* @author chaizepeng*/ private static void quickSort1(int[] array) {int i = 0;int j = array.length - 1;quickSort(i,j,array); }/*** 遞歸實現快排算法* @author chaizepeng*/ private static void quickSort(int i, int j, int[] array) {int left = i;int right = j;int key = array[i];while (i < j) {//只要不相等就循環比較,交換操作//從右往左遍歷while(i < j && array[j] >= key) {j--;}if (array[j] < key) {//每次交換的都是一個小于key的元素和key所在位置上的值,也就是key值int temp = array[i];array[i] = array[j];array[j] = temp;}//從左往右遍歷while(i < j && array[i] <= key) {i++;}//交換i處和j處的數據if (array[i] > key) {//每次交換的都是一個大于key的元素和key所在位置上的值,也就是key值int temp = array[i];array[i] = array[j];array[j] = temp;}}//遞歸使用if (i > left) {quickSort(0, i, array);}if (j < right) {quickSort(j + 1, array.length-1, array);} }

算法分析:

?這里寫的快排是不穩定,快速排序算法效率受標記值(基準值)的影響,假如每次選擇的基準值都是最值的話,那么就會導致被分割的子序列是不平衡的(一個里邊就一個元素,另一個里邊是其余的n-1個元素),則需要比較的趟數就會增加,導致算法效率低下,快排的時間復雜度在O(nlogn)到O(n^2)之間。所以想要提高快排的效率就要保證每次找的基準值是當前序列的中間值。

快排的空間復雜度在O(logn)到O(n)之間

5、選擇排序

5、1 直接選擇排序

算法描述:以升序為例:從第一個元素開始,依次比較序列中的元素,將最小的元素放在第一個位置;接著在第二個元素開始,依次比較序列中的值,將最小的元素放在第二個位置,依次類推,直到排序結束。

算法實現:

/*** 以升序為例:從第一個元素開始,依次比較序列中的元素,將最小的元素放在第一個位置;接著在第二個元素開始,依次比較序列中的值,將最小的元素放在第二個位置,依次類推,直到排序結束* 直接選擇排序* @author chaizepeng*/ private static void straightSelectSort(int[] array) {//控制比較的趟數for (int i = 0; i < array.length - 1; i++) {int minFlag = i;//記錄一下最小值所在的下標for (int j = i+1; j < array.length; j++) {//控制從何處開始比較,比較到何處結束if (array[j] < array[minFlag]) {//比較當前值和之前記錄的最小下標對應的值minFlag = j;}}//將最小值放在最前邊if (minFlag != i) {int temp = array[minFlag];//最小值array[minFlag] = array[i];//將當前值賦給最小值所在的位置array[i] = temp;//當前位置放最小值}} }

算法分析:

直接選擇排序,最多需要n-1趟,并且每一趟都需進行n-i此的比較,所以時間復雜度是O(n^2),并且直接選擇排序是不穩定的算法。自我感覺這是最容易理解和實現的排序算法。

5、2 堆排序

算法描述:堆排序是對直接選擇排序的一種優化,直接選擇排序算法在每一趟比較兩個數大小時, 只是比較大小沒有做任何操作, 而堆排序針對此處做了優化,他在比較的同時也將其他的元素(不是最小的元素)做了相應調整。堆排序是先使用待排序的序列構建一個堆,這里用大頂堆來實現,然后根據大頂堆的特點,將根結點元素放到最后(這里實現升序排序),然后將剩下的元素再構成一個大頂堆,依次進行,直到堆的長度為1結束排序。

說一下什么是堆,堆是一種數據結構,首先是一個完全二叉樹(有右子樹必有左子樹的二叉樹),另外,這個完全二叉樹的各個結點上的數值從根結點到每一個葉子結點都會有一種規律:
?? ? 父結點一定大于或者等于其孩子結點,這樣的稱為大頂堆
?? ? 父結點一定小于或者等于其孩子結點,這樣的稱為小頂堆

算法實現:

/*** * 堆排序:是對直接選擇排序的一種優化,直接選擇排序算法在每一趟比較兩個數大小時, 只是比較大小沒有做任何操作,* 而堆排序針對此處做了優化,他在比較的同時也將其他的元素(不是最小的元素)做了相應調整。堆排序是先使用待排序的序列構建一個堆,這里用大頂堆來實現,* 然后根據大頂堆的特點,將根結點元素放到最后(這里實現升序排序),然后將剩下的元素再構成一個大頂堆,依次進行,直到堆的長度為1結束排序。* 存儲堆時的數據下標在1開始,而不是0,因為可以直接使用二叉樹的性質進行堆的構建 * @author chaizepeng*/ private static void heapSort(int[] array) {//這里已經-1了int len = array.length-1;//用待排序的序列構建一個大頂堆,因為排序是借助堆結構進行的,這里相當于初始化堆//存儲序列的數組下標必須在1開始,根據平衡二叉樹的順序存儲特性可以知道,len/2之后的是葉子節點,而len/2以及它前邊的結點是存在子結點的//依次遍歷每一個存在子結點的結點,然后進行判斷、交換結點,使得構建一個堆//1、初始化最大堆for (int i = len/2; i > 0; i--) {heapAdjust(array, i, len);}for (int i = 1; i < array.length; i++) {System.out.print(array[i]+" ");}System.out.println("--------------------------");//2、交換根結點和最后一個結點位置,將剩下的重新構建一個堆for (int i = len; i > 1; i--) {//交換元素int temp = array[i];array[i] = array[1];array[1] = temp;heapAdjust(array, 1, i-1);} }/*** 構建大頂堆* 什么是堆:* 堆是一種數據結構,首先是一個完全二叉樹(有右子樹必有左子樹的二叉樹),另外,這個完全二叉樹的各個結點上的數值從根結點到每一個葉子結點都會有一種規律:* 父結點一定大于或者等于其孩子結點,這樣的稱為大頂堆* 父結點一定小于或者等于其孩子結點,這樣的稱為小頂堆* @author chaizepeng*/ private static void heapAdjust(int[] array, int i, int len) {//遍歷當前操作結點對應的子結點int j ;for (j = i * 2; j <= len ; j *= 2) {//記錄一下當前操作的結點int temp = array[i];//子結點的左孩子和右孩子進行比較//這里為什么要比較一下呢?假設子結點比父節點大,則需要上浮子結點,但是有可能有左右兩個結點,這里比較一下,確定哪一個結點上浮//此處j < len 必須要加if (j < len && array[j] < array[j+1]) {++j;}//如果當前操作的結點 > 子結點 ,不操作if (temp >= array[j]) {break;}//交換父子結點的值array[i] = array[j];array[j] = temp;//將需要判斷的元素下標改成下降的元素下標,用于與其子節點進行判斷i = j;} }

?算法分析:

?堆排序的時間復雜度是O(n㏒n),算法是不穩定的。堆排序算法充分利用了完全二叉樹的性質,效率高,比較復雜,不容易理解,比較適合元素多的序列排序使用。

6、歸并排序

算法描述:歸并排序使用的是算法設計中分治的思想,分而治之,然后合并,將小的子序列進行排序,然后慢慢的將有序的子序列進行合并,最終使得整個序列有序,這里介紹的是二路歸并算法,也就是每次只將兩個子序列進行歸并。具體操作是這樣的,每次是將兩個序列A、B進行合并,這里假設這兩個序列是有序的,首先初始一個長度為兩個序列長度之和的容器,然后聲明兩個標記位i,j,i指向序列A的第一個元素,j指向序列B的第一個元素,然后比較兩個數組中標記位上數的大小,哪一個小就將標記位上的數放到初始的容器中,然后將標記位指向下一個元素,然后直至其中一個序列中的元素已被移動完畢,則將另一個序列中的元素復制到容器中,排序完畢,這只是核心的兩個序列歸并邏輯。

算法實現:

public static void main(String[] args) {int []array = {24, 27 ,41, 44, 19, 47 ,50 ,5,65, 93 ,94 };for (int i = 0; i < array.length; i++) {System.out.print(array[i]+" ");}System.out.println();System.out.println("-----------------------------");//從第一個元素開始,第一次歸并時,每一個歸并的序列長度是1(默認每一個序列長度為1的序列是有序的)mergeSort(array,1);for (int i = 0; i < array.length; i++) {System.out.print(array[i]+" ");} }/*** * @author chaizepeng** @param array 排序總序列* @param len 要歸并的序列的長度*/ private static void mergeSort(int[] array ,int len) {//序列{6,5,8,4,7,9,2,1,4}要進行歸并,第一次歸并時需要將相鄰的兩個元素進行排序歸并,分組如下//6,5,8,4,7,9,2,1,4//此時,需要比較兩個相鄰的元素,所以,就需要進行分組和排序//6,5 8,4 7,9 2,1 4//需要先判斷一下要分幾個組int count = array.length / (len * 2);//判斷一下count的值,如果=0的話,則說明len*2已經大于序列總長度,這時序列已經排序完成,結束即可if (count == 0) {return;}//然后依次歸并for (int i = 0; i < count; i++) {//i*len 第一個序列的第一個元素位置//len 有序序列長度//(2+i)*len 第二個序列的最后一個元素位置(不包括)merge(array,i*len*2,(i+1)*len*2,len);}//在這里判斷一下是否歸并正好兩兩對應,如果有剩下的,則也需要歸并一下(這里是拿剩下的序列組和它的前一組進行歸并操作)int rem = array.length % (len * 2);if (rem != 0) {merge(array, array.length-rem-len, array.length,len);}//進行完一次歸并后,繼續下一次操作//子序列長度為len的已經歸并完成,下一次使用len*2作為長度繼續歸并mergeSort(array,len * 2); }/*** 一次歸并過程* @author chaizepeng** @param array* @param leftBegin* @param rightEnd* @param len*/ private static void merge(int[] array, int leftBegin, int rightEnd,int len) {//標記位,用于復制數組用int flag = leftBegin;//用一個數組來存一下需要合并的序列 int []temp = new int[rightEnd - leftBegin];int leftEnd = leftBegin + len;int rightBegin =leftEnd;//右邊序列開始的位置//標記temp的下標int j = 0;//比較兩個序列中的元素大小,進行填充while (leftBegin < leftEnd && rightBegin < rightEnd) {if (array[leftBegin] > array[rightBegin]) {temp[j++] = array[rightBegin++];}else {temp[j++] = array[leftBegin++];}}//判斷一下前后兩個被比較的序列那個還有元素剩余,則直接復制到temp中while(leftBegin < leftEnd) {temp[j++] = array[leftBegin++];}while(rightBegin < rightEnd) {temp[j++] = array[rightBegin++];}//將temp中的數據填到array中for (int k = 0; k < temp.length; k++) {array[flag+k] = temp[k];} }

?算法分析:

?歸并算法的時間復雜度是O(n㏒n),算法是穩定的,效率較高。

7、性能比較

沒有絕對好的算法,要根據具體的情況來分析那個算法更好,平均情況下快排(個人比較喜歡快排)、堆排序和歸并排序效率高;如果排序的序列基本有序,那么冒泡排序和直接插入排序效率比較高;如果序列基本逆序,則堆排序和歸并排序效率高;在空間復雜度上看,堆排序是最好的。但是快排是最常用的。

附加一張算法指標對比表:

?

總結

以上是生活随笔為你收集整理的6、java中的排序算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。