旋轉鏈表?面試官你確定要讓手寫這個嗎?

前言:

今天練習了一道關於單鏈表的算法題 《旋轉鏈表》,由於之前寫過一篇 《單鏈表反轉?面試官你確定要問這個嗎?》 的文章,然後今天又碰到了這道有關單鏈表的算法,就想着再 “水篇文章” 吧(帶引號的哈),可以證明我沒偷懶,按時寫作業了。嘿嘿 . . . . . . . . .

接下來,①、首先回憶下單鏈表的數據結構 ;②、詳解描述下什麼是旋轉鏈表(題目描述); ③、圖解旋轉鏈表代碼

數據結構:

1. 單鏈表的數據結構:

單鏈表是一種線性結構,它是由一個個 節點(Node) 組成的。並且每個節點(Node)是由一塊數據域(data)和一塊指針域(next)組成的。     

①、節點(Node)結構圖如下:

  1. 節點的數據域:data數據域一般就是用來存放數據的 。(注:data域需要指定類型,只能存放指定類型的數據,不能什麼東西都放,是不是呀; 那代碼中是怎麼實現的呢? 使用 泛型 。)
  2. 節點的指針域:next指針域一般就是存放的指向下一個節點的指針;這個指針其實是一個內存地址,因爲Node節點對象是存放在JVM中的堆內存中,所以節點的next指針域中存放就是下一個節點在堆內存中的地址;而在代碼中對象的內存地址是賦值給其引用變量了,所以指針域中存放的是下一個節點對象的引用變量

