《算法》讀書筆記(未完)

排序

排序模板

package algorithm.sort;
import java.lang.Comparable;
public abstract class SortExample<T extends Comparable<T>> {
    public abstract void sort(T[] a);

    public boolean less(T v, T w){
        return v.compareTo(w)<0;
    }
    public void exch(T[] a,int i,int j){
        T t=a[i];
        a[i]=a[j];
        a[j]=t;
    }
    public void show(T[] a){
        for (T comparable : a) {
            System.out.println(comparable + "");
        }
    }
    public boolean isSorted(T[] a){
        for(int i=1;i<a.length;i++){
            if(less(a[i],a[i=1])) return false;
        }
        return true;
    }
}

插入排序

在尚未排序元素中按順序遍歷每個亂序元素稽煤,將每個亂序元素按順序與有序元素比較,放在合適的位置后,操作下一個亂序元素

package algorithm.sort;
public class Insertion<T extends Comparable<T>> extends SortExample<T>{
    @Override
    public void sort(T[] a){
        int N = a.length;
        for(int i=1;i<N;i++){
            for(int j=i;j>0&&less(a[j],a[j=1]);j--){
                exch(a,j,j-1);
            }
        }
    }
}

希爾排序

希爾排序基于插入排序谜喊,插入排序的比較與交換是相鄰元素之間的所以大規(guī)模的亂序會很慢,希爾排序的思想是將數(shù)組中任意間隔為h的元素都是有序的倦始,即是說希爾排序意在構(gòu)建多個相互獨(dú)立的有序數(shù)組斗遏。伴隨著間隔逐漸的縮減為1后便是有序數(shù)組,而各個間隔為h的獨(dú)立數(shù)組的實(shí)現(xiàn)都是插入排序楣号。
對于中等大小的數(shù)組最易,希爾排序的運(yùn)行時(shí)間是可以接受的,代碼量小炫狱,且不需要使用額外的內(nèi)存藻懒,對于很大的N其它高效算法只會比希爾排序快兩倍左右,但其更復(fù)雜视译。
此處希爾排序?qū)崿F(xiàn)中的h的使用的是實(shí)時(shí)計(jì)算出的1/2(3^k-1)序列嬉荆,也可以將h的序列存儲在數(shù)組當(dāng)中供使用。

package algorithm.sort;
public class Shell<T extends Comparable<T>> extends SortExample<T>{
    @Override
    public void sort(T[] a) {
        int N = a.length;
        int h = 1;
        while(h<N/3)
            h=3*h+1;
        while (h>=1){
            for(int i=h;i<N;i++){
                for(int j=i;j>=h&&less(a[j],a[j-h]);j-=h)
                    exch(a,j,j-h);
            }
            h=h/3;
        }
    }
}

歸并排序

歸并排序的實(shí)現(xiàn)思想為:先將左邊排序酷含,再將右邊排序鄙早,最后左右統(tǒng)一起來汪茧,在自頂向下調(diào)用的過程中總是先排左再排右最后聯(lián)合,有種后序遍歷的感覺限番,排序主要右merge算法實(shí)現(xiàn)舱污。
歸并排序在最壞的情況下的比較次數(shù)是~NlgN,這是任何最好的算法在最壞的 情況下的最小的比較次數(shù)弥虐。

package algorithm.sort;
public class Merge{
    private static Comparable[] aux;
    public static void sort(Comparable[] a){
        aux = new Comparable[a.length];
        sort(a,0,a.length-1);
    }
    private static void sort(Comparable[] a,int lo,int hi){
        if(hi<=lo) return;
        int mid = lo+(hi-lo)/2;
        sort(a,lo,mid);
        sort(a,mid+1,hi);
        merge(a,lo,mid,hi);
    }

    public static void merge(Comparable[] a,int lo,int mid ,int hi){
        int i = lo,j = mid+1;
        for (int k = lo;k<=hi;k++)
            aux[k] = a[k];
        for(int k=lo;k<=hi;k++)
            if(i>mid)
                a[k]=aux[j++];
            else if (j>hi)
                a[k] = aux[i++];
            else if (less(aux[j],aux[i]))
                a[k] = aux[j++];
            else
                a[k] = aux[i++];
    }
    private static boolean less(Comparable v, Comparable w){
        return v.compareTo(w)<0;
    }
}

