数据结构与算法-链表习题

这次做了链表五个习题:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值