②、單鏈表結構圖如下:(下圖是由三個節點構成的單鏈表

若有所思,en en en . . . . . . 好像單鏈表的知識在腦海中清晰了些呀;那要不我們快馬加鞭,趕緊把單鏈表的數據結構代碼弄出來,然後再思索下怎麼實現旋轉操作, en en en. . . … . . 嘿嘿!

2. 節點類Node代碼:

創建Node節點類,節點類中並且額外提供了兩個方法(單鏈表的創建方法、單鏈表的遍歷方法);

注意:單鏈表的創建方法 createLinkedList( ):Node節點的插入方式爲 尾插法 , 其實還有 頭插法 方式;

擴展:鏈表中節點的插入方式還在 HashMap 中使用到了,在 JDK 1.7 時是頭插法,JDK 1.8時是尾插法

/**
 * @PACKAGE_NAME: com.lyl.linklist
 * @ClassName: Node
 * @Description:  單鏈表的 節點類
 * @Date: 2020-06-07 15:51
 **/
public class Node<T> {

    // 節點的數據域
    public T data;
    // 節點的指針域
    public Node next;

    /**
     * 構造方法
     * @param data 數據域值
     */
    public Node(T data) {
        this.data = data;
    }


    /**
     * 創建 單鏈表 (尾插法)
     * @return  返回頭結點
     */
    public static Node createLinkedList(){
        // 頭節點
        Node<String> head;

        Node<String> n = new Node<String>("111");
        Node<String> n1 = new Node<String>("222");
        Node<String> n2 = new Node<String>("333");
        Node<String> n3 = new Node<String>("444");
        Node<String> n4 = new Node<String>("555");
        Node<String> n5 = new Node<String>("666");
        // 指定頭節點
        head = n;
        n.next = n1;
        n1.next = n2;
        n2.next = n3;
        n3.next = n4;
        n4.next = n5;
        // 返回頭結點
        return head;
    }


    /**
     * 遍歷單鏈表並在控制檯打印輸出
     * @param head  單鏈表的 頭結點
     */
    public static void traverse(Node head) {
        while (head != null) {
            System.out.print(head.data + " --> ");
            head = head.next;
        }
        System.out.print("null");
        System.out.println();
    }

}

題目描述:

給定一個鏈表,旋轉鏈表,將鏈表每個節點向右移動 k 個位置,其中 k 是非負數。

自我理解:其實是將從尾部數的 k 個節點截取出來再拼接到 head 頭節點上。

注意:旋轉鏈表操作會存在兩種情況的,正如下面的 實例1 和 實例2

還需知道的一點就是當移動的位置數是鏈表長度的 整數倍 時,最終相當於沒有移動一樣,還是原來的樣子。

實例1:(移動位置 k 小於 單鏈表的長度)

輸入: 1->2->3->4->5->NULL , k = 2 (每個節點向右移動2個位置)
輸出: 4->5->1->2->3->NULL
解釋:
向右旋轉 1 步: 5->1->2->3->4->NULL
向右旋轉 2 步: 4->5->1->2->3->NULL

如圖:(直接將 4 、5節點截取下來拼接到頭結點上)

實例2:(移動位置 k 大於 單鏈表的長度)

輸入: 0->1->2->NULL , k = 4 (每個節點向右移動4個位置)
輸出: 2->0->1->NULL
解釋:
向右旋轉 1 步: 2->0->1->NULL
向右旋轉 2 步: 1->2->0->NULL
向右旋轉 3 步: 0->1->2->NULL
向右旋轉 4 步: 2->0->1->NULL

如圖:

圖解代碼:

旋轉鏈表使用的方法是雙指針法,會存在兩個指針:current 指針、previous 指針 。通過兩個指針移動,並且保證兩個指針之間的間距爲需要移動的位置數(當移動的位置數大於鏈表長度時,此時爲兩者取餘的餘數)

1. 先看圖:

2. 代碼:

注意:代碼中使用的節點類Node在本文的上面已經提供了。

/**
 * @PACKAGE_NAME: com.lyl.linklist
 * @ClassName: RotateLinkedListByDoublePointer
 * @Description:  通過雙指針法 旋轉鏈表
 * @Date: 2020-06-07 16:00
 **/
public class RotateLinkedListByDoublePointer {

    /**
     *  旋轉單鏈表
     * @param head   單鏈表 頭結點
     * @param placeNum  向右移動的位置數
     */
    public static Node rotate(Node head, int placeNum){
        if (head == null)
            return null;
        // 臨時節點
        Node temp;
        // 將臨時節點指向頭結點
        temp = head;
        // 記錄單鏈表的長度
        int length = 1;
        // 遍歷單鏈表得到其長度
        while (temp.next != null){
            length++;
            temp = temp.next;
        }
        /**
         *  如果單鏈表的長度小於移動的位置數
         * (注意:移動位置數是單鏈表長度的整數倍時,其實相當於單鏈表沒有移動,還是原來的樣式)
         */
        if (length <= placeNum){
            // 獲取餘數,也就是最終要向右移動的位置數
            placeNum = placeNum % length;
        }
        // 如果餘數爲0,和上面所說的單鏈表其實沒有變化的
        if (placeNum == 0){
            return head;
        }

        // 當前節點指針
        Node current;
        // 前節點指針
        Node previous;
        current = head;
        previous = head;

        // 記錄當前指針是否移動了(要求移動的位置數)
        int i = 0;
        while (current.next != null){
            /**
             * 在當前指針移動了(要求的位置數)後,並且當前指針還未移動到單鏈表的尾節點的話,
             * 此時需要current、previous指針一起移動了
             */
            if (i == placeNum){
                current = current.next;
                previous = previous.next;
            }else {
                // 在當前指針還未移動(要求的位置數)前,只有當前指針移動,previous指針不動
                i++;
                current = current.next;
            }
        }

        /**
         * 當 current指針移動到了鏈表的尾部後,此時指針移動結束,將當前previous、current指針截取的
         * 這段節點拼接到頭節點前,並將previous指針指向的節點與後繼結點斷開連接
         */
        Node newTemp = previous.next;
        previous.next = null;
        current.next = head;

        return newTemp;
    }

    // Test
    public static void main(String[] args) {
        // 創建單鏈表
        Node head = Node.createLinkedList();
        System.out.print("新創建的單鏈表: ");
        // 遍歷新創建的單鏈表
        Node.traverse(head);
        // 旋轉鏈表,向右移動 10 個位置數
        Node newHead = rotate(head, 10);
        System.out.print("反轉後的單鏈表: ");
        // 遍歷反轉後的單鏈表
        Node.traverse(newHead);
    }

}

不要忘記留下你學習的足跡 [點贊 + 收藏 + 評論]嘿嘿ヾ

一切看文章不點贊都是“耍流氓”,嘿嘿ヾ(◍°∇°◍)ノ゙!開個玩笑,動一動你的小手,點贊就完事了,你每個人出一份力量(點贊 + 評論)就會讓更多的學習者加入進來!非常感謝! ̄ω ̄=

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章