14. 排序算法總結(jié)
??冒泡排序:依次比較相鄰元素的排序碼,若發(fā)現(xiàn)逆序則交換猪叙。 可以設(shè)置一個變量記錄娇斩,每輪比較的時候是否有元素交換仁卷,若沒有則已經(jīng)有序,沒有必要再繼續(xù)了犬第。(對于原本有序的數(shù)組比較好五督,可由平方階時間復(fù)雜度提升至線性階)。如果兩個元素相等瓶殃,無需改變他們的位置充包,因此冒泡排序是穩(wěn)定的。
??快速排序:是對冒泡排序的一種改進遥椿。通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨立的兩部分基矮,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對這兩部數(shù)據(jù)分別進行快速排序冠场。在選擇比較基數(shù)時一般有(開頭元素家浇、結(jié)尾元素、中間元素碴裙、隨機選擇)幾種方式钢悲,建議隨機或者選擇中間元素,因為若選擇兩個邊界的則當(dāng)序列基本有序時快排退化成冒泡舔株。快排在交換元素的時候很可能就打亂了相等元素的相對位置莺琳,所以快排是不穩(wěn)定的。
??選擇排序法:第一次從 R[0]-R[n-1]中選取最小值载慈,與 R[0]交換惭等,第二次從 R[1]-R[n-1]中選取最小值,與R[1]交換办铡,依次類推辞做。在一趟選擇中,如果當(dāng)前元素比一個元素小寡具,而該小的元素又出現(xiàn)在一個和當(dāng)前元素相等的元素后面秤茅,那么交換后穩(wěn)定性就被破壞了。例如童叠,序列5 8 5 2 9框喳,我們知道第一個元素是2,那么原序列中兩個5的相對順序就破壞了拯钻,所以選擇排序是不穩(wěn)定的帖努。
??插入排序:插入排序是在一個已經(jīng)有序的小序列基礎(chǔ)上,一次插入一個元素粪般。當(dāng)然拼余,剛開始這個有序的小序列只有1個元素。當(dāng)元素基本有序時亩歹,插入排序的比較次數(shù)會大大降低匙监,最好情況下時間復(fù)雜度降為線性凡橱。相等的元素在插入的時候可以不改變順序,所以插入排序是穩(wěn)定的亭姥。
??歸并排序:是將兩個有序的序列進行合并排序(遞歸劃分稼钩,當(dāng)只有1個元素時則即是有序的)。合并過程中我們可以保證如果兩個當(dāng)前元素相等時达罗,我們把處在前面的序列的元素保存在結(jié)果序列的前面坝撑,這樣就保證了穩(wěn)定性。(合并的時候需要一個n長度的輔助數(shù)組)粮揉,在Java的集合工具類Collections.sort(list);使用的就是基于歸并的排序策略巡李。
??希爾排序:改進的插入排序,插入排序每次的增量為1扶认,希爾排序在元素很無序時使用較大的增量厌殉,當(dāng)元素基本有序了酱讶,步長很小殖妇,插入排序?qū)τ谟行虻男蛄行屎芨摺?/strong> 一次插入排序是穩(wěn)定的询筏,不會改變相同元素的相對順序,但在不同的插入排序過程中叠纹,相同元素在各自的插入排序中移動季研,最后其穩(wěn)定性就會被打亂。所以希爾排序是不穩(wěn)定的吊洼。
??堆排序:直接選擇排序的改進(將序列創(chuàng)建成堆之后训貌,每次選擇的堆頂元素即可)。堆的結(jié)構(gòu)是節(jié)點i的孩子為2i和2i+1節(jié)點冒窍,大頂堆要求父節(jié)點大于等于2個子節(jié)點,小頂堆要求父節(jié)點小于等于其2個子節(jié)點豺鼻。在一個長為n的序列综液,堆排序的過程從第n/2開始和其子節(jié)點共3個值選擇最大或最小,這3個值的選擇當(dāng)然不會破壞其穩(wěn)定性儒飒。但當(dāng)n/2-1谬莹,n/2-2,…桩了,1這些父節(jié)點選擇元素時附帽,就會破壞穩(wěn)定性。有可能第n/2個父節(jié)點交換把后面一個元素交換過去了井誉,而第n/2-1個父節(jié)點把后面一個相同的元素沒有交換蕉扮,那么這2個相同的元素之間的穩(wěn)定性就被破壞了。所以堆排序不是穩(wěn)定的排序算法颗圣。
??基數(shù)排序:是按照低位先排序喳钟,然后收集屁使;再按照高位排序,然后再收集奔则; 依次類推蛮寂,直到最高位。有時候有些屬性是有優(yōu)先級順序的易茬,先按低優(yōu)先級排序酬蹋,再按高優(yōu)先級排序。最后的次序就是高優(yōu)先級高的在前抽莱,高優(yōu)先級相同的低優(yōu)先級高的在前范抓。基數(shù)排序基于分別排序岸蜗,分別收集尉咕,所以是穩(wěn)定的。
時間復(fù)雜度
??(1)璃岳、平方階(O(n^2))排序:各類簡單排序年缎,直接插入、直接選擇和冒泡排序铃慷;
??(2)单芜、線性對數(shù)階(O(nlog2n))排序:快速排序、堆排序和歸并排序犁柜;
??(3)洲鸠、O(n^(1+§)))排序,§是介于 0 和 1 之間的常數(shù):希爾排序馋缅;
??(4)扒腕、線性階(O(n))排序:基數(shù)排序,此外還有桶萤悴、箱排序瘾腰。
??說明:當(dāng)原表有序或基本有序時,直接插入排序和冒泡排序?qū)⒋蟠鬁p少比較次數(shù)和移動記錄的次數(shù)覆履,時間復(fù)雜度可降至O(n)蹋盆;
??而快速排序則相反,當(dāng)原表基本有序時硝全,將蛻化為冒泡排序栖雾,時間復(fù)雜度提高為O(n^2);原表是否有序伟众,對簡單選擇排序析藕、堆排序、歸并排序和基數(shù)排序的時間復(fù)雜度影響不大赂鲤。
穩(wěn)定性
??排序算法的穩(wěn)定性:若待排序的序列中噪径,存在多個具有相同關(guān)鍵字的記錄柱恤,經(jīng)過排序,這些記錄的相對次序保持不變找爱,則稱該算法是穩(wěn)定的梗顺;若經(jīng)排序后,記錄的相對次序發(fā)生了改變车摄,則稱該算法是不穩(wěn)定的寺谤。
??穩(wěn)定性的好處:排序算法如果是穩(wěn)定的,那么從一個鍵上排序吮播,然后再從另一個鍵上排序变屁,第一個鍵排序的結(jié)果可以為第二個鍵排序所用∫夂荩基數(shù)排序就是這樣粟关,先按低位排序,逐次按高位排序环戈,低位相同的元素其順序再高位也相同時是不會改變的闷板。另外,如果排序算法穩(wěn)定院塞,可以避免多余的比較遮晚;
??穩(wěn)定的排序算法:冒泡排序、插入排序拦止、歸并排序和基數(shù)排序县遣;
??不是穩(wěn)定的排序算法:選擇排序、快速排序汹族、希爾排序萧求、堆排序。
??注意:凡是對有序甚至基本有序的序列進行操作時顶瞒,都可以考慮使用二分策略(二分查找饭聚,二分插入)來提升性能。
15. Java反射機制
Java.Lang.Class
??在Java中萬事萬物都被抽象成為類搁拙,一個具體的事物則是某個類的實例對象,那么對Java中類的抽象呢(例如法绵,每個類都有成員變量箕速,構(gòu)造方法,成員方法等)朋譬?對類的抽象就是類類型(class type)盐茎,即Class類。
??Class類的實例就是Java中的類徙赢,Class類的構(gòu)造方法是私有的字柠,只能由Java虛擬機創(chuàng)建探越,并且每個類只有一個與之對應(yīng)的Class類實例(不管用何種方式獲取的都一樣)。
??當(dāng)一個類由JVM加載到內(nèi)存中時窑业,都會生成類對應(yīng)的一個Class類的對象钦幔,(更恰當(dāng)?shù)卣f,是被保存在一個同名的.class文件中)常柄。為了生成這個類的對象鲤氢,運行這個程序的Java虛擬機將使用被稱為“類加載器”的子系統(tǒng)。該Class類的對象就保存了運行時Java類的類型信息西潘。
??1卷玉、getName():一個Class對象描述了一個特定類的屬性,Class類中最常用的方法getName喷市,以String的形式返回此Class對象所表示的實體(類相种、接口、數(shù)組類品姓、基本類型或void)名稱寝并。
??2、newInstance():Class還有一個有用的方法可以為類創(chuàng)建一個實例缭黔,這個方法叫做newInstance()食茎。newInstance()方法調(diào)用默認(rèn)構(gòu)造器(無參數(shù)構(gòu)造器)初始化新建對象。
??3馏谨、getClassLoader():返回該類的類加載器别渔。
??4、getComponentType():返回表示數(shù)組組件類型的Class惧互。
??5哎媚、getSuperclass():返回表示此Class所表示的實體(類、接口喊儡、基本類型或void)的超類的Class拨与。
??6、isArray():判定此Class對象是否表示一個數(shù)組類艾猜。
java.lang.reflect.Field
??成員變量也是對象买喧,是java.lang.reflect.Field類的對象:
??* getFields():獲取的是所有的public的屬性,包括父類繼承而來的匆赃;
??* getDeclaredFields():獲取的是該類自己聲明的屬性淤毛,不論訪問權(quán)限。
java.lang.reflect.Method
??成員方法也是對象算柳,一個成員方法就是一個Method類的對象:
??* getMethods():獲取的是所有的public的方法低淡,包括從父類繼承而來的方法;
??* getDeclaredMethods():獲取的是所有該類自己聲明的方法,不論訪問權(quán)限蔗蹋。
??Method類中的如下幾種方法:
??1何荚、getModifiers():以整數(shù)形式返回此 Method 對象所表示方法的 Java 語言修飾符。
??2猪杭、getReturnType():返回一個 Class 對象餐塘,該對象描述了此Method 對象所表示的方法的正式返回類型。
??3胁孙、getName():以 String 形式返回此 Method 對象表示的方法名稱唠倦。
??4、getParameterTypes():按照聲明順序返回 Class 對象的數(shù)組涮较,這些對象描述了此Method 對象所表示的方法的形參類型稠鼻。
??5、getExceptionTypes():返回 Class 對象的數(shù)組狂票,這些對象描述了聲明將此Method 對象表示的底層方法拋出的異常類型候齿。
??在這需要注意的是,利用getModifiers()獲取修飾符并不是簡單的輸出public闺属、static等慌盯,而是以整數(shù)形式返回所表示的方法的Java語言修飾符〉嗥鳎可借助Modifier類的toString()方法來完成亚皂。
java.lang.reflect.Constructor
??構(gòu)造函數(shù)也是對象,是java.lang.reflect.Constructor類的對象:
??getConstructors():獲取所有的public的構(gòu)造函數(shù)(實際上構(gòu)造函數(shù)也不能被繼承国瓮,因此所有的也都是自己定義的)灭必。
??getDeclaredConstructors():獲取自己定義的所有的構(gòu)造函數(shù)。
??在Construction乃摹,Method禁漓,F(xiàn)ield三個類中有一個共同的父類AccessibleObject,定義了取消封裝的操作:setAccessible(Boolean flag)孵睬,
??public void setAccessible(boolean flag) throws SecurityException
??該方法默認(rèn)的參數(shù)是false播歼,表示反射的對象應(yīng)該實施 Java 語言訪問檢查。值為 true 則指示反射的對象在使用時應(yīng)該取消 Java 語言訪問檢查掰读。所謂語言訪問檢查即不能通過反射來訪問類的私有屬性與方法秘狞,因為這樣做會破壞類的封裝性。實在要這樣做就可以:
??setAccessible(true);
16. Java異常處理
??當(dāng)出現(xiàn)程序無法控制的外部環(huán)境問題(如:用戶提供的文件不存在蹈集,文件內(nèi)容損壞谒撼,網(wǎng)絡(luò)不可用......)時,JAVA就會用異常對象來描述雾狈。
??JAVA中用2種方法處理異常:
??1、在發(fā)生異常的地方直接處理抵皱。
??2善榛、將異常拋給調(diào)用者辩蛋,讓調(diào)用者處理。
Java異常的分類
??1移盆、檢查性異常:java.lang.Exception(程序正確)
??因為外在的環(huán)境條件不滿足引發(fā)悼院。JAVA編譯器強制要求處理這類異常,如果不捕獲這類異常咒循,程序?qū)⒉荒鼙痪幾g据途。
??2、運行期異常:java.lang.RuntimeException
??意味著程序存在bug叙甸,如數(shù)字越界颖医,0被除。這類異常需要更改程序來避免裆蒸,JAVA編譯器強制要求處理這類異常熔萧。
??3、錯誤:java.lang.Error
??一般很少見僚祷,也很難通過程序解決佛致。它可能源于程序的bug,但一般更可能源于環(huán)境問題辙谜,如內(nèi)存耗盡俺榆。錯誤在程序中無需處理,而由運行環(huán)境處理装哆。
??java.lang.Exception和java.lang.Error繼承自java.lang.Throwable罐脊,而java.lang.RuntimeException繼承自java.lang.Exception。
異常處理
??1烂琴、Try....catch:程序運行產(chǎn)生異常時爹殊,將從異常發(fā)生點中斷程序并向外拋出異常信息。
??2奸绷、Finally:如果把finally塊置try...catch...語句后梗夸,finally塊一般都會得到執(zhí)行,它相當(dāng)于一個萬能的保險号醉,即使前面的try塊發(fā)生異常反症,而又沒有對應(yīng)的catch塊,finally塊將會馬上執(zhí)行畔派。 若try語句快中存在return語句铅碍,則會在finally語句執(zhí)行完再返回。(若try語句和finally中都有return线椰,則返回的是finally語句塊中的)胞谈。
??以下情形,finally塊將不會被執(zhí)行;
??1烦绳、finally塊中發(fā)生了異常卿捎。
??2、程序所在線程死亡径密。
??3午阵、前面代碼使用了System.exit();
??4享扔、關(guān)閉CPU底桂。
??注意:return要返回的值會存儲到一個臨時棧,若finally塊中只是改變要返回變量的值惧眠,而不返回籽懦,則臨時棧中的值不會改變。
public static int returnTry() {
int a = 0;
try {
a = 1;
return 1;
} finally {
// 只是改變了a的值锉试,但是返回的還是1
System.out.println("改變a的值Cㄊ!");
a = 2;
}
}
public static int returnFinally() {
int a = 0;
try {
a = 1;
return 1;
} finally {
a = 2;
// 返回2
return a;
}
}