快速排序

快速排序適用于各種不同的輸入數(shù)據(jù)且在一般應(yīng)用中比其他排序算法都要快得多扩灯。并且快速排序是原地排序,只需要一個很小的輔助棧霜瘪,不會有太多余的內(nèi)存開銷珠插,將長度為N的數(shù)組排序所需要的時(shí)間和NlgN成正比。
快速排序有著分治的思想:將一個數(shù)組分為兩個數(shù)組颖对,當(dāng)兩個子數(shù)組都有序時(shí)捻撑,整個數(shù)組自然有序了。
快速排序的缺點(diǎn)便是很脆弱缤底,一不小心便會使得性能變得惡劣顾患,主要注意一下幾個點(diǎn):原地切分(不在遞歸調(diào)用中新建數(shù)組);越界训堆;保持隨機(jī)性(消除對輸入的依賴)描验;終止循環(huán);終止遞歸坑鱼;切分元素值會有重復(fù)的情況

package algorithm.sort;
public class Quick <T extends Comparable<T>> extends SortExample<T>{
    @Override
    public void sort(T[] a) {
        sort(a,0,a.length-1);
    }
    private void sort(T[] a,int lo,int hi){
        //該算法實(shí)現(xiàn)的核心思想是將數(shù)組首位的元素放在合適的位置膘流,使得元素左邊的小于元素右邊的
        if(hi<=lo)return;
        int j=partition(a,lo,hi);
        sort(a,lo,j-1);
        sort(a,j+1,hi);
    }
    private int partition(T[] a,int lo,int hi){
        //左右掃描指針
        int i=lo,j=hi+1;
        T v = a[lo];
        while(true){
            //從左到右遍歷,直到某一值大于首位元素鲁沥,或者遍歷完整個數(shù)組
            while(less(a[++i],v))
                if(i==hi)break;
            //從右到左遍歷呼股,直到某一值小于首位元素,或者遍歷完整個數(shù)組
            while(less(v,a[--j]))
                if(j==lo)break;
                
            if(i>=j) break;
            //交換該兩元素画恰,使得整個數(shù)組呈現(xiàn)左邊小右邊大的趨勢
            exch(a,i,j);
        }
        //此時(shí)整個數(shù)組已經(jīng)遍歷完畢彭谁,掃描指針已經(jīng)重合,重合的位置有著左邊小右邊大的特點(diǎn)允扇,將首元素移動該位置缠局,并返回以供歸并
        exch(a,lo,j);
        return j;
    }
}

對于小數(shù)組而言,可以做如下更改將快速排序切換到插入排序
if(hi<=lo) return;
更改為
if(hi<=lo+M){Insertion.sort(a,lo,hi); return;}
當(dāng)數(shù)據(jù)具有大量重復(fù)元素時(shí)考润,通過將原切分
a[lo,j-1]<v=a[j]<a[j+1,hi]
更改為
a[lo..lt-1]<v=a[lt,gt]<a[gt+1,hi]
具體實(shí)現(xiàn)為三向切分的快速排序

三向切分的快速排序

package algorithm.sort;
public class Quick3way extends Quick {
    @Override
    protected void sort(Comparable[] a, int lo, int hi) {
        if(hi<=lo)return;
        int lt = lo,i=lo+1,gt=hi;
        Comparable v = a[lo];
        while(i<=gt){
            int cmp = a[i].compareTo(v);
            if(cmp<0)exch(a,lt++,i++);
            else if(cmp>0)exch(a,i,gt--);
            else i++;
        }
        sort(a,lo,lt-1);
        sort(a,gt+1,hi);
    }
}

堆/隊(duì)列

堆常為優(yōu)先隊(duì)列的實(shí)現(xiàn)采用的數(shù)據(jù)結(jié)構(gòu)狭园,由于優(yōu)先隊(duì)只在乎極值,所以在實(shí)現(xiàn)過程中少去了許多比較與交換糊治。

