139. Java中的泛型是什么 ? 使用泛型的好處是什么?
泛型是Java SE 1.5的新特性,泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數。
好處:
1、類型安全,提供編譯期間的類型檢測
2、前后兼容
3、泛化代碼,代碼可以更多的重復利用
4、性能較高,用GJ(泛型JAVA)編寫的代碼可以為java編譯器和虛擬機帶來更多的類型信息,這些信息對java程序做進一步優化提供條件。
140,Java的泛型是如何工作的 ? 什么是類型擦除 ?如何工作?
1、類型檢查:在生成字節碼之前提供類型檢查
2、類型擦除:所有類型參數都用他們的限定類型替換,包括類、變量和方法(類型擦除)
3、如果類型擦除和多態性發生了沖突時,則在子類中生成橋方法解決
4、如果調用泛型方法的返回類型被擦除,則在調用該方法時插入強制類型轉換
類型擦除:
所有類型參數都用他們的限定類型替換:
比如T->Object ? extends BaseClass->BaseClass
如何工作:
泛型是通過類型擦除來實現的,編譯器在編譯時擦除了所有類型相關的信息,所以在運行時不存在任何類型相關的信息。例如 List<String>在運行時僅用一個List來表示。這樣做的目的,是確保能和Java 5之前的版本開發二進制類庫進行兼容。你無法在運行時訪問到類型參數,因為編譯器已經把泛型類型轉換成了原始類型。根據你對這個泛型問題的回答情況,你會得到一些后續提問,比如為什么泛型是由類型擦除來實現的或者給你展示一些會導致編譯器出錯的錯誤泛型代碼。
141,你可以把List<String>傳遞給一個接受List<Object>參數的方法嗎?
對任何一個不太熟悉泛型的人來說,這個Java泛型題目看起來令人疑惑,因為乍看起來String是一種Object,所以 List<String>應當可以用在需要List<Object>的地方,但是事實並非如此。真這樣做的話會導致編譯錯誤。如果你再深一步考慮,你會發現Java這樣做是有意義的,因為List<Object>可以存儲任何類型的對象包括String, Integer等等,而List<String>卻只能用來存儲String s。
List<Object> objectList;
List<String> stringList;
objectList = stringList; //compilation error incompatible types
142,如何阻止Java中的類型未檢查的警告?
如果你把泛型和原始類型混合起來使用,例如下列代碼,java 5的javac編譯器會產生類型未檢查的警告,例如
List<String> rawList = newArrayList()
注意: Hello.java使用了未檢查或稱為不安全的操作;
這種警告可以使用@SuppressWarnings(“unchecked”)注解來屏蔽。
143,Java中List<Object>和原始類型List之間的區別?
原始類型和帶參數類型<Object>之間的主要區別是,在編譯時編譯器不會對原始類型進行類型安全檢查,卻會對帶參數的類型進行檢查,通過使用Object作為類型,可以告知編譯器該方法可以接受任何類型的對象,比如String或Integer。
這道題的考察點在於對泛型中原始類型的正確理解。它們之間的第二點區別是,你可以把任何帶參數的類型傳遞給原始類型List,但卻不能把List<String>傳遞給接受 List<Object>的方法,因為會產生編譯錯誤。
144,編寫一段泛型程序來實現LRU緩存?
對於喜歡Java編程的人來說這相當於是一次練習。給你個提示,LinkedHashMap可以用來實現固定大小的LRU緩存,當LRU緩存已經滿了的時候,它會把最老的鍵值對移出緩存。
LinkedHashMap提供了一個稱為removeEldestEntry()的方法,該方法會被put() 和putAll()調用來刪除最老的鍵值對。當然,如果你已經編寫了一個可運行的JUnit測試,你也可以隨意編寫你自己的實現代碼。
145,Array中可以用泛型嗎?
這可能是Java泛型面試題中最簡單的一個了,當然前提是你要知道Array事實上並不支持泛型,這也是為什么Joshua Bloch在Effective Java一書中建議使用List來代替Array,因為List可以提供編譯期的類型安全保證,而Array卻不能。
146,如何編寫一個泛型方法,讓它能接受泛型參數並返回泛型類型?
編寫泛型方法並不困難,你需要用泛型類型來替代原始類型,比如使用T, E or K,V等被廣泛認可的類型占位符。最簡單的情況下,一個泛型方法可能會像這樣:
public V put(K key, V value) {
return cahe.put(key,value);
}
147,C++模板和java泛型之間有何不同?
java泛型實現根植於“類型消除”這一概念。當源代碼被轉換為Java虛擬機字節碼時,這種技術會消除參數化類型。有了Java泛型,我們可以做的事情也並沒有真正改變多少;他只是讓代碼變得漂亮些。鑒於此,Java泛型有時也被稱為“語法糖”。
這和 C++模板截然不同。在 C++中,模板本質上就是一套宏指令集,只是換了個名頭,編譯器會針對每種類型創建一份模板代碼的副本。
由於架構設計上的差異,Java泛型和C++模板有很多不同點:
C++模板可以使用int等基本數據類型。Java則不行,必須轉而使用Integer。
在Java中,可以將模板的參數類型限定為某種特定類型。
在C++中,類型參數可以實例化,但java不支持。
在Java中,類型參數不能用於靜態方法(?)和變量,因為它們會被不同類型參數指定的實例共享。在C++,這些類時不同的,因此類型參數可以用於靜態方法和靜態變量。
在Java中,不管類型參數是什么,所有的實例變量都是同一類型。類型參數會在運行時被抹去。在C++中,類型參數不同,實例變量也不同。
148,AJAX有哪些有點和缺點?
優點:
1、最大的一點是頁面無刷新,用戶的體驗非常好。
2、使用異步方式與服務器通信,具有更加迅速的響應能力。
3、可以把以前一些服務器負擔的工作轉嫁到客戶端,利用客戶端閑置的能力來處理,減輕服務器和帶寬的負擔,節約空間和寬帶租用成本。並且減輕服務器的負擔,ajax的原則是“按需取數據”,可以最大程度的減少冗余請求,和響應對服務器造成的負擔。
4、基於標准化的並被廣泛支持的技術,不需要下載插件或者小程序。
缺點:
1、ajax不支持瀏覽器back按鈕。
2、安全問題 AJAX暴露了與服務器交互的細節。
3、對搜索引擎的支持比較弱。
4、破壞了程序的異常機制。
5、不容易調試。
149,AJAX應用和傳統Web應用有什么不同?
在傳統的Javascript編程中,如果想得到服務器端數據庫或文件上的信息,或者發送客戶端信息到服務器,需要建立一個HTML form然后GET或者POST數據到服務器端。用戶需要點擊”Submit”按鈕來發送或者接受數據信息,然后等待服務器響應請求,頁面重新加載。
因為服務器每次都會返回一個新的頁面, 所以傳統的web應用有可能很慢而且用戶交互不友好。
使用AJAX技術, 就可以使Javascript通過XMLHttpRequest對象直接與服務器進行交互。
通過HTTP Request, 一個web頁面可以發送一個請求到web服務器並且接受web服務器返回的信息(不用重新加載頁面),展示給用戶的還是同一個頁面,用戶感覺頁面刷新,也看不到到Javascript后台進行的發送請求和接受響應,體驗非常好。
150,Ajax的實現流程是怎樣的?
(1)創建XMLHttpRequest對象,也就是創建一個異步調用對象.
(2)創建一個新的HTTP請求,並指定該HTTP請求的方法、URL及驗證信息.
(3)設置響應HTTP請求狀態變化的函數.
(4)發送HTTP請求.
(5)獲取異步調用返回的數據.
(6)使用JavaScript和DOM實現局部刷新.
具體一點:
1,創建XNLHttpRequest對象
(不考慮ie)XMLHttpRequest request = new XMLHttprequest();
2,創建新的Http請求
XMLHttprequest.open(method,url,flag,name,password);
3,設置響應Http請求變化的函數
XMLHttprequest.onreadystatechange=getData;
function getData(){
if(XMLHttprequest.readyState==4){
獲取數據
}
}
4,發送http請求
XMLHttprequest.send(data);
5,獲取異步調用返回的對象
,function(data){
//異步提交后,交互成功,返回的data便是異步調用返回的對象,該對象是一個string類型的
}
6,使用js、DOM實現局部刷新
myDiv.innerHTML=''這是刷新后的數據''
151,簡單說一下數據庫的三范式?
第一范式:數據庫表的每一個字段都是不可分割的
第二范式:數據庫表中的非主屬性只依賴於主鍵
第三范式:不存在非主屬性對關鍵字的傳遞函數依賴關系
152,Java集合框架是什么?說出一些集合框架的優點?
每種編程語言中都有集合,最初的Java版本包含幾種集合類:Vector、Stack、HashTable和Array。
隨着集合的廣泛使用,Java1.2提出了囊括所有集合接口、實現和算法的集合框架。在保證線程安全的情況下使用泛型和並發集合類,Java已經經歷了很久。它還包括在Java並發包中,阻塞接口以及它們的實現。
集合框架的部分優點如下:
(1)使用核心集合類降低開發成本,而非實現我們自己的集合類。
(2)隨着使用經過嚴格測試的集合框架類,代碼質量會得到提高。
(3)通過使用JDK附帶的集合類,可以降低代碼維護成本。
(4)復用性和可操作性。
153,Java集合框架的基礎接口有哪些?
Collection為集合層級的根接口。一個集合代表一組對象,這些對象即為它的元素。Java平台不提供這個接口任何直接的實現。
Set是一個不能包含重復元素的集合。這個接口對數學集合抽象進行建模,被用來代表集合,就如一副牌。
List是一個有序集合,可以包含重復元素。你可以通過它的索引來訪問任何元素。List更像長度動態變換的數組。
Map是一個將key映射到value的對象.一個Map不能包含重復的key:每個key最多只能映射一個value。
一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。
154,集合框架中的泛型有什么優點?
Java1.5引入了泛型,所有的集合接口和實現都大量地使用它。泛型允許我們為集合提供一個可以容納的對象類型。
因此,如果你添加其它類型的任何元素,它會在編譯時報錯。這避免了在運行時出現ClassCastException,因為你將會在編譯時得到報錯信息。
泛型也使得代碼整潔,我們不需要使用顯式轉換和instanceOf操作符。它也給運行時帶來好處,因為不會產生類型檢查的字節碼指令。
155,Enumeration和Iterator接口的區別?
Enumeration的速度是Iterator的兩倍,也使用更少的內存。Enumeration是非常基礎的,也滿足了基礎的需要。
但是,與Enumeration相比,Iterator更加安全,因為當一個集合正在被遍歷的時候,它會阻止其它線程去修改集合。
迭代器取代了Java集合框架中的Enumeration。迭代器允許調用者從集合中移除元素,而Enumeration不能做到。為了使它的功能更加清晰,迭代器方法名已經經過改善。
156,Iterater和ListIterator之間有什么區別?
1,我們可以使用Iterator來遍歷Set和List集合,而ListIterator只能遍歷List。
2,Iterator只可以向前遍歷,而LIstIterator可以雙向遍歷。
3,ListIterator從Iterator接口繼承,然后添加了一些額外的功能,比如添加一個元素、替換一個元素、獲取前面或后面元素的索引位置。
157,我們如何對一組對象進行排序?
如果我們需要對一個對象數組進行排序,我們可以使用Arrays.sort()方法。如果我們需要排序一個對象列表,我們可以使用Collection.sort()方法。
兩個類都有用於自然排序(使用Comparable)或基於標准的排序(使用Comparator)的重載方法sort()。
Collections內部使用數組排序方法,所有它們兩者都有相同的性能,只是Collections需要花時間將列表轉換為數組。
158,與Java集合框架相關的有哪些最好的實踐?
1,根據需要選擇正確的集合類型。比如,如果指定了大小,我們會選用Array而非ArrayList。如果我們想根據插入順序遍歷一個Map,我們需要使用TreeMap。如果我們不想重復,我們應該使用Set。
2,一些集合類允許指定初始容量,所以如果我們能夠估計到存儲元素的數量,我們可以使用它,就避免了重新哈希或大小調整。
3,基於接口編程,而非基於實現編程,它允許我們后來輕易地改變實現。
4,總是使用類型安全的泛型,避免在運行時出現ClassCastException。
5,使用JDK提供的不可變類作為Map的key,可以避免自己實現hashCode()和equals()。
6,盡可能使用Collections工具類,或者獲取只讀、同步或空的集合,而非編寫自己的實現。它將會提供代碼重用性,它有着更好的穩定性和可維護性。
159,什么是事務?,
事務是恢復和並發控制的基本單位
事務的四個基本特征
原子性,一致性,隔離性,持久性
原子性和一致性差不多,意思是要么全部成功,要么就失敗
一致性是說,從一個一致性狀態到另一個一致性狀態
隔離性是說一個事務執行的過程中不能被另一個事務干擾
持久性也就是事務一旦提交,他對數據庫中數據的改變就應該是永久的,不能變的(這里只是面試簡單的說一下理解,詳細理解問度娘)
161,Java內存模型是什么?
Java內存模型規定和指引Java程序在不同的內存架構、CPU和操作系統間有確定性地行為。它在多線程的情況下尤其重要。Java內存模型對一個線程所做的變動能被其它線程可見提供了保證,它們之間是先行發生關系。這個關系定義了一些規則讓程序員在並發編程時思路更清晰。比如,先行發生關系確保了:
線程內的代碼能夠按先后順序執行,這被稱為程序次序規則。
對於同一個鎖,一個解鎖操作一定要發生在時間上后發生的另一個鎖定操作之前,也叫做管程鎖定規則。
前一個對volatile的寫操作在后一個volatile的讀操作之前,也叫volatile變量規則。
一個線程內的任何操作必需在這個線程的start()調用之后,也叫作線程啟動規則。
一個線程的所有操作都會在線程終止之前,線程終止規則。
一個對象的終結操作必需在這個對象構造完成之后,也叫對象終結規則。
可傳遞性
更多介紹可以移步並發編程網:
(深入理解java內存模型系列文章:http://ifeve.com/java-memory-model-0)
162,Java中interrupted 和isInterruptedd方法的區別?
interrupted() 和 isInterrupted()的主要區別是前者會將中斷狀態清除而后者不會。Java多線程的中斷機制是用內部標識來實現的,調用Thread.interrupt()來中斷一個線程就會設置中斷標識為true。當中斷線程調用靜態方法Thread.interrupted()來檢查中斷狀態時,中斷狀態會被清零。
非靜態方法isInterrupted()用來查詢其它線程的中斷狀態且不會改變中斷狀態標識。簡單的說就是任何拋出InterruptedException異常的方法都會將中斷狀態清零。無論如何,一個線程的中斷狀態都有可能被其它線程調用中斷來改變。
163,Java中的同步集合與並發集合有什么區別?
同步集合與並發集合都為多線程和並發提供了合適的線程安全的集合,不過並發集合的可擴展性更高。在Java1.5之前程序員們只有同步集合來用且在多線程並發的時候會導致爭用,阻礙了系統的擴展性。Java5介紹了並發集合像ConcurrentHashMap,不僅提供線程安全還用鎖分離和內部分區等現代技術提高了可擴展性。
不管是同步集合還是並發集合他們都支持線程安全,他們之間主要的區別體現在性能和可擴展性,還有他們如何實現的線程安全上。
同步HashMap, Hashtable, HashSet, Vector, ArrayList 相比他們並發的實現(ConcurrentHashMap, CopyOnWriteArrayList, CopyOnWriteHashSet)會慢得多。造成如此慢的主要原因是鎖, 同步集合會把整個Map或List鎖起來,而並發集合不會。並發集合實現線程安全是通過使用先進的和成熟的技術像鎖剝離。
比如ConcurrentHashMap 會把整個Map 划分成幾個片段,只對相關的幾個片段上鎖,同時允許多線程訪問其他未上鎖的片段。
同樣的,CopyOnWriteArrayList 允許多個線程以非同步的方式讀,當有線程寫的時候它會將整個List復制一個副本給它。
如果在讀多寫少這種對並發集合有利的條件下使用並發集合,這會比使用同步集合更具有可伸縮性。
164,什么是線程池? 為什么要使用它?
創建線程要花費昂貴的資源和時間,如果任務來了才創建線程那么響應時間會變長,而且一個進程能創建的線程數有限。為了避免這些問題,在程序啟動的時候就創建若干線程來響應處理,它們被稱為線程池,里面的線程叫工作線程。從JDK1.5開始,Java API提供了Executor框架讓你可以創建不同的線程池。比如單線程池,每次處理一個任務;數目固定的線程池或者是緩存線程池(一個適合很多生存期短的任務的程序的可擴展線程池)
線程池的作用,就是在調用線程的時候初始化一定數量的線程,有線程過來的時候,先檢測初始化的線程還有空的沒有,沒有就再看當前運行中的線程數是不是已經達到了最大數,如果沒有,就新分配一個線程去處理。
就像餐館中吃飯一樣,從里面叫一個服務員出來;但如果已經達到了最大數,就相當於服務員已經用盡了,那沒得辦法,另外的線程就只有等了,直到有新的“服務員”為止。
線程池的優點就是可以管理線程,有一個高度中樞,這樣程序才不會亂,保證系統不會因為大量的並發而因為資源不足掛掉。
165,Java中活鎖和死鎖有什么區別?
活鎖:一個線程通常會有會響應其他線程的活動。如果其他線程也會響應另一個線程的活動,那么就有可能發生活鎖。同死鎖一樣,發生活鎖的線程無法繼續執行。然而線程並沒有阻塞——他們在忙於響應對方無法恢復工作。這就相當於兩個在走廊相遇的人:甲向他自己的左邊靠想讓乙過去,而乙向他的右邊靠想讓甲過去。可見他們阻塞了對方。甲向他的右邊靠,而乙向他的左邊靠,他們還是阻塞了對方。
死鎖:兩個或更多線程阻塞着等待其它處於死鎖狀態的線程所持有的鎖。死鎖通常發生在多個線程同時但以不同的順序請求同一組鎖的時候,死鎖會讓你的程序掛起無法完成任務。
166,如何避免死鎖?
死鎖的發生必須滿足以下四個條件:
互斥條件:一個資源每次只能被一個進程使用。
請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。
三種用於避免死鎖的技術:
加鎖順序(線程按照一定的順序加鎖)
加鎖時限(線程嘗試獲取鎖的時候加上一定的時限,超過時限則放棄對該鎖的請求,並釋放自己占有的鎖)
死鎖檢測
(死鎖原因及如何避免更深理解移步:http://blog.csdn.net/ls5718/article/details/51896159)
167,notify()和notifyAll()有什么區別?
1,notify()和notifyAll()都是Object對象用於通知處在等待該對象的線程的方法。
2,void notify(): 喚醒一個正在等待該對象的線程。
3,void notifyAll(): 喚醒所有正在等待該對象的線程。
兩者的最大區別在於:
notifyAll使所有原來在該對象上等待被notify的線程統統退出wait的狀態,變成等待該對象上的鎖,一旦該對象被解鎖,他們就會去競爭。
notify他只是選擇一個wait狀態線程進行通知,並使它獲得該對象上的鎖,但不驚動其他同樣在等待被該對象notify的線程們,當第一個線程運行完畢以后釋放對象上的鎖,此時如果該對象沒有再次使用notify語句,即便該對象已經空閑,其他wait狀態等待的線程由於沒有得到該對象的通知,繼續處在wait狀態,直到這個對象發出一個notify或notifyAll,它們等待的是被notify或notifyAll,而不是鎖。
168,什么是可重入鎖(ReentrantLock)?
Java.util.concurrent.lock 中的 Lock 框架是鎖定的一個抽象,它允許把鎖定的實現作為Java 類,而不是作為語言的特性來實現。這就為Lock 的多種實現留下了空間,各種實現可能有不同的調度算法、性能特性或者鎖定語義。 ReentrantLock 類實現了Lock ,它擁有與synchronized 相同的並發性和內存語義,但是添加了類似鎖投票、定時鎖等候和可中斷鎖等候的一些特性。此外,它還提供了在激烈爭用情況下更佳的性能。(換句話說,當許多線程都想訪問共享資源時,JVM可以花更少的時候來調度線程,把更多時間用在執行線程上。)
Reentrant 鎖意味着什么呢?簡單來說,它有一個與鎖相關的獲取計數器,如果擁有鎖的某個線程再次得到鎖,那么獲取計數器就加1,然后鎖需要被釋放兩次才能獲得真正釋放。這模仿了synchronized 的語義;如果線程進入由線程已經擁有的監控器保護的synchronized 塊,就允許線程繼續進行,當線程退出第二個(或者后續)synchronized塊的時候,不釋放鎖,只有線程退出它進入的監控器保護的第一個synchronized 塊時,才釋放鎖。
169,讀寫鎖可以用於什么應用場景?
讀寫鎖可以用於 “多讀少寫” 的場景,讀寫鎖支持多個讀操作並發執行,寫操作只能由一個線程來操作
ReadWriteLock對向數據結構相對不頻繁地寫入,但是有多個任務要經常讀取這個數據結構的這類情況進行了優化。ReadWriteLock使得你可以同時有多個讀取者,只要它們都不試圖寫入即可。如果寫鎖已經被其他任務持有,那么任何讀取者都不能訪問,直至這個寫鎖被釋放為止。
ReadWriteLock 對程序性能的提高主要受制於如下幾個因素:
1,數據被讀取的頻率與被修改的頻率相比較的結果。
2,讀取和寫入的時間
3,有多少線程競爭
4,是否在多處理機器上運行
170,堆和棧在內存中的區別是什么?
概念:
棧(stack)是為執行線程留出的內存空間。當函數被調用的時候,棧頂為局部變量和一些 bookkeeping 數據預留塊。當函數執行完畢,塊就沒有用了,可能在下次的函數調用的時候再被使用。棧通常用后進先出的方式預留空間;因此最近的保留塊通常最先被釋放。這么做可以使跟蹤堆棧變的簡單;從棧中釋放塊只不過是指針的偏移而已。
堆(heap)是為動態分配預留的內存空間。和棧不一樣,從堆上分配和重新分配塊沒有固定模式;你可以在任何時候分配和釋放它。這樣使得跟蹤哪部分堆已經被分配和被釋放變的異常復雜;有許多定制的堆分配策略用來為不同的使用模式下調整堆的性能。
區別:
內存分配:
棧:由編譯器自動分配和釋放,存放函數的參數、局部變量、臨時變量、函數返回地址等。
堆:一般人為分配和釋放,對Java而言由系統釋放回收,但對於C++等,必須手動釋放,如果沒有手動釋放會引起內存泄漏。
系統響應:
棧:只要棧的剩余空間大於所申請的空間,系統將為程序提供內存,否則將報異常提示棧溢出。
堆:在記錄空閑內存地址的鏈表中尋找一個空間大於所申請空間的堆結點,然后將該結點從空閑結點鏈表中刪除,並將該結點的空間分配給程序。
大小限制:
棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 windows下,棧的大小是2M,如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。
結論:堆獲得的空間比較靈活,也比較大。
分配效率:
棧:由系統自動分配,速度較快,無法人為控制。
堆:由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便。
存儲內容:
棧:在棧中,第一個進棧的是主函數下一條指令的地址,然后是函數的各個參數,在大多數編譯器中,參數是由右往左入棧,然后是函數中的局部變量。注意,靜態變量不入棧。出棧則剛好順序相反。
堆:一般在堆的頭部用一個字節存放堆的大小,具體內容受人為控制。
171,求出1000之內的所有水仙花數
概念:
水仙花數指的是一個三位整數,它的各位數字的立方和等於這個數本身.。
解法:
public class NumberOfDaffodils {
public static void main(String[] args) {
// TODO Auto-generated method stub
int hundred, ten, bits;
System.out.println("水仙花數為:");
for (int i = 100; i <= 999; i++)
{
hundred = i / 100;
ten = i % 100 / 10;
bits = i % 10;
if (i == hundred * hundred * hundred + ten * ten * ten + bits * bits * bits)
{
System.out.print(i + " ");
}
}
}
}
172,比較一下幾種常用的排序算法,簡單的寫一下你知道的幾種排序算法?
比較:
1.穩定性比較
插入排序、冒泡排序、二叉樹排序、二路歸並排序及其他線形排序是穩定的
選擇排序、希爾排序、快速排序、堆排序是不穩定的
2.時間復雜性比較
插入排序、冒泡排序、選擇排序的時間復雜性為O(n2)
其它非線形排序的時間復雜性為O(nlog2n)
線形排序的時間復雜性為O(n);
3.輔助空間的比較
線形排序、二路歸並排序的輔助空間為O(n);
其它排序的輔助空間為O(1);
4.其它比較
*插入、冒泡排序的速度較慢,但參加排序的序列局部或整體有序時,這種排序能達到較快的速度,但是在這種情況下,快速排序反而慢了。
*當n較小時,對穩定性不作要求時宜用選擇排序,對穩定性有要求時宜用插入或冒泡排序。
*若待排序的記錄的關鍵字在一個明顯有限范圍內時,且空間允許是用桶排序。
*當n較大時,關鍵字元素比較隨機,對穩定性沒要求宜用快速排序。
*當n較大時,關鍵字元素可能出現本身是有序的,對穩定性有要求時,空間允許的情況下宜用歸並排序。
*當n較大時,關鍵字元素可能出現本身是有序的,對穩定性沒有要求時宜用堆排序。
常見的排序算法:
選擇排序
public class SelectionSort {
public void selectionSort(int[] array) {
int temp;
for (int i = 0; i < array.length - 1; i++) {
for (int j = i + 1; j <= array.length - 1; j++) {
if (array[i] > array[j]) {
// 注意和冒泡排序的區別,這里是i和j比較。
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
} // 打印每趟排序結果
for (int m = 0; m <= array.length - 1; m++) {
System.out.print(array[m] + "\t");
}
System.out.println();
}
}
public static void main(String[] args) {
SelectionSort selectionSort = new SelectionSort();
int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 };
selectionSort.selectionSort(array);
for (int m = 0; m <= array.length - 1; m++) {
System.out.print(array[m] + "\t");
}
}
}
插入排序
public class InsertSort {
public void insertSort(int[] array, int first, int last) {
int temp, i, j;
for (i = first + 1; i <= last - 1; i++) {
temp = array[i];
j = i - 1;
while (j >= first && array[j] > temp) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = temp; // 打印每次排序結果
for (int m = 0; m <= array.length - 1; m++) {
System.out.print(array[m] + "\t");
}
System.out.println();
}
}
public static void main(String[] args) {
InsertSort insertSort = new InsertSort();
int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 };
insertSort.insertSort(array, 0, array.length);
for (int i = 0; i <= array.length - 1; i++) {
System.out.print(array[i] + "\t");
}
}
}
快速排序
public class QuickSort {
public int partition(int[] sortArray, int low, int height) {
int key = sortArray[low];
while (low < height) {
while (low < height && sortArray[height] >= key)
height--;
sortArray[low] = sortArray[height];
while (low < height && sortArray[low] <= key)
low++;
sortArray[height] = sortArray[low];
}
sortArray[low] = key; // 打印每次排序結果
for (int i = 0; i <= sortArray.length - 1; i++) {
System.out.print(sortArray[i] + "\t");
}
System.out.println();
return low;
}
public void sort(int[] sortArray, int low, int height) {
if (low < height) {
int result = partition(sortArray, low, height);
sort(sortArray, low, result - 1);
sort(sortArray, result + 1, height);
}
}
public static void main(String[] args) {
QuickSort quickSort = new QuickSort();
int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 };
for (int i = 0; i <= array.length - 1; i++) {
System.out.print(array[i] + "\t");
}
System.out.println();
quickSort.sort(array, 0, 8);
for (int i = 0; i <= array.length - 1; i++) {
System.out.print(array[i] + "\t");
}
}
}
希爾排序
public class ShellSort {
public void shellSort(int[] array, int n) {
int i, j, gap;
int temp;
for (gap = n / 2; gap > 0; gap /= 2) {
for (i = gap; i < n; i++) {
for (j = i - gap; j >= 0 && array[j] > array[j + gap]; j -= gap) {
temp = array[j];
array[j] = array[j + gap];
array[j + gap] = temp;
} // 打印每趟排序結果
for (int m = 0; m <= array.length - 1; m++) {
System.out.print(array[m] + "\t");
}
System.out.println();
}
}
}
public static void main(String[] args) {
ShellSort shellSort = new ShellSort();
int[] array = { 5, 69, 12, 3, 56, 789, 2, 5648, 23 };
shellSort.shellSort(array, array.length);
for (int m = 0; m <= array.length - 1; m++) {
System.out.print(array[m] + "\t");
}
}
}