鏈表結(jié)束篇:鏈表的五種常見操作

鏈表結(jié)束篇:鏈表的五種常見操作

  • 單鏈表翻轉(zhuǎn)
  • 檢測一個鏈表中是否有環(huán)
  • 兩個有序的鏈表合并
  • 刪除鏈表倒數(shù)第 n 個結(jié)點(diǎn)
  • 求鏈表的中間結(jié)點(diǎn)

本片筆記的GitHub地址

更新一下 五苍凛、 的思路

規(guī)則界限就是:
快指針走的長度(鏈表長度 + null) = 快length = length + 1
快指針先走 n + 1
然后慢指針 和 快指針一起走,當(dāng)快指針走到 快length
慢指針走到:
快length - (n + 1) = length + 1 - (n + 1) = length - n
第 (length - n) 位置就是倒數(shù)第(n + 1)個結(jié)點(diǎn)。

一庐氮、單鏈表翻轉(zhuǎn)

判斷回文串 這篇筆記中有詳細(xì)記錄独悴,此處只列舉代碼了述吸。

 /**
     * 鏈表的反轉(zhuǎn)可以想象成摸石頭過河那個游戲
     * 兩只腳配上三塊磚頭就可以前進(jìn)了
     * <p>
     * 每次循環(huán)做的事:
     * ①第一塊磚頭的next改變
     * ②三塊磚頭前移一個位置
     *
     * @return 反轉(zhuǎn)后的單鏈表
     */
    public SingleLinked<E> reverse() {
        Node<E> realHead = first;
        //無結(jié)點(diǎn)
        if (realHead == null) {
            return this;
        }

        Node<E> temp = realHead.getNext();
        //只有一個頭結(jié)點(diǎn)
        if (temp == null) {
            return this;
        }

        Node<E> flag = temp.getNext();

        //把realHead與其下一個結(jié)點(diǎn)的連接斷開
        realHead.setNext(null);

        //一次循環(huán)盏求,只改變一個結(jié)點(diǎn)的指針反轉(zhuǎn)
        //剩下就是temp flag 等引用的變化
        while (flag != null) {
            temp.setNext(realHead);
            realHead = temp;

            temp = flag;

            flag = flag.getNext();
        }

        temp.setNext(realHead);
        first = temp;

        return this;
    }

二噪漾、檢測一個鏈表中是否有環(huán)

思路:使用快慢指針充活,快指針每次循環(huán)比慢指針多走一步蜂莉,這樣當(dāng)快指針走到 null 時蜡娶,慢指針走了一半, 若鏈表中有環(huán)(尾結(jié)點(diǎn)的后繼指針指向了鏈表中的某一結(jié)點(diǎn))映穗,那么 快指針 永遠(yuǎn)不會為 null窖张,但是會出現(xiàn) 快指針與慢指針 指向同一結(jié)點(diǎn)的情況,以此來判斷一個單鏈表是否有環(huán)蚁滋。

image

畫這個圖好累宿接。。辕录。


    public boolean hasCircle() {
        Node<E> fast = first;
        Node<E> slow = first;

        while (fast != null && fast.getNext() != null) {
            fast = fast.getNext().getNext();
            slow = slow.getNext();

            if (fast == slow) {
                //說明有環(huán)睦霎,fast追到了 slow
                return true;
            }
        }

        return false;
    }

三、兩個有序的鏈表合并

思路:若兩個鏈表都是升序鏈表踏拜。
取兩個鏈表(A,B)的頭結(jié)點(diǎn)比較誰兴橛:將小的作為新鏈表的頭結(jié)點(diǎn)。假設(shè) B 小速梗,接下來比較 B的頭結(jié)點(diǎn).next 與 A 的頭結(jié)點(diǎn)誰小肮塞,小的作為新鏈表的頭結(jié)點(diǎn)的 next。

image

可以使用遞歸解決此類問題:

import bean.Node;
import exception.CannotControlException;
import exception.UnorderedLinkedException;

/**
 * @author : zyf
 * @since : 2018-12-28 15:55
 **/
public class SingleLinkedUtil {

    private static final int ASC = 0;
    private static final int DES = 1;
    private static final int NULL_LINKED = 2;//表示空鏈表
    private static final int UNORDERED = -1;


