判環(huán)算法以及鏈表常見算法題

??由于涉及到Floyd判環(huán)算法,故先簡單闡述一下Floyd判環(huán)算法原理。

Floyd判環(huán)算法

算法原理:設(shè)置兩個指針同時從頭結(jié)點(diǎn)向后訪問(當(dāng)兩指針后繼不為null),不過兩指針的訪問速度不一致,第一個指針每次后移一個元素,第二個指針每次后移兩個元素。若鏈表成環(huán),則兩指針在某一時刻必然相遇,否則不相遇

計算環(huán)長度:
當(dāng)指針相遇時,保持其中一個指針不動,另一個指針向后訪問,每次后移一個單位,當(dāng)兩指針再次相遇時,該指針移動的單位即為環(huán)的長度

計算環(huán)起始節(jié)點(diǎn):
第一次相遇后,慢指針從頭結(jié)點(diǎn)向后訪問,快指針從相遇點(diǎn)同時向后訪問,不過兩者的移動速度都為1,當(dāng)兩者再次相遇時,該相遇的節(jié)點(diǎn)即為環(huán)的起始節(jié)點(diǎn),分析如下:

如下圖所示,假設(shè)h為鏈表頭結(jié)點(diǎn),p為兩指針第一次相遇節(jié)點(diǎn),m為頭結(jié)點(diǎn)到起始節(jié)點(diǎn)走過的路程(經(jīng)過的節(jié)點(diǎn)個數(shù)),n為環(huán)起始節(jié)點(diǎn)到p的路程,則對于第一次相遇有:

慢指針走過的路程:s=m+n+aC

快指針走過的路程:2
s=m+n+bC

a,b分別表示兩指針相遇前第一次經(jīng)過p點(diǎn)后繞環(huán)的圈數(shù),C表示環(huán)的長度,由于快指針的速度是慢指針的兩倍,時間一樣,故快指針走過的路程是慢指針的兩倍。

兩式相減有s=(b-a)
C=m+n+a*C

故m+n恰為環(huán)長度的整數(shù)倍

慢指針從頭結(jié)點(diǎn)到起始節(jié)點(diǎn)走了m,由于同時同速度快指針也走了m,而移動前快指針距起始節(jié)點(diǎn)距離為n,m+n恰為環(huán)長度的整數(shù)倍,故快指針移動m后也到了起始節(jié)點(diǎn),即此時相遇點(diǎn)為起始節(jié)點(diǎn)


1.尋找鏈表的第n個節(jié)點(diǎn)

/**
 * 時間復(fù)雜度為O(n),空間復(fù)雜度為O(1)
 * @param head
 * @param kth
 * @return
 */
public static SNode getKthNode(SNode head,int kth){
    SNode temNode=head,kthNode=null;
    for(int count=1;count<kth;count++) {
        if(temNode!=null)
            temNode=temNode.getNext();
    }
    while(temNode!=null) {
        if(kthNode==null)
            kthNode=head;
        else
            kthNode=kthNode.getNext();
        temNode=temNode.getNext();
    }
    if(kthNode!=null)
        return kthNode;
    return null;
}

2.1判斷鏈表是否以null結(jié)尾,是否包含環(huán)

/**
 * 時間復(fù)雜度為O(n),空間復(fù)雜度為O(1)
 * @param head
 * @return
 */
public static boolean doesLinkedListContainsLoop(SNode head) {
    if(head==null)
        return false;
    SNode fastNode=head,slowNode=head;
    while(fastNode.getNext()!=null&&fastNode.getNext().getNext()!=null) {
        slowNode=slowNode.getNext();
        fastNode=fastNode.getNext().getNext();
        if(fastNode==slowNode)
            return true;
    }
    return false;
}

2.2判斷給定的鏈表是否以null結(jié)束。如果鏈表中存在環(huán),找到環(huán)的起始節(jié)點(diǎn)

/**
 * @param head
 * @return
 */