基于堆的優(yōu)先隊(duì)列

public class MaxPQ <Key extends Comparable<Key>>{
    //基于堆的完全二叉樹唱矛,pq[0]未使用
    private Key[] pq;
    private int N=0;
    //創(chuàng)建優(yōu)先隊(duì)列
    public MaxPQ(){
        ***
    }
    public MaxPQ(int max){
        pq = (Key[])new Comparable[max+1];
    }
    public MaxPQ(Key[] a){
      ***
    }
    public void insert(Key v){
        pq[++N] = v;
        swim(N);
    }
    //返回最大元素
    public Key max(){
        return pq[1];
    }
    //刪除、返回最大元素
    public Key delMax(){
        Key max = pq[1];
        exch(1,N--);
        //N+1處不再使用,設(shè)為null以便系統(tǒng)回收空間
        pq[N+1]=null;
        sink(1);
        return max;
    }
    //返回隊(duì)列是否為空
    public boolean isEmpty(){
        return N==0;
    }
    public int size(){
        return N;
    }
    private boolean less(int i,int j){
        return pq[i].compareTo(pq[j])<0;
    }
    private void exch(int i,int j){
        Key t = pq[i];pq[i] = pq[j];pq[j] = t;
    }

    //從下到上绎谦,將更大的節(jié)點(diǎn)向上傳遞
    private void swim(int k){
        while(k>1&&less(k/2,k)){
            exch(k/2,k);
            k = k/2;
        }
    }
    //從上到下管闷,將頂部的節(jié)點(diǎn)向下傳遞
    private void sink(int k){
        while(2*k<=N){
            int j = 2*k;
            //選出當(dāng)前節(jié)點(diǎn)的最大子節(jié)點(diǎn)
            if(j<N&&less(j,j+1)) j++;
            //若當(dāng)前節(jié)點(diǎn)不小于選出的最大子節(jié)點(diǎn),則打破循環(huán)
            if(!less(k,j)) break;
            exch(k,j);
            k=j;
        }
    }
}

基于堆的索引優(yōu)先隊(duì)列

適用于優(yōu)先隊(duì)列中的元素已經(jīng)被多個地方的無關(guān)用例代碼用整數(shù)索引來引用的情況
待修改:

public class IndexMinPQ <Key extends Comparable<Key>>{
    private int N;
    //元素對于的整數(shù)索引,索引二叉堆窃肠,這個是堆
    private int[] pq;
    //整數(shù)索引在pq中的位置包个,可以直接實(shí)現(xiàn)索引的訪問與修改;訪問索引i:pq[qp[i]]
    private int[] qp;
    //元素
    private Key[] keys;
    public IndexMinPQ(int maxN){
        keys = (Key[]) new Comparable[maxN+1];
        pq = new int[maxN+1];
        qp = new int[maxN+1];
        for(int i = 0;i<=maxN;i++) qp[i] =-1;
    }
    public Boolean isEmpty(){
        return N ==0;
    }
    public boolean contains(int k){
        return qp[k] !=-1;
    }
    public void insert(int k,Key key){
        N++;
        qp[k] = N;
        qp[N] = k;
        keys[k]=key;
        swim(N);
    }
    public Key min(){
        return keys[pq[1]];
    }
    public int delMin(){
        int indexOfMin = pq[1];
        exch(1,N--);
        sink(1);
        keys[pq[N+1]] = null;
        qp[pq[N+1]] = -1;
        return indexOfMin;
    }
    //從下到上,將更大的節(jié)點(diǎn)向上傳遞
    private void swim(int k){
        while(k>1&&less(k/2,k)){
            exch(k/2,k);
            k = k/2;
        }
    }
    //從上到下冤留,將頂部的節(jié)點(diǎn)向下傳遞
    private void sink(int k){
        while(2*k<=N){
            int j = 2*k;
            //選出當(dāng)前節(jié)點(diǎn)的最大子節(jié)點(diǎn)
            if(j<N&&less(j,j+1)) j++;
            //若當(dāng)前節(jié)點(diǎn)不小于選出的最大子節(jié)點(diǎn)赃蛛,則打破循環(huán)
            if(!less(k,j)) break;
            exch(k,j);
            k=j;
        }
    }
    private boolean less(int i,int j){
        return keys[pq[i]].compareTo(keys[pq[j]])<0;
    }
    private void exch(int i,int j){
        int t1 = pq[i];pq[i] = pq[j];pq[j] = t1;
        int t2 = qp[i];qp[i] = qp[j];qp[j] = t2;
    }
    //返回最小元素的索引
    public int minIndex(){
        return pq[1];
    }
    //將索引為k的元素設(shè)為key
    public void change(int k,Key key){
        keys[k] = key;
        //索引為k的元素的位置
        swim(qp[k]);
        sink(qp[k]);
    }
    public void delete(int k){
        int index = qp[k];
        exch(index,N--);
        swim(index);
        sink(index);
        keys[k] = null;
        qp[k]=-1;
    }
}

