數據結構與算法-鏈表習題

這次做了鏈表五個習題:

1、單鏈表反轉

2、鏈表中環的檢測

3、兩個有序的鏈表合併

4、刪除鏈表倒數第 n 個結點

5、求鏈表的中間結點

需要用到下列兩個鏈表類:

package com.freshbin.dataStructAndAlgo.chapter06.mycode.Link.LRU;

/**
 * @author freshbin
 * @date 2020/4/11 21:59
 */
public class MyNode {
    public String token;
    public MyNode next;

    public MyNode(String token) {
        this.token = token;
    }

    public String getToken() {
        return this.token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public MyNode getNext() {
        return this.next;
    }

    public void setNext(MyNode next) {
        this.next = next;
    }

    @Override
    public String toString() {
        return "MyNode{" +
                "token='" + token + '\'' +
                ", next=" + next +
                '}';
    }
}
package com.freshbin.dataStructAndAlgo.chapter06.mycode.Link.palindromicString;

import com.freshbin.dataStructAndAlgo.chapter06.mycode.Link.LRU.MyNode;

/**
 * 使用鏈表實現LRU緩存淘汰策略
 * @author freshbin
 * @date 2020/4/11 21:24
 */
public class MyLink {
    public MyNode firstNode;

    public MyLink() {

    }

    /**
     * 每次都從頭結點插入
     * @param value
     * @return
     */
    public MyNode addLink(String value) {
        MyNode myNode = new MyNode(value);
        MyNode oldFirstNode = this.firstNode;
        this.firstNode = myNode;
        this.firstNode.next = oldFirstNode;
        return this.firstNode;
    }

    /**
     * 每次都從尾部寫入
     * @param value
     * @return
     */
    public MyNode addTailLink(String value) {
        MyNode myNode = new MyNode(value);
        if(this.firstNode == null) {
            this.firstNode = myNode;
            return this.firstNode;
        }

        MyNode nextNode = this.firstNode;
        while(nextNode != null) {
            if(nextNode.next == null) {
                break;
            }
            nextNode = nextNode.next;
        }

        nextNode.next = myNode.next;
        nextNode.next = myNode;

        return this.firstNode;
    }

    public MyNode getFirstNode() {
        return this.firstNode;
    }

    public void setFirstNode(MyNode firstNode) {
        this.firstNode = firstNode;
    }
}

完成代碼如下:

package com.freshbin.dataStructAndAlgo.chapter06.mycode.Link.exam;

import com.freshbin.dataStructAndAlgo.chapter06.mycode.Link.LRU.MyNode;
import com.freshbin.dataStructAndAlgo.chapter06.mycode.Link.palindromicString.MyLink;

/**
 * @author freshbin
 * @date 2020/4/12 15:34
 */
public class MyLinkExam {
    /**
     * 單鏈表方式實現翻轉單鏈表
     * 思路:需要添加一個前節點用於輔助
     * 將當前節點的下一節點指向前節點,將前節點和當前節點都往後移動。
     * @param node
     * @return
     */
    public static MyNode reversalLink(MyNode node) {
        if(node == null || node.next == null) {
            return node;
        }

        MyNode preNode = null;
        MyNode currentNode = node;
        MyNode nextNode = null;

        while(currentNode != null) {
            nextNode = currentNode.next;
            currentNode.next = preNode;
            preNode = currentNode;
            currentNode = nextNode;
        }

        return preNode;
    }

    /**
     * 檢測鏈表中是否有環的操作
     * 一開始我連這題目是什麼意思都不知道,後來搜了一下,才恍然大悟,下面列一下大神們的解法,
     * 圖解見此大佬的知乎回答https://zhuanlan.zhihu.com/p/103626709
     * 思路:設置一快一慢指針,快指針是慢指針的兩倍。當快指針與慢指針能相遇的時候,說明有環,該點就是鏈表的環內節點,反之沒有環
     * 當快慢指針相遇之後,將兩個指針分別重新定位到頭結點和相遇節點,兩個指針繼續往前走,這次都一步一步的走
     * 當兩個指針再次相遇的時候,這個點就是環入口節點
     *
     * @param pHead
     * @return
     */
    public static MyNode getLinkRingNode(MyNode pHead) {
        if(pHead == null || pHead.next == null || pHead.next.next == null) {
            return null;
        }

        MyNode ringNode = null;
        MyNode slowNode = pHead.next;
        MyNode fastNode = pHead.next.next;

        while(slowNode != fastNode) {
            if(fastNode == null || fastNode.next == null || fastNode.next.next == null) {
                // 快指針一定走得比慢指針快,所以這裏只要判斷快指針爲空的話,那麼就說明鏈表中沒有環,直接退出
                return null;
            }
            slowNode = slowNode.next;
            fastNode = fastNode.next.next;
        }

        // 執行到這裏, 說明鏈表有環,而且快慢指針相遇了,這時候,分別兩個指針放到頭結點和相遇節點,繼續往前走
        slowNode = pHead;
        while(slowNode != fastNode) {
            slowNode = slowNode.next;
            fastNode = fastNode.next;
        }

        // 退出循環執行到這裏,說明已經在環節點相遇了,直接賦值返回即可
        ringNode = slowNode;
        return ringNode;
    }