public static SNode findBeginOfLoop(SNode head) {
    SNode slowNode=head,fastNode=head;
    boolean loopExists=false;
    if(head==null)
        return null;
    while(fastNode.getNext()!=null&&fastNode.getNext().getNext()!=null) {
        slowNode=slowNode.getNext();
        fastNode=fastNode.getNext().getNext();
        if(slowNode==fastNode) {
            loopExists=true;
            break;
        }
    }
    if(loopExists) {  //環(huán)存在
        slowNode=head;
        while(slowNode!=fastNode) {
            slowNode=slowNode.getNext();
            fastNode=fastNode.getNext();
        }
        return slowNode;  //返回環(huán)開始節(jié)點(diǎn)
    }
    return null;  //環(huán)不存在
}

2.3判斷鏈表是否存在環(huán),若存在,則返回環(huán)的長度,否則返回0

/**
 * @param head
 * @return
 */
public static int findLoopLength(SNode head) {
    SNode slowNode=head,fastNode=head;
    boolean loopExists=false;
    int counter=0;
    if(head==null)
        return 0;
    while(fastNode.getNext()!=null&&fastNode.getNext().getNext()!=null) {
        slowNode=slowNode.getNext();
                    counter++;
        fastNode=fastNode.getNext().getNext();
        if(slowNode==fastNode) {
            loopExists=true;
            break;
        }
    }
    if(loopExists) {
        fastNode=fastNode.getNext();
        while(fastNode!=slowNode) {
            fastNode=fastNode.getNext();
            counter++;
        }
        return counter;
    }
    return 0;
}

3.將一個循環(huán)鏈表變成兩個循環(huán)鏈表

/**
 * @param head
 * @param head1
 * @param head2
 */
public static void splitList(SNode head,SNode head1,SNode head2) {
    SNode slowNode=head,fastNode=head;
    if(head==null)
        return;
    //如果循環(huán)鏈表有奇數(shù)個節(jié)點(diǎn),則fastNode.getNext()指向head
    //如果循環(huán)鏈表有偶數(shù)個節(jié)點(diǎn),則fastNode.getNext().getNext()指向head
    while(fastNode.getNext()!=head&&fastNode.getNext().getNext()!=head) {
        fastNode=fastNode.getNext().getNext();
        slowNode=slowNode.getNext();
    }
    if(fastNode.getNext().getNext()==head) {
        fastNode=fastNode.getNext();
    }
    //head1指向前半部分的head指針
    head1=head;
    //head2指向后半部分的指針
    if(head.getNext()!=head)
        head2=slowNode.getNext();
    //把后半部分變成環(huán)
    fastNode.setNext(slowNode.getNext());
    //把前半部分變成環(huán)
    slowNode.setNext(head);
}

4.有序鏈表中插入一個節(jié)點(diǎn)

/**
 * 時間復(fù)雜度為O(n),空間復(fù)雜度為O(1)
 * @param head
 * @param newNode
 * @return
 */
public static SNode insertSortedList(SNode head,SNode newNode) {
    SNode current=head,temp=null;
    if(head==null)
        return newNode;
    while(current!=null) {
        temp=current;
        current=current.getNext();
    }
    newNode.setNext(current);
    temp.setNext(newNode);
    return head;
}

5.1鏈表逆置

/**
 * 時間復(fù)雜度為O(n),空間復(fù)雜度為O(1)
 * @param head
 * @return
 */
public static SNode reverseList(SNode head) {
    SNode temp=null,nextNode=null;
    while(head!=null) {
        nextNode=head.getNext();
        head.setNext(temp);
        temp=head;
        head=nextNode;
    }
    return temp;
}

5.2逐對逆置鏈表,假如初始鏈表為1 2 3 4 x,逐對逆置后為2 1 4 3 x

/**
 * @param head
 * @return
 */
public static SNode reversePairRecursive(SNode head) {
    SNode temp;
    if(head==null||head.getNext()==null)
        return head;
    else {
        temp=head.getNext();
        head.setNext(temp.getNext());
        temp.setNext(head);
        head=temp;
        head.getNext().setNext(reversePairRecursive(head.getNext().getNext()));
        return head;
    }
}

6.判斷兩個鏈表是否相交成為一個單鏈表,若相交,得出兩鏈表的相交節(jié)點(diǎn)

/**
 * 思路:兩鏈表相交之后的長度必然相等,先獲得兩鏈表的長度,讓更長的鏈表先后移兩鏈表長度差個單位,再一起向后遍歷,直到出現(xiàn)兩節(jié)點(diǎn)相等或到達(dá)表尾
 * 時間復(fù)雜度為O(m,n),空間復(fù)雜度為O(1)
 * @param list1
 * @param list2
 * @return
 */
