java堆演算法
㈠ JVM垃圾回收的「三色標記演算法」實現,內容太干
三色標記法是一種垃圾回收法,它可以讓JVM不發生或僅短時間發生STW(Stop The World),從而達到清除JVM內存垃圾的目的。JVM中的 CMS、G1垃圾回收器 所使用垃圾回收演算法即為三色標記法。
三色標記法將對象的顏色分為了黑、灰、白,三種顏色。
白色 :該對象沒有被標記過。(對象垃圾)
灰色 :該對象已經被標記過了,但該對象下的屬性沒有全被標記完。(GC需要從此對象中去尋找垃圾)
黑色 :該對象已經被標記過了,且該對象下的屬性也全部都被標記過了。(程序所需要的對象)
從我們main方法的根對象(JVM中稱為GC Root)開始沿著他們的對象向下查找,用黑灰白的規則,標記出所有跟GC Root相連接的對象,掃描一遍結束後,一般需要進行一次短暫的STW(Stop The World),再次進行掃描,此時因為黑色對象的屬性都也已經被標記過了,所以只需找出灰色對象並順著繼續往下標記(且因為大部分的標記工作已經在第一次並發的時候發生了,所以灰色對象數量會很少,標記時間也會短很多), 此時程序繼續執行,GC線程掃描所有的內存,找出掃描之後依舊被標記為白色的對象(垃圾),清除。
具體流程:
在JVM虛擬機中有兩種常見垃圾回收器使用了該演算法:CMS(Concurrent Mark Sweep)、G1(Garbage First) ,為了解決三色標記法對對象漏標問題各自有各自的法:
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器。目前很大一部分的java應用集中在互聯網網站或者基於瀏覽器的B/S系統的服務端上,這類應用通常都會較為關注服務的響應速度,希望系統停頓時間盡可能短,以給用戶帶來良好的交互體驗。CMS收集器就非常符合這類應用的需求(但是實際由於某些問題,很少有使用CMS作為主要垃圾回收器的)。
從名字(包含「Mark Sweep」)上就可以看出CMS收集器是基於標記-清除演算法實現的,它的運作過程相對於前面幾種收集器來說要更復雜一些,整個過程分為四個步驟,包括:1)初始標記(CMS initial mark) 2)並發標記(CMS concurrent mark) 3)重新標記(CMS remark) 4)並發清除(CMS concurrent sweep)
其中初始標記、重新標記這兩個步驟仍然需要「Stop The World」。初始標記僅僅只是標記一下GCRoots能直接關聯到的對象,速度很快;
並發標記階段就是從GC Roots的直接關聯對象開始遍歷整個對象圖的過程,這個過程耗時較長但是不需要停頓用戶線程,可以與垃圾收集線程一起並發運行;
重新標記階段則是為了修正並發標記期間,因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段稍長一些,但也遠比並發標記階段的時間短;
最後是並發清除階段,清理刪除掉標記階段判斷的已經死亡的對象,由於不需要移動存活對象,所以這個階段也是可以與用戶線程同時並發的。由於在整個過程中耗時最長的並發標記和並發清除階段中,垃圾收集器線程都可以與用戶線程一起工作,所以從總體上來說,CMS收集器的內存回收過程是與用戶線程一起並發執行的。
在應對漏標問題時,CMS使用了增量更新(Increment Update)方法來做:
在一個未被標記的對象(白色對象)被重新引用後, 引用它的對象若為黑色則要變成灰色,在下次二次標記時讓GC線程繼續標記它的屬性對象 。
但是就算是這樣,其仍然是存在漏標的問題:
G1(Garbage First)物理內存不再分代,而是由一塊一塊的Region組成,但是邏輯分代仍然存在。G1不再堅持固定大小以及固定數量的分代區域劃分,而是把連續的Java堆劃分為多個大小相等的獨立區域(Region),每一個Region都可以根據需要,扮演新生代的Eden空間、Survivor空間,或者老年代空間。收集器能夠對扮演不同角色的Region採用不同的策略去處理,這樣無論是新創建的對象還是已經存活了一段時間、熬過多次收集的舊對象都能獲取很好的收集效果。
Region中還有一類特殊的Humongous區域,專門用來存儲大對象。G1認為只要大小超過了一個Region容量一半的對象即可判定為大對象。每個Region的大小可以通過參數-XX:G1HeapRegionSize設定,取值范圍為1MB~32MB,且應為2的N次冪。而對於那些超過了整個Region容量的超級大對象,將會被存放在N個連續的Humongous Region之中,G1的大多數行為都把Humongous Region作為老年代的一部分來進行看待,如圖所示
Card Table(多種垃圾回收器均具備)
RSet(Remembered Set)
是輔助GC過程的一種結構,典型的空間換時間工具,和Card Table有些類似。
後面說到的CSet(Collection Set)也是輔助GC的,它記錄了GC要收集的Region集合,集合里的Region可以是任意年代的。
在GC的時候,對於old->young和old->old的跨代對象引用,只要掃描對應的CSet中的RSet即可。邏輯上說每個Region都有一個RSet,RSet記錄了其他Region中的對象引用本Region中對象的關系,屬於points-into結構(誰引用了我的對象)。
而Card Table則是一種points-out(我引用了誰的對象)的結構,每個Card 覆蓋一定范圍的Heap(一般為512Bytes)。G1的RSet是在Card Table的基礎上實現的:每個Region會記錄下別的Region有指向自己的指針,並標記這些指針分別在哪些Card的范圍內。這個RSet其實是一個Hash Table,Key是別的Region的起始地址,Value是一個集合,裡面的元素是Card Table的Index。每個Region中都有一個RSet,記錄其他Region到本Region的引用信息;使得垃圾回收器不需要掃描整個堆找到誰引用當前分區中的對象,只需要掃描RSet即可。
CSet(Collection Set)
一組可被回收的分區Region的集合, 是多個對象的集合內存區域。
新生代與老年代的比例
5% - 60%,一般不使用手工指定,因為這是G1預測停頓時間的基準,這地方簡要說明一下,G1可以指定一個預期的停頓時間,然後G1會根據你設定的時間來動態調整年輕代的比例,例如時間長,就將年輕代比例調小,讓YGC盡早行。
SATB(Snapshot At The Beginning), 在應對漏標問題時,G1使用了SATB方法來做,具體流程:
因為SATB在重新標記環節只需要去重新掃描那些被推到堆棧中的引用,並配合Rset來判斷當前對象是否被引用來進行回收;
並且在最後G1並不會選擇回收所有垃圾對象,而是根據Region的垃圾多少來判斷與預估回收價值(指回收的垃圾與回收的STW時間的一個預估值),將一個或者多個Region放到CSet中,最後將這些Region中的存活對象壓縮並復制到新的Region中,清空原來的Region。
會,當內存滿了的時候就會進行Full GC;且JDK10之前的Full GC,為單線程的,所以使用G1需要避免Full GC的產生。
解決方案:
㈡ java問題
只要加到比較方法之中就可以
如:比較方法如下
for(int i=0;i<100;i++)
for(int j=i;j<100;j++){
if(array[i]>array[j]){
int temp=array[i];
array[i]=array[j];
array[j]=temp;
}
}
把::
比較次數compare_count、交換次數exchange_count、探測次數probe_count)加到裡面就可以
for(int i=0,compare_count=0;i<100;i++)
for(int j=i;j<100;j++){
if(array[i]>array[j]){
compare_count++;
int temp=array[i];
array[i]=array[j];
array[j]=temp;
exchange_count++;
}
}
就可以了
各種排序方法的綜合比較
一、時間性能
按平均的時間性能來分,有三類排序方法:
時間復雜度為O(nlogn)的方法有:快速排序、堆排序和歸並排序,其中以快速排序為最好;
時間復雜度為O(n2)的有:直接插入排序、起泡排序和簡單選擇排序,其中以直接插入為最好,特別是對那些對關鍵字近似有序的記錄序列尤為如此;
時間復雜度為O(n)的排序方法只有,基數排序。
當待排記錄序列按關鍵字順序有序時,直接插入排序和起泡排序能達到O(n)的時間復雜度;而對於快速排序而言,這是最不好的情況,此時的時間性能蛻化為O(n2),因此是應該盡量避免的情況。
簡單選擇排序、堆排序和歸並排序的時間性能不隨記錄序列中關鍵字的分布而改變。
二、空間性能
指的是排序過程中所需的輔助空間大小。
1. 所有的簡單排序方法(包括:直接插入、起泡和簡單選擇)和堆排序的空間復雜度為O(1);
2. 快速排序為O(logn ),為棧所需的輔助空間;
3. 歸並排序所需輔助空間最多,其空間復雜度為O(n );
4.鏈式基數排序需附設隊列首尾指針,則空間復雜度為O(rd )。
三、排序方法的穩定性能
1. 穩定的排序方法指的是,對於兩個關鍵字相等的記錄,它們在序列中的相對位置,在排序之前和經過排序之後,沒有改變。
2. 當對多關鍵字的記錄序列進行LSD方法排序時,必須採用穩定的排序方法。
3. 對於不穩定的排序方法,只要能舉出一個實例說明即可。
4. 快速排序和堆排序是不穩定的排序方法。
四、關於「排序方法的時間復雜度的下限」
本章討論的各種排序方法,除基數排序外,其它方法都是基於「比較關鍵字」進行排序的排序方法,可以證明,這類排序法可能達到的最快的時間復雜度為O(n logn )。(基數排序不是基於「比較關鍵字」的排序方法,所以它不受這個限制)。
可以用一棵判定樹來描述這類基於「比較關鍵字」進行排序的排序方法。
例如,對三個關鍵字進行排序的判定樹如下:
描述排序的判定樹有兩個特點:
1.樹上的每一次「比較」都是必要的;
2.樹上的葉子結點包含所有可能情況。
則由上圖所示「判定樹的深度為4」可以推出「至多進行三次比較」即可完成對三個關鍵字的排序。反過來說,由此判定樹可見,考慮最壞情況,「至少要進行三次比較」才能完成對三個關鍵字的排序。
對三個關鍵字進行排序的判定樹深度是唯一的。即無論按什麼先後順序去進行比較,所得判定樹的深度都是3。
當關鍵字的個數超過3之後,不同的排序方法其判定樹的深度不同。例如,對4個關鍵字進行排序時,直接插入的判定樹的深度為6, 而折半插入的判定樹的深度為5。
可以證明,對4個關鍵字進行排序,至少需進行5次比較。因為,4個關鍵字排序的結果有4!=24種可能,即排序的判定樹上必須有24個葉子結點,其深度的最小值為6。
一般情況下,對n個關鍵字進行排序,可能得到的結果有n! 種,由於含n! 個葉子結點的二叉樹的深度不小於 , 則對n個關鍵字進行排序的比較次數至少是
。利用斯蒂林近似公式
所以,基於「比較關鍵字」進行排序的排序方法,可能達到的最快的時間復雜度為O(n logn )。
快速排序是對冒泡排序的一種改進。它的基本思想是:通過一躺排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一不部分的所有數據都要小,然後再按次方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。
假設要排序的數組是A[1]……A[N],首先任意選取一個數據(通常選用第一個數據)作為關鍵數據,然後將所有比它的數都放到它前面,所有比它大的數都放到它後面,這個過程稱為一躺快速排序。一躺快速排序的演算法是:
1)、設置兩個變數I、J,排序開始的時候I:=1,J:=N;
2)以第一個數組元素作為關鍵數據,賦值給X,即X:=A[1];
3)、從J開始向前搜索,即由後開始向前搜索(J:=J-1),找到第一個小於X的值,兩者交換;
4)、從I開始向後搜索,即由前開始向後搜索(I:=I+1),找到第一個大於X的值,兩者交換;
5)、重復第3、4步,直到I=J;
例如:待排序的數組A的值分別是:(初始關鍵數據X:=49)
A[1] A[2] A[3] A[4] A[5] A[6] A[7]:
49 38 65 97 76 13 27
進行第一次交換後: 27 38 65 97 76 13 49
( 按照演算法的第三步從後面開始找
進行第二次交換後: 27 38 49 97 76 13 65
( 按照演算法的第四步從前面開始找>X的值,65>49,兩者交換,此時I:=3 )
進行第三次交換後: 27 38 13 97 76 49 65
( 按照演算法的第五步將又一次執行演算法的第三步從後開始找
進行第四次交換後: 27 38 13 49 76 97 65
( 按照演算法的第四步從前面開始找大於X的值,97>49,兩者交換,此時J:=4 )
此時再執行第三不的時候就發現I=J,從而結束一躺快速排序,那麼經過一躺快速排序之後的結果是:27 38 13 49 76 97 65,即所以大於49的數全部在49的後面,所以小於49的數全部在49的前面。
快速排序就是遞歸調用此過程——在以49為中點分割這個數據序列,分別對前面一部分和後面一部分進行類似的快速排序,從而完成全部數據序列的快速排序,最後把此數據序列變成一個有序的序列,根據這種思想對於上述數組A的快速排序的全過程如圖6所示:
初始狀態 {49 38 65 97 76 13 27}
進行一次快速排序之後劃分為 {27 38 13} 49 {76 97 65}
分別對前後兩部分進行快速排序 {13} 27 {38}
結束 結束 {49 65} 76 {97}
49 {65} 結束
結束
圖6 快速排序全過程
1)、設有N(假設N=10)個數,存放在S數組中;
2)、在S[1。。N]中任取一個元素作為比較基準,例如取T=S[1],起目的就是在定出T應在排序結果中的位置K,這個K的位置在:S[1。。K-1]<=S[K]<=S[K+1..N],即在S[K]以前的數都小於S[K],在S[K]以後的數都大於S[K];
3)、利用分治思想(即大化小的策略)可進一步對S[1。。K-1]和S[K+1。。N]兩組數據再進行快速排序直到分組對象只有一個數據為止。 1 2 3 4 5 6 7 8 9 10
如具體數據如下,那麼第一躺快速排序的過程是:
數組下標:
45 36 18 53 72 30 48 93 15 36
5) 36 36 18 15 30 45 48 93 72 534) 36 36 18 15 45 30 48 93 72 533) 36 36 18 15 72 30 48 93 45 532) 36 36 18 45 72 30 48 93 15 53
program kuaisu(input,output);
const n=10;
var
s:array[1..10] of integer;
k,l,m:integer;
procere qsort(lx,rx:integer);
var
I,j,t:integer;
Begin
I:lx;j:rx;t:s[I];
Repeat
While (s[j]>t) and (j>I) do
Begin
k:=k+1;
j:=j-1
end;
if I<j then
begin
s[I]:=s[j];I:=I+1;l:=l+1;
while (s[I]<t) and (I<j) do
begin
k:=k+1;
I:=I+1
End;
If I<j then
begin
S[j]:=s[I];j:=j-1;l:=l+1;
End;
End;
Until I=j;
S[I]:=t;I:=I+1;j:=j-1;l:=l+1;
If lx<j then qsort(lx,j);
If I<rx then qsort(I,rx)
End;{過程qsort結束}
Begin
Writeln('input 10 integer num:');
For m:=1 to n do read(s[m]);
K:=0;l:=0;
Qsort(l,n);
Writeln('排序後結果是:');
For m:=1 to n do write(s[m]:4)
End.
通過一躺排序將45放到應該放的位置K,這里K=6,那麼再對S[1。。5]和S[6。。10]分別進行快速排序。程序代碼如下:<49,兩者交換,此時J:=6>
㈢ java十大演算法
演算法一:快速排序演算法
快速排序是由東尼·霍爾所發展的一種排序演算法。在平均狀況下,排序 n 個項目要Ο(n log n)次比較。在最壞狀況下則需要Ο(n2)次比較,但這種狀況並不常見。事實上,快速排序通常明顯比其他Ο(n log n) 演算法更快,因為它的內部循環(inner loop)可以在大部分的架構上很有效率地被實現出來。
快速排序使用分治法(Divide and conquer)策略來把一個串列(list)分為兩個子串列(sub-lists)。
演算法步驟:
1 從數列中挑出一個元素,稱為 "基準"(pivot),
2 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分區退出之後,該基準就處於數列的中間位置。這個稱為分區(partition)操作。
3 遞歸地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。
遞歸的最底部情形,是數列的大小是零或一,也就是永遠都已經被排序好了。雖然一直遞歸下去,但是這個演算法總會退出,因為在每次的迭代(iteration)中,它至少會把一個元素擺到它最後的位置去。
演算法二:堆排序演算法
堆排序(Heapsort)是指利用堆這種數據結構所設計的一種排序演算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。
堆排序的平均時間復雜度為Ο(nlogn) 。
演算法步驟:
創建一個堆H[0..n-1]
把堆首(最大值)和堆尾互換
3. 把堆的尺寸縮小1,並調用shift_down(0),目的是把新的數組頂端數據調整到相應位置
4. 重復步驟2,直到堆的尺寸為1
演算法三:歸並排序
歸並排序(Merge sort,台灣譯作:合並排序)是建立在歸並操作上的一種有效的排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。
演算法步驟:
1. 申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合並後的序列
2. 設定兩個指針,最初位置分別為兩個已經排序序列的起始位置
3. 比較兩個指針所指向的元素,選擇相對小的元素放入到合並空間,並移動指針到下一位置
4. 重復步驟3直到某一指針達到序列尾
5. 將另一序列剩下的所有元素
㈣ java垃圾是怎麼回收的,回收演算法
Java ,C#語言與C/C++語言一個很大的區別是java與C#具有自動垃圾回收機制。C++程序員經常需要絞盡腦汁的分析哪裡出現了內存泄漏。而在java,C#中,雖然有時也會出現內存泄漏,但大部分情況下程序員不需要考慮對象或者數據何時需要被銷毀。因此程序員不會因為錯誤的釋放內存而導致程序崩潰。垃圾回收的缺點是加大了程序的負擔,有可能影響程序的性能。
1.垃圾收集器的主要功能有
(1) 定期發現那些對象不再被引用,並把這些對象占據的堆空間釋放出來。
(2) 類似於操作系統的內存管理,垃圾收集器還需要處理由於對象動態生成與銷毀產生的堆碎塊,以便更有效的利用虛擬機內存。
2.區分活動對象與垃圾的演算法
(1)引用計數法
堆中每一個對象都有一個引用計數。當新創建一個對象,或者有變數被賦值為這個對象的引用,則這個對象的引用計數加1;當一個對象的引用超過生存期或者被設置一個新的值時,這個對象的引用計數減1。當對象的引用計數變為0時,就可以被當作垃圾收集。
這種方法的好處是垃圾收集較快,適用於實時環境。缺點是這種方法無法監測出循環引用。例如對象A引用對象B,對象B也引用對象A,則這兩個對象可能無法被垃圾收集器收集。因此這種方法是垃圾收集的早期策略,現在很少使用。
(2)跟蹤法
這種方法把每個對象看作圖中一個節點,對象之間的引用關系為圖中各節點的鄰接關系。垃圾收集器從一個或數個根結點遍歷對象圖,如果有些對象節點永遠無法到達,則這個對象可以被當作垃圾回收。
容易發現,這種方法可以檢測出循環引用,避免了引用計數法的缺點,較為常用。
3.常用垃圾回收機制
(1)標記-清除收集器
這種收集器首先遍歷對象圖並標記可到達的對象,然後掃描堆棧以尋找未標記對象並釋放它們的內存。這種收集器一般使用單線程工作並停止其他操作。
(2)標記-壓縮收集器
有時也叫標記-清除-壓縮收集器,與標記-清除收集器有相同的標記階段。在第二階段,則把標記對象復制到堆棧的新域中以便壓縮堆棧。這種收集器也停止其他操作。
(3)復制收集器
這種收集器將堆棧分為兩個域,常稱為半空間。每次僅使用一半的空間,虛擬機生成的新對象則放在另一半空間中。垃圾回收器運行時,它把可到達對象復制到另一半空間,沒有被復制的的對象都是不可達對象,可以被回收。這種方法適用於短生存期的對象,持續復制長生存期的對象由於多次拷貝,導致效率降低。缺點是只有一半的虛擬機空間得到使用。
(4)增量收集器
增量收集器把堆棧分為多個域,每次僅從一個域收集垃圾。這會造成較小的應用程序中斷。
(5)分代收集器
這種收集器把堆棧分為兩個或多個域,用以存放不同壽命的對象。虛擬機生成的新對象一般放在其中的某個域中。過一段時間,繼續存在的對象將獲得使用期並轉入更長壽命的域中。分代收集器對不同的域使用不同的演算法以優化性能。這樣可以減少復制對象的時間。
(6)並發收集器
並發收集器與應用程序同時運行。這些收集器在某點上(比如壓縮時)一般都不得不停止其他操作以完成特定的任務,但是因為其他應用程序可進行其他的後台操作,所以中斷其他處理的實際時間大大降低。
(7)並行收集器
並行收集器使用某種傳統的演算法並使用多線程並行的執行它們的工作。在多CPU機器上使用多線程技術可以顯著的提高java應用程序的可擴展性。
(8)自適應收集器
根據程序運行狀況以及堆的使用狀況,自動選一種合適的垃圾回收演算法。這樣可以不局限與一種垃圾回收演算法。
4. 火車演算法
垃圾收集演算法一個很大的缺點就是難以控制垃圾回收所佔用的CPU時間,以及何時需要進行垃圾回收。火車演算法是分代收集器所用的演算法,目的是在成熟對象空間中提供限定時間的漸進收集。目前應用於SUN公司的Hotspot虛擬機上。
在火車演算法中,內存被分為塊,多個塊組成一個集合。為了形象化,一節車廂代表一個塊,一列火車代表一個集合,見圖一
注意每個車廂大小相等,但每個火車包含的車廂數不一定相等。垃圾收集以車廂為單位,收集順序依次為1.1,1.2,1.3,1.4,2.1,2.2,2.3,3.1,3.2,3.3。這個順序也是塊被創建的先後順序。
垃圾收集器先從塊1.1開始掃描直到1.4,如果火車1四個塊中的所有對象沒有被火車2和火車3的對象引用,而只有火車1內部的對象相互引用,則整個火車1都是垃圾,可以被回收。
如圖二,車廂1.1中有對象A和對象B,1.3中有對象C,1.4中有對象D,車廂2.2中有對象E,車廂3.3中有對象F。在火車1中,對象C引用對象A,對象B引用對象D,可見,火車2和火車3沒有引用火車1的對象,則整個火車1都是垃圾。
如果火車1中有對象被其它火車引用,見圖三,掃描車廂1.1時發現對象A被火車2中的E引用,則將對象A從車廂1.1轉移到車廂2.2,然後掃描A引用的對象D,把D也轉移到車廂2.2,然後掃描D,看D是否引用其它對象,如果引用了其它對象則也要轉移,依次類推。掃描完火車1的所有對象後,剩下的沒有轉移的對象都是垃圾,可以把整個火車1都作為垃圾回收。注意如果在轉移時,如果車廂2.2空間滿了,則要在火車2末尾開辟新的車廂2.4,將新轉移的對象都放到2.4,即火車的尾部)
補充說明:垃圾回收器一次只掃描一個車廂。圖三中的對象B與C並不是立即被回收,而是先會被轉移到火車1的尾部車廂。即掃描完1.1後,B被轉移到火車1尾部,掃描完1.3後,C被轉移到車尾。等垃圾收集器掃描到火車1尾部時,如果仍然沒有外部對象引用它們,則B和C會被收集。
火車演算法最大的好處是它可以保證大的循環結構可以被完全收集,因為成為垃圾的循環結構中的對象,無論多大,都會被移入同一列火車,最終一起被收集。還有一個好處是這種演算法在大多數情況下可以保證一次垃圾收集所耗時間在一定限度之內,因為一次垃圾回收只收集一個車廂,而車廂的大小是有限度的。
㈤ 數據結構 java開發中常用的排序演算法有哪些
排序演算法有很多,所以在特定情景中使用哪一種演算法很重要。為了選擇合適的演算法,可以按照建議的順序考慮以下標准:
(1)執行時間
(2)存儲空間
(3)編程工作
對於數據量較小的情形,(1)(2)差別不大,主要考慮(3);而對於數據量大的,(1)為首要。
主要排序法有:
一、冒泡(Bubble)排序——相鄰交換
二、選擇排序——每次最小/大排在相應的位置
三、插入排序——將下一個插入已排好的序列中
四、殼(Shell)排序——縮小增量
五、歸並排序
六、快速排序
七、堆排序
八、拓撲排序
一、冒泡(Bubble)排序
----------------------------------Code 從小到大排序n個數------------------------------------
void BubbleSortArray()
{
for(int i=1;i<n;i++)
{
for(int j=0;i<n-i;j++)
{
if(a[j]>a[j+1])//比較交換相鄰元素
{
int temp;
temp=a[j]; a[j]=a[j+1]; a[j+1]=temp;
}
}
}
}
-------------------------------------------------Code------------------------------------------------
效率 O(n²),適用於排序小列表。
二、選擇排序
----------------------------------Code 從小到大排序n個數--------------------------------
void SelectSortArray()
{
int min_index;
for(int i=0;i<n-1;i++)
{
min_index=i;
for(int j=i+1;j<n;j++)//每次掃描選擇最小項
if(arr[j]<arr[min_index]) min_index=j;
if(min_index!=i)//找到最小項交換,即將這一項移到列表中的正確位置
{
int temp;
temp=arr[i]; arr[i]=arr[min_index]; arr[min_index]=temp;
}
}
}
-------------------------------------------------Code-----------------------------------------
效率O(n²),適用於排序小的列表。
三、插入排序
--------------------------------------------Code 從小到大排序n個數-------------------------------------
void InsertSortArray()
{
for(int i=1;i<n;i++)//循環從第二個數組元素開始,因為arr[0]作為最初已排序部分
{
int temp=arr[i];//temp標記為未排序第一個元素
int j=i-1;
while (j>=0 && arr[j]>temp)/*將temp與已排序元素從小到大比較,尋找temp應插入的位置*/
{
arr[j+1]=arr[j];
j--;
}
arr[j+1]=temp;
}
}
------------------------------Code--------------------------------------------------------------
最佳效率O(n);最糟效率O(n²)與冒泡、選擇相同,適用於排序小列表
若列表基本有序,則插入排序比冒泡、選擇更有效率。
四、殼(Shell)排序——縮小增量排序
-------------------------------------Code 從小到大排序n個數-------------------------------------
void ShellSortArray()
{
for(int incr=3;incr<0;incr--)//增量遞減,以增量3,2,1為例
{
for(int L=0;L<(n-1)/incr;L++)//重復分成的每個子列表
{
for(int i=L+incr;i<n;i+=incr)//對每個子列表應用插入排序
{
int temp=arr[i];
int j=i-incr;
while(j>=0&&arr[j]>temp)
{
arr[j+incr]=arr[j];
j-=incr;
}
arr[j+incr]=temp;
}
}
}
}
--------------------------------------Code-------------------------------------------
適用於排序小列表。
效率估計O(nlog2^n)~O(n^1.5),取決於增量值的最初大小。建議使用質數作為增量值,因為如果增量值是2的冪,則在下一個通道中會再次比較相同的元素。
殼(Shell)排序改進了插入排序,減少了比較的次數。是不穩定的排序,因為排序過程中元素可能會前後跳躍。
五、歸並排序
----------------------------------------------Code 從小到大排序---------------------------------------
void MergeSort(int low,int high)
{
if(low>=high) return;//每個子列表中剩下一個元素時停止
else int mid=(low+high)/2;/*將列表劃分成相等的兩個子列表,若有奇數個元素,則在左邊子列表大於右側子列表*/
MergeSort(low,mid);//子列表進一步劃分
MergeSort(mid+1,high);
int [] B=new int [high-low+1];//新建一個數組,用於存放歸並的元素
for(int i=low,j=mid+1,k=low;i<=mid && j<=high;k++)/*兩個子列表進行排序歸並,直到兩個子列表中的一個結束*/
{
if (arr[i]<=arr[j];)
{
B[k]=arr[i];
I++;
}
else
{ B[k]=arr[j]; j++; }
}
for( ;j<=high;j++,k++)//如果第二個子列表中仍然有元素,則追加到新列表
B[k]=arr[j];
for( ;i<=mid;i++,k++)//如果在第一個子列表中仍然有元素,則追加到新列表中
B[k]=arr[i];
for(int z=0;z<high-low+1;z++)//將排序的數組B的 所有元素復制到原始數組arr中
arr[z]=B[z];
}
-----------------------------------------------------Code---------------------------------------------------
效率O(nlogn),歸並的最佳、平均和最糟用例效率之間沒有差異。
適用於排序大列表,基於分治法。
六、快速排序
------------------------------------Code--------------------------------------------
/*快速排序的演算法思想:選定一個樞紐元素,對待排序序列進行分割,分割之後的序列一個部分小於樞紐元素,一個部分大於樞紐元素,再對這兩個分割好的子序列進行上述的過程。*/ void swap(int a,int b){int t;t =a ;a =b ;b =t ;}
int Partition(int [] arr,int low,int high)
{
int pivot=arr[low];//採用子序列的第一個元素作為樞紐元素
while (low < high)
{
//從後往前栽後半部分中尋找第一個小於樞紐元素的元素
while (low < high && arr[high] >= pivot)
{
--high;
}
//將這個比樞紐元素小的元素交換到前半部分
swap(arr[low], arr[high]);
//從前往後在前半部分中尋找第一個大於樞紐元素的元素
while (low <high &&arr [low ]<=pivot )
{
++low ;
}
swap (arr [low ],arr [high ]);//將這個樞紐元素大的元素交換到後半部分
}
return low ;//返回樞紐元素所在的位置
}
void QuickSort(int [] a,int low,int high)
{
if (low <high )
{
int n=Partition (a ,low ,high );
QuickSort (a ,low ,n );
QuickSort (a ,n +1,high );
}
}
----------------------------------------Code-------------------------------------
平均效率O(nlogn),適用於排序大列表。
此演算法的總時間取決於樞紐值的位置;選擇第一個元素作為樞紐,可能導致O(n²)的最糟用例效率。若數基本有序,效率反而最差。選項中間值作為樞紐,效率是O(nlogn)。
基於分治法。
七、堆排序
最大堆:後者任一非終端節點的關鍵字均大於或等於它的左、右孩子的關鍵字,此時位於堆頂的節點的關鍵字是整個序列中最大的。
思想:
(1)令i=l,並令temp= kl ;
(2)計算i的左孩子j=2i+1;
(3)若j<=n-1,則轉(4),否則轉(6);
(4)比較kj和kj+1,若kj+1>kj,則令j=j+1,否則j不變;
(5)比較temp和kj,若kj>temp,則令ki等於kj,並令i=j,j=2i+1,並轉(3),否則轉(6)
(6)令ki等於temp,結束。
-----------------------------------------Code---------------------------
void HeapSort(SeqIAst R)
{ //對R[1..n]進行堆排序,不妨用R[0]做暫存單元 int I; BuildHeap(R); //將R[1-n]建成初始堆for(i=n;i>1;i--) //對當前無序區R[1..i]進行堆排序,共做n-1趟。{ R[0]=R[1]; R[1]=R[i]; R[i]=R[0]; //將堆頂和堆中最後一個記錄交換 Heapify(R,1,i-1); //將R[1..i-1]重新調整為堆,僅有R[1]可能違反堆性質 } } ---------------------------------------Code--------------------------------------
堆排序的時間,主要由建立初始堆和反復重建堆這兩部分的時間開銷構成,它們均是通過調用Heapify實現的。
堆排序的最壞時間復雜度為O(nlgn)。堆排序的平均性能較接近於最壞性能。 由於建初始堆所需的比較次數較多,所以堆排序不適宜於記錄數較少的文件。 堆排序是就地排序,輔助空間為O(1), 它是不穩定的排序方法。
堆排序與直接插入排序的區別:
直接選擇排序中,為了從R[1..n]中選出關鍵字最小的記錄,必須進行n-1次比較,然後在R[2..n]中選出關鍵字最小的記錄,又需要做n-2次比較。事實上,後面的n-2次比較中,有許多比較可能在前面的n-1次比較中已經做過,但由於前一趟排序時未保留這些比較結果,所以後一趟排序時又重復執行了這些比較操作。
堆排序可通過樹形結構保存部分比較結果,可減少比較次數。
八、拓撲排序
例 :學生選修課排課先後順序
拓撲排序:把有向圖中各頂點按照它們相互之間的優先關系排列成一個線性序列的過程。
方法:
在有向圖中選一個沒有前驅的頂點且輸出
從圖中刪除該頂點和所有以它為尾的弧
重復上述兩步,直至全部頂點均已輸出(拓撲排序成功),或者當圖中不存在無前驅的頂點(圖中有迴路)為止。
---------------------------------------Code--------------------------------------
void TopologicalSort()/*輸出拓撲排序函數。若G無迴路,則輸出G的頂點的一個拓撲序列並返回OK,否則返回ERROR*/
{
int indegree[M];
int i,k,j;
char n;
int count=0;
Stack thestack;
FindInDegree(G,indegree);//對各頂點求入度indegree[0....num]
InitStack(thestack);//初始化棧
for(i=0;i<G.num;i++)
Console.WriteLine("結點"+G.vertices[i].data+"的入度為"+indegree[i]);
for(i=0;i<G.num;i++)
{
if(indegree[i]==0)
Push(thestack.vertices[i]);
}
Console.Write("拓撲排序輸出順序為:");
while(thestack.Peek()!=null)
{
Pop(thestack.Peek());
j=locatevex(G,n);
if (j==-2)
{
Console.WriteLine("發生錯誤,程序結束。");
exit();
}
Console.Write(G.vertices[j].data);
count++;
for(p=G.vertices[j].firstarc;p!=NULL;p=p.nextarc)
{
k=p.adjvex;
if (!(--indegree[k]))
Push(G.vertices[k]);
}
}
if (count<G.num)
Cosole.WriteLine("該圖有環,出現錯誤,無法排序。");
else
Console.WriteLine("排序成功。");
}
----------------------------------------Code--------------------------------------
演算法的時間復雜度O(n+e)。
㈥ Java的排序演算法有哪些
java的排序大的分類可以分為兩種:內排序和外排序。在排序過程中,全部記錄存放在內存,則稱為內排序,如果排序過程中需要使用外存,則稱為外排序。下面講的排序都是屬於內排序。
1.插入排序:直接插入排序、二分法插入排序、希爾排序。
2.選擇排序:簡單選擇排序、堆排序。
3.交換排序:冒泡排序、快速排序。
4.歸並排序
5.基數排序
㈦ java有哪些垃圾回收演算法
常用的垃圾回收演算法有:
(1).引用計數演算法:
給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器都為0的對象就是不再被使用的,垃圾收集器將回收該對象使用的內存。
引用計數演算法實現簡單,效率很高,微軟的COM技術、ActionScript、Python等都使用了引用計數演算法進行內存管理,但是引用計數演算法對於對象之間相互循環引用問題難以解決,因此java並沒有使用引用計數演算法。
(2).根搜索演算法:
通過一系列的名為「GC Root」的對象作為起點,從這些節點向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Root沒有任何引用鏈相連時,則該對象不可達,該對象是不可使用的,垃圾收集器將回收其所佔的內存。
主流的商用程序語言C#、java和Lisp都使用根搜素演算法進行內存管理。
在java語言中,可作為GC Root的對象包括以下幾種對象:
a. java虛擬機棧(棧幀中的本地變數表)中的引用的對象。
b.方法區中的類靜態屬性引用的對象。
c.方法區中的常量引用的對象。
d.本地方法棧中JNI本地方法的引用對象。
java方法區在Sun HotSpot虛擬機中被稱為永久代,很多人認為該部分的內存是不用回收的,java虛擬機規范也沒有對該部分內存的垃圾收集做規定,但是方法區中的廢棄常量和無用的類還是需要回收以保證永久代不會發生內存溢出。
判斷廢棄常量的方法:如果常量池中的某個常量沒有被任何引用所引用,則該常量是廢棄常量。
判斷無用的類:
(1).該類的所有實例都已經被回收,即java堆中不存在該類的實例對象。
(2).載入該類的類載入器已經被回收。
(3).該類所對應的java.lang.Class對象沒有任何地方被引用,無法在任何地方通過反射機制訪問該類的方法。
Java中常用的垃圾收集演算法:
(1).標記-清除演算法:
最基礎的垃圾收集演算法,演算法分為「標記」和「清除」兩個階段:首先標記出所有需要回收的對象,在標記完成之後統一回收掉所有被標記的對象。
標記-清除演算法的缺點有兩個:首先,效率問題,標記和清除效率都不高。其次,標記清除之後會產生大量的不連續的內存碎片,空間碎片太多會導致當程序需要為較大對象分配內存時無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。
(2).復制演算法:
將可用內存按容量分成大小相等的兩塊,每次只使用其中一塊,當這塊內存使用完了,就將還存活的對象復制到另一塊內存上去,然後把使用過的內存空間一次清理掉。這樣使得每次都是對其中一塊內存進行回收,內存分配時不用考慮內存碎片等復雜情況,只需要移動堆頂指針,按順序分配內存即可,實現簡單,運行高效。
復制演算法的缺點顯而易見,可使用的內存降為原來一半。
(3).標記-整理演算法:
標記-整理演算法在標記-清除演算法基礎上做了改進,標記階段是相同的標記出所有需要回收的對象,在標記完成之後不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,在移動過程中清理掉可回收的對象,這個過程叫做整理。
標記-整理演算法相比標記-清除演算法的優點是內存被整理以後不會產生大量不連續內存碎片問題。
復制演算法在對象存活率高的情況下就要執行較多的復制操作,效率將會變低,而在對象存活率高的情況下使用標記-整理演算法效率會大大提高。
(4).分代收集演算法:
根據內存中對象的存活周期不同,將內存劃分為幾塊,java的虛擬機中一般把內存劃分為新生代和年老代,當新創建對象時一般在新生代中分配內存空間,當新生代垃圾收集器回收幾次之後仍然存活的對象會被移動到年老代內存中,當大對象在新生代中無法找到足夠的連續內存時也直接在年老代中創建。
㈧ 求JAVA.數據結構.演算法學習視頻百度雲。
《數據結構課程精講教案合集-復旦大學(共計1061頁).pdf 》網路網盤免費資源下載
鏈接: https://pan..com/s/15uwjtHgKKzZdheWFQC21pQ