    /**
     * 合併兩個有序鏈表
     * 思路:1、當兩個鏈表都不爲空,首先遍歷鏈表1,如果值小於等於鏈表2當前節點值,那麼就將鏈表遍歷寫入新鏈表
     * 2、遍歷鏈表2,如果值小於等於鏈表1的當前節點值,那麼就寫入新鏈表
     * 3、繼續步驟1
     * 4、遍歷鏈表1,將值全部寫入新鏈表,遍歷鏈表2,將值全部寫入新鏈表
     * @param p1Node
     * @param p2Node
     * @return
     */
    public static MyNode mergeOrderLink(MyNode p1Node, MyNode p2Node) {
        MyNode pHeadNode = null;
        MyNode currentNode = null;

        if(p1Node == null) {
            return p2Node;
        }
        if(p2Node == null) {
            return p1Node;
        }
        if(Integer.valueOf(p1Node.token) <= Integer.valueOf(p2Node.token)) {
            pHeadNode = new MyNode(p1Node.token);
            p1Node = p1Node.next;
        } else {
            pHeadNode = new MyNode(p2Node.token);
            p2Node = p2Node.next;
        }
        MyNode returnNode = pHeadNode;

        while(p1Node != null && p2Node != null) {
            while(p1Node != null && p2Node != null && Integer.valueOf(p1Node.token) <= Integer.valueOf(p2Node.token)) {
                currentNode = new MyNode(p1Node.token);
                pHeadNode.next = currentNode;
                pHeadNode = pHeadNode.next;
                p1Node = p1Node.next;
            }
            while(p1Node != null && p2Node != null && Integer.valueOf(p2Node.token) <= Integer.valueOf(p1Node.token)) {
                currentNode = new MyNode(p2Node.token);
                pHeadNode.next = currentNode;
                pHeadNode = pHeadNode.next;
                p2Node = p2Node.next;
            }
        }

        while(p1Node != null) {
            currentNode = new MyNode(p1Node.token);
            pHeadNode.next = currentNode;
            pHeadNode = pHeadNode.next;
            p1Node = p1Node.next;
        }

        while(p2Node != null) {
            currentNode = new MyNode(p2Node.token);
            pHeadNode.next = currentNode;
            pHeadNode = pHeadNode.next;
            p2Node = p2Node.next;
        }

        return returnNode ;
    }

    /**
     * 刪除鏈表倒數第 n 個結點
     * 先遍歷一遍鏈表,記錄鏈表總長度,求倒數第n個,就是求鏈表總數減去倒數第n個之後的值
     * @param pHeadNode
     * @return
     */
    public static MyNode deleteBackwardNode(MyNode pHeadNode, int deleteIndex) {
        if(pHeadNode == null || deleteIndex < 1) {
            return null;
        }

        MyNode countLengthNode = pHeadNode;
        int length = 0;
        int targetIndex = 0;
        while (countLengthNode != null) {
            length++;
            countLengthNode = countLengthNode.next;
        }
        if(deleteIndex > length) {
            return null;
        }

        targetIndex = length - deleteIndex;
        if(targetIndex == 0) {
            // 如果是刪除頭結點,這裏直接把頭結點刪除,這樣下面的代碼邏輯會比較好操作
            pHeadNode = pHeadNode.next;
            return pHeadNode;
        }

        // 當前鏈表從1開始,因爲頭結點在上面已經判斷過了
        int currentIndex = 1;
        MyNode nextNode = pHeadNode.next;
        MyNode preNode = pHeadNode;
        while (nextNode != null) {
            if(targetIndex == currentIndex) {
                System.out.println("需要刪除的節點值:" + nextNode.token);
                preNode.next = nextNode.next;
                return pHeadNode;
            }
            currentIndex++;
            preNode = nextNode;
            nextNode = nextNode.next;
        }

        return pHeadNode;
    }

    /**
     * 求鏈表的中間結點
     * 思路:用快慢指針,快指針每次步長爲2,慢指針步長爲1
     * 快慢指針一直前進,判斷快指針的下一個節點是否爲空,
     * 如果爲空,說明到尾節點了,而且該鏈表是雙數鏈表,中間節點有兩個,就是當前慢指針的節點和下一節點
     * 如果不爲空,快慢指針繼續前進,如果退出循環,那麼表明該鏈表爲單數鏈表,中間節點爲慢指針當前節點
     *
     * @param pHeadNode
     * @return
     */
    public static MyNode getMiddleNode(MyNode pHeadNode) {
        if(pHeadNode == null) {
            return pHeadNode;
        }

        MyNode deleteNode = null;
        MyNode slowNode = pHeadNode;
        MyNode fastNode = pHeadNode.next;
        MyNode middleNode = null;

        while(fastNode != null) {
            if(fastNode.next == null) {
                // 說明是雙數鏈表,中間節點爲慢指針和其下一節點
                middleNode = slowNode;
                if(middleNode.next != null && middleNode.next.next != null) {
                    middleNode.next.next = null;
                }
                return middleNode;
            }

            slowNode = slowNode.next;
            fastNode = fastNode.next.next;
        }

        // 退出循環,說明是單數鏈表,中間節點爲當前慢節點
        middleNode = slowNode;
        middleNode.next = null;

        return middleNode;
    }