public static SNode findIntersectingNode(SNode list1,SNode list2) {
    int l1=0,l2=0,diff=0;
    SNode head1=list1,head2=list2;
    while(head1!=null) {
        l1++;
        head1=head1.getNext();
    }
    while(head2!=null) {
        l2++;
        head2=head2.getNext();
    }
    if(l1<l2) {
        head1=list2;
        head2=list1;
        diff=l2-l1;
    }else {
        head1=list1;
        head2=list2;
        diff=l1-l2;
    }
    for(int i=0;i<diff;i++)
        head1=head1.getNext();
    while(head1!=null&&head2!=null) {
        if(head1==head2)
            return head1;
        head1=head1.getNext();
        head2=head2.getNext();
    }
    return null;
}

7.尋找鏈表的中間節(jié)點(diǎn):設(shè)置兩個指針,第一個指針的移動速度是第二個指針的兩倍,當(dāng)?shù)谝粋€指針到達(dá)表尾,第二個指針即指向中間節(jié)點(diǎn)

/**
 * 時間復(fù)雜度為O(n),空間復(fù)雜度為O(1)
 * @param head
 * @return
 */
public static SNode findMiddle(SNode head) {
    SNode point1=head,point2=head;
    int i=0;
    while(point1.getNext()!=null) {
        if(i==0) {
            point1=point1.getNext();
            i=1;
        }else if(i==1) {
            point1=point1.getNext();
            point2=point2.getNext();
            i=0;
        }
    }
    return point2;
}

8.檢查鏈表長度是奇數(shù)還是偶數(shù),每次后移兩個元素,返回1表示奇數(shù),返回0表示偶數(shù)

/**
 * 時間復(fù)雜度為O(n/2),空間復(fù)雜度為O(1)
 * @param head
 * @return
 */
public static int isLinkedListLengthEven(SNode head) {
    while(head!=null&&head.getNext()!=null) {
        head=head.getNext().getNext();
    }
    if(head==null)
        return 0;
    else
        return 1;
}

9.合并兩個有序鏈表

/**
 * 時間復(fù)雜度為O(n)
 * @param a
 * @param b
 * @return
 */
public static SNode mergeList(SNode a,SNode b) {
    SNode result=null;
    if(a==null)
        return b;
    if(b==null)
        return a;
    if(a.getData()<=b.getData()) {
        result=a;
        result.setNext(mergeList(a.getNext(), b));
    }else {
        result=b;
        result.setNext(mergeList(a,b.getNext()));
    }
    return result;
}

10.從表尾開始輸出鏈表

/**
 * @param head
 */
public static void printListFromEnd(SNode head) {
    if(head==null)
        return;
    printListFromEnd(head.getNext());
    System.out.println(head.getData());
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內(nèi)容

  • //leetcode中還有花樣鏈表題,這里幾個例子,冰山一角 求單鏈表中結(jié)點(diǎn)的個數(shù)----時間復(fù)雜度O(n)這是最...
    暗黑破壞球嘿哈閱讀 1,524評論 0 6
  • 轉(zhuǎn)載請注明出處:http://www.lxweimin.com/p/c65d9d753c31 在上一篇博客《數(shù)據(jù)結(jié)構(gòu)...
    Alent閱讀 3,523評論 4 74
  • B樹的定義 一棵m階的B樹滿足下列條件: 樹中每個結(jié)點(diǎn)至多有m個孩子。 除根結(jié)點(diǎn)和葉子結(jié)點(diǎn)外,其它每個結(jié)點(diǎn)至少有m...
    文檔隨手記閱讀 13,281評論 0 25
  • 本來以為一篇就能寫完的,后來又感覺一篇多了一些,所以關(guān)于鏈表的簡單算法題有加了個續(xù)篇,和上一篇一樣,難度不會太大。...
    zero_sr閱讀 556評論 0 4
  • 大學(xué)的時候不好好學(xué)習(xí),老師在講臺上講課,自己在以為老師看不到的座位看小說,現(xiàn)在用到了老師講的知識,只能自己看書查資...
    和玨貓閱讀 1,465評論 1 3