小結(jié)

  • 多數(shù)情況下快速排序是最佳選擇;當(dāng)穩(wěn)定性很重要搀菩,且空間又不是問題的同時(shí),歸并排序也還行破托。
  • 原始類型數(shù)據(jù)的排序更快肪跋,非原始類數(shù)據(jù)排序過程中存儲引用所需要的空間以及引用訪問的成本再加上調(diào)用方法的開銷都是比原始類型多得多。
  • Java系統(tǒng)庫中的主要排序方法java.tuil.Arrays.sort();根據(jù)不同的參數(shù)類型土砂,都代表了一系列排序算法
  • 以上算法包括了Java現(xiàn)代系統(tǒng)的核心組成部分州既,所以實(shí)際應(yīng)用開發(fā)中Java的Arrays.sort()實(shí)現(xiàn)再加上自己實(shí)現(xiàn)的CompareTo()、compare()已經(jīng)夠用萝映,因?yàn)檫@里面已經(jīng)涵蓋了經(jīng)典的三向快速排序以及歸并排序吴叶。


    各種排序算法的性能特點(diǎn)

查找

小錯誤

  • 抽象類中的靜態(tài)方法不可以被重寫
  • 抽象方法不可以是靜態(tài)的
  • Raw use of parameterized class '*':含有泛型參數(shù)的類不能使用其原生態(tài)類型,不能不對泛型參數(shù)進(jìn)行定義便將該泛型類進(jìn)行使用
  • unchecked call to * as a member of the raw type $:泛型的引用后面沒有規(guī)定類型
  • 泛型數(shù)組創(chuàng)建存在的問題
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末序臂,一起剝皮案震驚了整個濱河市蚌卤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奥秆,老刑警劉巖逊彭,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異构订,居然都是意外死亡侮叮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門悼瘾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來囊榜,“玉大人,你說我怎么就攤上這事亥宿⌒渡祝” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵箩绍,是天一觀的道長孔庭。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么圆到? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任怎抛,我火速辦了婚禮,結(jié)果婚禮上芽淡,老公的妹妹穿的比我還像新娘马绝。我一直安慰自己,他們只是感情好挣菲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布富稻。 她就那樣靜靜地躺著,像睡著了一般白胀。 火紅的嫁衣襯著肌膚如雪椭赋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天或杠,我揣著相機(jī)與錄音哪怔,去河邊找鬼。 笑死向抢,一個胖子當(dāng)著我的面吹牛认境,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挟鸠,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼叉信,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了艘希?” 一聲冷哼從身側(cè)響起硼身,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎枢冤,沒想到半個月后鸠姨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淹真,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年讶迁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片核蘸。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡巍糯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出客扎,到底是詐尸還是另有隱情祟峦,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布徙鱼,位于F島的核電站宅楞,受9級特大地震影響针姿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厌衙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一距淫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧婶希,春花似錦榕暇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至筒饰,卻和暖如春缴啡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瓷们。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工盟猖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人换棚。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像反镇,于是被迫代替她去往敵國和親固蚤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354