    /**
     * 判斷鏈表是否有序
     * @param linked
     * @return 是升序還是降序
     */
    public static int isOrderLinked(SingleLinked<Integer> linked){
        Node<Integer> first = linked.getFirst();
        if(first == null){
            return NULL_LINKED;
        }

        Node<Integer> next = first.getNext();

        //0 表示升序
        //1 表示返序
        int flag = UNORDERED;

        while (next != null){
            if(next.getT() >= first.getT()){
                //大于 則 flag 為0

                if(flag == UNORDERED){
                    //說明是第一次判斷
                    flag = ASC;
                }else {
                    if(flag != ASC){
                        //說明在循環(huán)中的某一次將 flag 的值改為 1 了
                        //說明不是一個有序鏈表
                        return UNORDERED;
                    }else {
                        first = next;
                        next = next.getNext();
                    }
                }
            }else {
                //小于 則 flag 為 1
                if(flag == -1){
                    flag = DES;
                }else {
                    if(flag != DES){
                        return UNORDERED;
                    }else {
                        first = next;
                        next = next.getNext();
                    }
                }
            }

        }

        return flag;
    }

    public static SingleLinked<Integer> mergeOrderedLinked(SingleLinked<Integer> a,SingleLinked<Integer> b){
        //先判斷 a,b 是否有序
        int aOrder = SingleLinkedUtil.isOrderLinked(a);
        int bOrder = SingleLinkedUtil.isOrderLinked(b);
        if(aOrder == NULL_LINKED){
            return b;
        }

        if(bOrder == NULL_LINKED){
            return a;
        }

        SingleLinked<Integer> result = new SingleLinked<Integer>();

        if(aOrder != UNORDERED && bOrder != UNORDERED){
            if(aOrder != bOrder){
                //說明兩者的排序方式不一樣
                throw new CannotControlException();
            }else {

                if(aOrder == ASC){
                    //說明兩個鏈表都是升序
                    //通過遞歸的方式合并兩個鏈表
                    result.addLast(mergeASC(a.getFirst(), b.getFirst()));

                }else {
                    //說明兩個鏈表都是降序
                    result.addLast(mergeDES(a.getFirst(), b.getFirst()));
                }
            }


        }else {
            throw new UnorderedLinkedException();
        }


        return result;
    }

    /**
     * 合并升序鏈表
     * @param nodeA
     * @param nodeB
     * @return
     */
    private static Node<Integer> mergeASC(Node<Integer> nodeA,Node<Integer> nodeB){
        Node<Integer> result;
        if(nodeA == null){
            return nodeB;
        }

        if(nodeB == null){
            return nodeA;
        }

        if(nodeA.getT() < nodeB.getT()){
            result = nodeA;
            result.setNext(mergeASC(nodeA.getNext(),nodeB));
        }else {
            result = nodeB;
            result.setNext(mergeASC(nodeA,nodeB.getNext()));
        }

        return result;
    }

    /**
     * 合并降序鏈表
     * @param nodeA
     * @param nodeB
     * @return
     */
    private static Node<Integer> mergeDES(Node<Integer> nodeA,Node<Integer> nodeB){
        Node<Integer> result;

        if(nodeA == null){
            return nodeB;
        }

        if(nodeB == null){
            return nodeA;
        }

        if(nodeA.getT() > nodeB.getT()){
            result = nodeA;

            result.setNext(mergeDES(nodeA.getNext(),nodeB));
        }else {
            result = nodeB;
            result.setNext(mergeDES(nodeA,nodeB.getNext()));
        }

        return result;
    }
}

四姻锁、刪除單鏈表倒數(shù)第 n 個結(jié)點(diǎn)

這個好難枕赵,網(wǎng)上找到的答案能看懂,知道會得到正確的結(jié)果位隶,但是如何表述思考過程好難拷窜。

image

   public void removeAtFromEnd2(int n) {
        if (n <= 0) {
            throw new UnsupportedOperationException("n必須大于0");
        }
        int length = 0;
        Node<E> temp = this.first;
        while (temp != null) {
            temp = temp.getNext();
            length++;
        }

        if(n > length){
            throw new LinkedOutOfBoundsException();
        }
        int target = length - n;

        Node<E> again = this.first;
        if(target == 0){
            //要移除第一個
            this.first = again.getNext();
        }
        while (target > 1) {
            again = again.getNext();
            target--;
        }
        again.setNext(again.getNext().getNext());
    }

五、刪除單鏈表倒數(shù)第 n 個結(jié)點(diǎn)(使用快慢指針的方式解決問題)

