1. 程式人生 > 實用技巧 >資料結構-基於單向連結串列的迴文字串判斷

資料結構-基於單向連結串列的迴文字串判斷

原文連結http://xueliang.org/article/detail/20200727003018759

前言

學習了極客時間王錚老師的《資料結構與演算法之美》中《06 | 連結串列(上):如何實現LRU快取淘汰演算法?》,課後思考留了一道演算法題,
給定一個字串,判斷是否是迴文字串,而且呢,這個字串不是普通的字串,字串中各個字元是以單向連結串列的資料結構首尾相連,看起來像下面這樣:1 -> 2 -> 3 -> 0 -> 3 -> 2 -> 1,我們來一起看下這個演算法可以如何實現。

演算法

演算法簡述

這裡介紹一種快慢指標的方法(所謂快慢指標,說白了就是兩個變數而已)。
開始時,快慢指標都指向頭結點

,然後從頭結點開始,,快指標每次走兩步,慢指標每次走一步。
對於一個長度為奇數的連結串列,當快指標走到尾結點時,慢指標剛好走到這個連結串列最中間的那個節點。從頭結點開始,慢指標走過的節點 next 引用都反轉方向,指向之前指向自己的那個節點。然後從中間向兩側開始逐節點對比,一直對比到頭尾節點,如果每次對比,內容都相同,則說明是一個迴文字串。

演算法視覺化

上面大致理清了思路,為了將思路更清晰,在擼程式碼之前讓更多小夥伴理解,我用圖片來說明一下。
我們還是基於長度為奇數的單向連結串列,因為長度為偶數的單向連結串列相對容易一些。

  • 首先,我們定義3個元素,用 fast 表示快指標,slow 表示慢指標,以及一個 pre

    表示 slow 的上一個元素 fast尾結點方向走兩步,然後將 slownext 指標指向上一個元素 pre,由於 slow 的上一個元素即 pre 初始狀態是空,所以從圖上看的效果,就是直接將 slownext 丟棄了。隨後將 slow 賦給 preslow 向前走一步。大家注意這裡,在將 slow 的指標指向pre前,我們需要一個區域性變數 slowNext,來儲存此時的 slow 最初的下一個元素,否則我們後續無法再獲取到 slow 最初的下一個元素。

  • 上面我們走了一個完整的快慢指標從頭結點尾結點方向移動的邏輯,下面我放一個快指標從頭結點一直走到尾結點完整過程。

  • 對於長度為奇數的單向連結串列,當 fast 走到尾結點時,slow 指向連結串列正中央的節點,判斷 fast 的 下一個元素 next 為空時,表示 fast 走到尾結點,將 slow 繼續向前走一步

  • 此時 slow 與 pre 的位置呈軸對稱,且 pre 所處連結串列方向(向左) 與 slow 所處連結串列方向(向右)相反,若 pre 和 slow 分別向各自尾結點逐步前進,每前進一步,對比一次兩個節點內容是否相同即可知道是否是迴文字串。

  • pre 或者 slownext 指向的元素是否為空,來判斷是否迴文判斷是否結束,並最終得到該字串是否是迴文字串。

程式碼實現

這裡,我沒加註釋,上面已經圖文講解的很詳細了。

package org.xueliang.algorithm.linkedlist;

/**
 * @author xueliang
 * @since 2020-07-25 00:01
 */
public class Palindrome {

    public static void main(String[] args) {
        Node a = new Node("1", null);
        Node b = new Node("2", a);
        Node c = new Node("3", b);
        Node d = new Node("0", c);
        Node c1 = new Node("1", d);
        Node b1 = new Node("2", c1);
        Node a1 = new Node("3", b1);

        Node fast = a1;
        Node slow = a1;
        Node pre = null;

        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            Node slowNext = slow.next;
            slow.next = pre;
            pre = slow;
            slow = slowNext;
        }
        if (fast != null) {
            slow = slow.next;
        }
        boolean isPalindrome = true;
        while (pre != null) {
            if (!pre.text.equals(slow.text)) {
                isPalindrome = false;
                break;
            }
            pre = pre.next;
            slow = slow.next;
        }

        System.out.println("是否是迴文字串:" + isPalindrome);
    }

    static class Node {

        Node(String text, Node next) {
            this.text = text;
            this.next = next;
        }

        private String text;

        private Node next;

        @Override
        public String toString() {
            return "Node{id:" + Integer.toHexString(hashCode()) + ",text:" + text + "}";
        }
    }
}

相關閱讀

負載均衡演算法之一致性 Hash 演算法實現
負載均衡演算法之加權輪詢演算法實現

原文連結http://xueliang.org/article/detail/20200727003018759