Java內(nèi)部排序之插入排序與交換排序詳解
內(nèi)部排序
排序:將任意序列重新排列按照關(guān)鍵字有序;
排序根基存儲(chǔ)器的不同分為:內(nèi)部排序、外部排序;(這里指的都是內(nèi)部排序)
排序根據(jù)關(guān)鍵字分為:穩(wěn)定排序、不穩(wěn)定排序
排序根據(jù)不同的原則分為:插入排序、交換排序、選擇排序、歸并排序、基數(shù)排序;
排序根據(jù)時(shí)間復(fù)雜度分為:簡(jiǎn)單排序(O(N2))、先進(jìn)排序(O(nlogn))、基數(shù)排序(O(d*n))
排序的操作:比較、移動(dòng) 被稱(chēng)為基于比較的排序;
假設(shè)這里使用的存儲(chǔ)結(jié)構(gòu)均為:順序存儲(chǔ)結(jié)構(gòu);
內(nèi)部排序:
1、插入排序:直接插入排序、折半插入、2-路插入排序、希爾排序
2、交換排序:冒泡排序、快速排序
3、選擇排序:簡(jiǎn)單選擇排序、堆排序
4、歸并排序
5、基數(shù)排序
插入排序
1直接插入排序
由n-1趟排序組成; 對(duì)于P=1趟到P=N-1趟,插入排序保證從位置0到位置P上的元素為已排序狀態(tài);
第P趟,我們將位置P上的元素向左移動(dòng)到它在前P+1個(gè)元素中正確位置上。(這里需要使用一個(gè)哨兵元素)
直接插入程序示例:
public class InserSort { public static void insertSort(int a[]){ int temp;//哨兵 int i;//執(zhí)行趟數(shù) int j; for (i = 1; i < a.length; i++) {//執(zhí)行N-1趟 temp=a[i]; for ( j = i-1; (j >=0)&&(a[j]>temp); j--) { a[j+1]=a[j]; } a[j+1]=temp; } } public static void main(String[] args) { int a[]={9,6,3,2,7,5}; InserSort.insertSort(a); for (int i = 0; i < a.length; i++) { System.out.print(a[i]); } } }
算法分析:
排序的操作:比較、移動(dòng)
1、空間復(fù)雜度: 只需要一個(gè)記錄的輔助空間;
2、時(shí)間復(fù)雜度:O(N2) 如果已有序時(shí)間復(fù)雜度為:O(N)
3、適應(yīng)場(chǎng)景:數(shù)據(jù)量不是很大 、基本有序
2 折半插入排序
將查找工作交與“折半查找”來(lái)實(shí)現(xiàn)。定位后,后續(xù)移動(dòng)元素。
public class BInsertSort { /** * 折半插入排序 * @param a */ public static void BinsertSort(int a[]){ int temp;//哨兵 for (int i = 1; i < a.length; i++) { temp=a[i]; int low=0; int hight=i-1; //查詢(xún) while (low<=hight) { int mad=(low+hight)/2; if (a[mad]>a[i]) { hight=mad-1; } if (a[mad]<a[i]) { low=mad+1; } } //移動(dòng) for (int k = i-1; k >=hight+1; k--) { a[k+1]=a[k]; } a[hight+1]=temp; } } public static void main(String[] args) { int a[]={9,6,3,2,7,5}; BInsertSort.BinsertSort(a); for (int i = 0; i < a.length; i++) { System.out.print(a[i]); } } }
折半插入排序減少關(guān)鍵字的比較次數(shù),但是記錄的移動(dòng)次數(shù)沒(méi)有改變。
時(shí)間復(fù)雜度:O(N2)
3 希爾排序(縮小增量排序)
基本思想:先將整個(gè)待排序記錄序列分割成為若干子序列分別進(jìn)行直接插入排序,待整個(gè)序列中的記錄“基本有序”時(shí),在對(duì)整個(gè)記錄進(jìn)行一次直接插入排序。
希爾排序使用一個(gè)序列,叫做增量序列。
序列最終值為1 PS:增量序列中的值沒(méi)有除1之外的公因子,并且最后一個(gè)增量必須等于1;
算法示例: 增量序列最流行的選擇是使用shell建議的序列N/2
public class ShellSort { /** * 希爾排序 * @param a */ public static void shellSort(int a[]){ int i,j; int inc;//增量 int temp;//哨兵 for ( inc = a.length/2; inc >0; inc/=2) {//設(shè)置增量隊(duì)列 //一趟希爾排序 for ( i = inc; i < a.length; i++) { temp=a[i]; for ( j = i; j >= inc; j-=inc) { if (temp<a[j-inc]) { a[j]=a[j-inc]; }else { break; } } a[j]=temp; } } } public static void main(String[] args) { int a[]={9,6,3,2,7,5}; shellSort(a); for (int i = 0; i < a.length; i++) { System.out.print(a[i]); } } }
算法分析: 希爾排序的運(yùn)算時(shí)間依賴(lài)于增量序列的選擇,最壞情況O(N2)
使用場(chǎng)景: 適度地大量的輸入數(shù)據(jù)
交換排序
交換排序:冒泡排序、快速排序
1 冒泡排序
思路: 一趟排序:將第一個(gè)記錄的關(guān)鍵字與第二個(gè)關(guān)鍵字進(jìn)行比較,若為逆序記錄交換,然后比較第二個(gè)與第三個(gè),以此類(lèi)推,直到地N-1個(gè)記錄與N個(gè)記錄比較位置。
其結(jié)果使得最大的記錄安置在最后一個(gè)記錄位置; 第二趟對(duì)n-1個(gè)記錄進(jìn)行冒泡排序。
判斷冒泡排序結(jié)束的條件:在一趟排序中沒(méi)有進(jìn)行過(guò)交換記錄的操作。 算法演示:
public class BubbleSort { /** * 冒泡排序 * @param a */ public static void bubbleSort(int a[]){ int temp=0; int log=0;//1表示有交換 0表示沒(méi)有交換 作為結(jié)束標(biāo)識(shí) for (int i = a.length-1; i >1; i--) { for (int j = 0; j < i; j++) { if (a[j+1]<a[j]) { temp=a[j+1]; a[j+1]=a[j]; a[j]=temp; log=1; } } if (log==0) { break; } } } public static void main(String[] args) { int a[]={2,3,5,6,7,9}; BubbleSort.bubbleSort(a); for (int i = 0; i < a.length; i++) { System.out.print(a[i]); } } }
算法分析: 時(shí)間復(fù)雜度:O(N2)
2快速排序
快速排序(Quick Sort)的基本思想:通過(guò)一趟排序?qū)⒋庞涗浄指畛瑟?dú)立的兩部分,其中一部分記錄的關(guān)鍵字均比另一部分記錄的關(guān)鍵字小,則可分別對(duì)這兩部分記錄繼續(xù)進(jìn)行排序,以達(dá)到整個(gè)序列有序。
首先選擇一個(gè)記錄(可選擇第一個(gè)記錄,不是最佳的做法)作為支點(diǎn)(pivot),然后按下述原則重新排列其余記錄:將所有關(guān)鍵字較它小的記錄都安置在它的位置之前,將所有關(guān)鍵字較它大的記錄都安置在它位置之后。
由此可以該支點(diǎn)記錄最后所落的位置i作為分界線,這就是一趟快速排序。 具體做法:附設(shè)兩個(gè)指針low和high,它們的初值分別指向序列的前端與后端,設(shè)支點(diǎn)的記錄關(guān)鍵字為pivotkey,則首先從high所指位置起向前搜索找到第一個(gè)關(guān)鍵字小于pivotkey的記錄和支點(diǎn)記錄交換,然后從low所指位置起向后搜索,找到第一個(gè)關(guān)鍵字大于pivotkey的記錄和支點(diǎn)記錄相互交換,重復(fù)這兩步直到low=high為止。
算法示例:
public class QuickSort { /** * 一趟快排序 */ public static int partition(int a[],int low ,int high) { int temp = a[low];// 支點(diǎn) while (low < high) { while ((low < high)&&(a[high] >= temp) ) { high--; } a[low]=a[high]; while ((low < high)&&(a[low] <= temp)) { low++; } a[high]=a[low]; } a[low]=temp; return low; } /** * 遞歸排序 * @param a * @param low * @param high */ public static void QSort(int a[],int low,int high){ //對(duì)子序列進(jìn)行排序 if (low<high) { int pivot=partition(a, low , high); QSort(a,low, pivot-1); QSort(a,pivot+1,high); } } /** * 快速排序 * @param a */ public static void QuickSort(int a[]){ QSort(a, 0, a.length-1); } //測(cè)試 public static void main(String[] args) { int a[]={7,6,3,8,4,9,7}; QuickSort.QuickSort(a); for (int i = 0; i < a.length; i++) { System.out.print(a[i]); } } }
算法分析:
1、時(shí)間復(fù)雜度: 快速排序的平均時(shí)間為:O(nlogn) 就平均時(shí)間而言,快排序是目前被認(rèn)為最好的一種內(nèi)部排序方法; 較壞的情況:當(dāng)關(guān)鍵字基本有序或者有序時(shí),快速排序?qū)⑼凶優(yōu)槊芭菖判颍瑫r(shí)間復(fù)雜度:O(n2); 支點(diǎn)的選取可以?xún)?yōu)化該算法,一般認(rèn)為“三者取中”的做法最合適:即a[i],a[j],a[(i+j)/2],取三者的中值。這樣可以改善最壞情況下的性能。
2、空間復(fù)雜度: 平均空間復(fù)雜度為:O(log2N) 因?yàn)榭焖倥判蛐枰粋€(gè)??臻g來(lái)實(shí)現(xiàn)遞歸。 一般如果一趟排序記錄均勻分布在支點(diǎn)兩側(cè),棧的深度為log2N+1 最壞情況,一趟排序后,都跑到一端空間,深度為N 降低空間復(fù)雜度的方法: 在一趟排序之后比較分割所得兩部分的長(zhǎng)度,且先對(duì)長(zhǎng)度短的子序列中的記錄進(jìn)行快速排序,則棧的最大深度可降為O(logn).
到此這篇關(guān)于Java內(nèi)部排序之插入排序與交換排序詳解的文章就介紹到這了,更多相關(guān)Java插入排序與交換排序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis注解開(kāi)發(fā) 一對(duì)多嵌套查詢(xún)方式
這篇文章主要介紹了mybatis注解開(kāi)發(fā) 一對(duì)多嵌套查詢(xún)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2023-03-03FastDFS分布式文件系統(tǒng)環(huán)境搭建及安裝過(guò)程解析
這篇文章主要介紹了FastDFS分布式文件系統(tǒng)環(huán)境搭建及安裝過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08Java實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出的步驟詳解
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)Excel的導(dǎo)入、導(dǎo)出,文中示例代碼介紹的非常詳細(xì),對(duì)我們的學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴們可以參考一下2023-06-06使用Spring AOP實(shí)現(xiàn)MySQL數(shù)據(jù)庫(kù)讀寫(xiě)分離案例分析(附demo)
分布式環(huán)境下數(shù)據(jù)庫(kù)的讀寫(xiě)分離策略是解決數(shù)據(jù)庫(kù)讀寫(xiě)性能瓶頸的一個(gè)關(guān)鍵解決方案,這篇文章主要介紹了使用Spring AOP實(shí)現(xiàn)MySQL數(shù)據(jù)庫(kù)讀寫(xiě)分離案例分析(附demo),有興趣的可以了解一下。2017-01-01java基于遞歸算法實(shí)現(xiàn)漢諾塔問(wèn)題實(shí)例
這篇文章主要介紹了java基于遞歸算法實(shí)現(xiàn)漢諾塔問(wèn)題,結(jié)合具體實(shí)例形式分析了java遞歸算法的實(shí)現(xiàn)與使用技巧,需要的朋友可以參考下2017-07-07