    public static void main(String[] arg) {
        // 鏈表反轉
        String[] reversalLinkTestData = new String[]{"a", "b", "c", "d", "e"};
//        reversalLinkTest(reversalLinkTestData);

        String[] ringLinkData = new String[]{"a", "b", "c", "d", "b"};
        // 獲取鏈表中的環入口節點
//        getLinkRingNodeTest(ringLinkData);

        // 合併兩個有序鏈表
        String[] orderlyLink01 = new String[]{"1", "3", "5", "7", "9"};
        String[] orderlyLink02 = new String[]{"0", "2", "4", "6", "8"};
//        mergeOrderLinkTest(orderlyLink01, orderlyLink02);

        // 刪除鏈表的倒數第n個節點
        String[] deleteLinkData = new String[]{"1", "2", "3", "4", "5"};
//        deleteBackwardNodeTest(deleteLinkData, 4);

        // 求鏈表的中間結點
//        String[] getMiddleNodeData = new String[]{"1"};
//        String[] getMiddleNodeData = new String[]{"1", "2"};
//        String[] getMiddleNodeData = new String[]{"1", "2", "3"};
        String[] getMiddleNodeData = new String[]{"1", "2", "3", "4"};
        getMiddleNodeTest(getMiddleNodeData);
    }

    private static void getMiddleNodeTest(String[] getMiddleNodeData) {
        MyLink myLink = initLinkFromTail(getMiddleNodeData);

        MyNode middleNode = getMiddleNode(myLink.firstNode);
        System.out.println("中間節點值爲:");
        while (middleNode != null) {
            System.out.print(middleNode.token + "->");
            middleNode = middleNode.next;
        }

    }

    private static void deleteBackwardNodeTest(String[] deleteLinkData, int deleteBackwardIndex) {
        MyLink myLink = initLinkFromTail(deleteLinkData);

        MyNode pHeadNode = deleteBackwardNode(myLink.firstNode, deleteBackwardIndex);
        displayLink(pHeadNode);
    }

    private static void mergeOrderLinkTest(String[] orderlyLink01, String[] orderlyLink02) {
        MyLink p1Link = initLinkFromTail(orderlyLink01);
        MyLink p2Link = initLinkFromTail(orderlyLink02);

        MyNode pHeadNode = mergeOrderLink(p1Link.firstNode, p2Link.firstNode);
        displayLink(pHeadNode);
    }

    private static void displayLink(MyNode pHeadNode) {
        System.out.println("鏈表數據:");
        while (pHeadNode != null) {
            System.out.print(pHeadNode.token + "->");
            pHeadNode = pHeadNode.next;
        }
        System.out.println();
    }

    private static void getLinkRingNodeTest(String[] ringLinkData) {
        MyNode pHeadNode = new MyNode("a");
        MyNode bNode = new MyNode("b");
        MyNode cNode = new MyNode("c");
        MyNode dNode = new MyNode("d");

        pHeadNode.next = bNode;
        bNode.next = cNode;
        cNode.next = dNode;
        dNode.next = bNode;

        MyNode ringNode = getLinkRingNode(pHeadNode);
        System.out.println("環入口節點的值爲:" + ringNode.token);
    }

    private static void reversalLinkTest(String[] reversalLinkTestData) {
        MyLink myLink = initLinkFromHead(reversalLinkTestData);

        System.out.print("原鏈表數據:");
        MyNode oldLink = myLink.firstNode;
        while(oldLink != null) {
            System.out.print(oldLink.token + "->");
            oldLink = oldLink.next;
        }

        System.out.println();
        MyNode myNode = reversalLink(myLink.firstNode);
        System.out.print("新鏈表數據:");
        while(myNode != null) {
            System.out.print(myNode.token + "->");
            myNode = myNode.next;
        }
    }

    public static MyLink initLinkFromHead(String[] data) {
        MyLink myLink = new MyLink();
        for(int i = 0; i < data.length; i++) {
            myLink.addLink(data[i]);
        }
        return myLink;
    }

    public static MyLink initLinkFromTail(String[] data) {
        MyLink myLink = new MyLink();
        for(int i = 0; i < data.length; i++) {
            myLink.addTailLink(data[i]);
        }
        return myLink;
    }
}

github地址:https://github.com/freshbin/dataStructAndAlgo

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