image


  /**
     * 為什么最后從 0 開始移動的涧黄?
     * 自己可以運(yùn)行測試一下篮昧,如果要刪除的是鏈表的頭結(jié)點(diǎn)
     * 那么從 頭結(jié)點(diǎn)前開始移動,代碼會更優(yōu)化
     *
     * @param n
     */
    public void removeAtFromEnd(int n) {
        if (n <= 0) {
            throw new UnsupportedOperationException("n必須大于0");
        }
        Node<E> beforeFirst = new Node<>();
        beforeFirst.setNext(first);

        //現(xiàn)在快慢指針都在 0 位置呢(如果假設(shè)頭結(jié)點(diǎn)為 1 )
        Node<E> fast = beforeFirst;
        Node<E> slow = beforeFirst;

        //開始移動 fast 直到到達(dá) size - n 的位置
        while (fast != null && n > -1) {
            fast = fast.getNext();
            n--;
        }

        if (n > -1) {
            //說明要刪除的位置超過了鏈表的長度
            throw new LinkedOutOfBoundsException();
        }

        //當(dāng) fast 處于 n 的位置的時候
        //再讓 slow 與 fast 一起移動
        //這樣當(dāng) fast 移動到鏈表尾部 fast==null 為 true 時
        //slow 正好移動到 size - n 的位置
        while (fast != null) {
            fast = fast.getNext();
            slow = slow.getNext();
        }

        slow.setNext(slow.getNext().getNext());

    }

六笋妥、求鏈表的中間結(jié)點(diǎn)

這個和第 二 道題很像懊昨,都是使用快慢指針來解決問題。
快指針從頭結(jié)點(diǎn)每次循環(huán)走兩步春宣。
慢指針從頭結(jié)點(diǎn)每次循環(huán)走一步酵颁。
當(dāng)快指針走到null時,慢指針剛好走到鏈表中間結(jié)點(diǎn)月帝。
若鏈表長度為奇數(shù):慢指針是中間結(jié)點(diǎn)躏惋。
若鏈表長度為偶數(shù):慢指針是上中結(jié)點(diǎn),慢指針.next 是下中結(jié)點(diǎn)嚷辅。
[1,2,3,4,5,6,7,8] :上中結(jié)點(diǎn):4 下中結(jié)點(diǎn):5

    /**
     * 使用快慢指針即可拿到中間結(jié)點(diǎn)
     * 快指針比慢指針多走一步即可
     *
     * @return
     */
    public Node<E> getMiddleNode() {
        if (first == null) {
            throw new EmptyLinkedException();
        }
        Node<E> fast = first;
        Node<E> slow = first;

        while (fast.getNext() != null && fast.getNext().getNext() != null) {

            fast = fast.getNext().getNext();
            slow = slow.getNext();
        }

        return slow;


    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末簿姨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子簸搞,更是在濱河造成了極大的恐慌款熬,老刑警劉巖深寥,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件攘乒,死亡現(xiàn)場離奇詭異贤牛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)则酝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門殉簸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沽讹,你說我怎么就攤上這事般卑。” “怎么了爽雄?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵蝠检,是天一觀的道長。 經(jīng)常有香客問我挚瘟,道長叹谁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任乘盖,我火速辦了婚禮焰檩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘订框。我一直安慰自己析苫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布穿扳。 她就那樣靜靜地躺著衩侥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪矛物。 梳的紋絲不亂的頭發(fā)上茫死,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機(jī)與錄音泽谨,去河邊找鬼璧榄。 笑死,一個胖子當(dāng)著我的面吹牛吧雹,可吹牛的內(nèi)容都是我干的骨杂。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼雄卷,長吁一口氣:“原來是場噩夢啊……” “哼搓蚪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起丁鹉,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤妒潭,失蹤者是張志新(化名)和其女友劉穎悴能,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雳灾,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漠酿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了谎亩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炒嘲。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖匈庭,靈堂內(nèi)的尸體忽然破棺而出夫凸,到底是詐尸還是另有隱情,我是刑警寧澤阱持,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布夭拌,位于F島的核電站,受9級特大地震影響衷咽,放射性物質(zhì)發(fā)生泄漏鸽扁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一兵罢、第九天 我趴在偏房一處隱蔽的房頂上張望献烦。 院中可真熱鬧,春花似錦卖词、人聲如沸巩那。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽即横。三九已至,卻和暖如春裆赵,著一層夾襖步出監(jiān)牢的瞬間东囚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工战授, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留页藻,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓植兰,卻偏偏與公主長得像份帐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子楣导,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349