数据结构与算法之美-01-五道经典链表算法练习题(带注释和测试用例)

此五道题依次对应Leetcode题目的第206、141、21、19、876道题,节点结构只有构造函数不同

package com.linkedlist;

import org.junit.Test;

import java.util.HashSet;
import java.util.Set;

/**
 * 1) 单链表反转
 * 2) 链表中环的检测
 * 3) 两个有序的链表合并
 * 4) 删除链表倒数第n个结点
 * 5) 求链表的中间结点
 */
public class LinkedListAlgo {
    /**
     * 节点结构
     */
    public static class Node{
        private int data;
        private Node next;

        //构造函数
        public Node(int data, Node next){
            this.data = data;
            this.next = next;
        }

        //获取data
        public int getData(){
            return data;
        }
    }

    /**
     * 创建节点
     */
    public static Node createNode(int data){
        return new Node(data, null);
    }

    /**
     * 打印所有节点
     */
    public static void printAll(Node list){
        Node p = list;
        while (p != null){
            System.out.print(p.getData() + " ");
            p = p.next;
        }
        System.out.println();
    }

    /**
     * 1.单链表的反转
     */
    public static Node reverse(Node list){
        Node curNode = list;   //curNode指向当前节点
        Node nextNode = null;
        Node preNode = null;
        while (curNode != null){
            nextNode = curNode.next; //nextNode 指向当前节点的下一个节点
            curNode.next = preNode;  //当前节点的next指向上一个节点
            preNode = curNode;  //pre指针后移
            curNode = nextNode; //当前指针后移
        }
        return preNode;
    }

    /**
     * 反转链表的测试
     */
    @Test
    public void testReverseList() {
        Node node5 = new Node(5, null);
        Node node4 = new Node(4, node5);
        Node node3 = new Node(3, node4);
        Node node2 = new Node(2, node3);
        Node node1 = new Node(1, node2);
        System.out.println("反转之前的链表:");
        LinkedListAlgo.printAll(node1);

        System.out.println("反转之后的链表:");
        Node reversedList = LinkedListAlgo.reverse(node1);
        LinkedListAlgo.printAll(reversedList);
    }

    /**
     * 2.链表中环的检测
     */
    public static boolean checkCircle(Node list){
        if (list == null){   //链表为空则返回false
            return false;
        }
        Node slow = list;    //慢指针指向当前节点
        Node fast = list.next;  //快指针指向当前节点的下一个节点
        while (slow != fast){   //慢指针与快指针不相等,则继续循环
            if (slow == null || fast == null){      //如果快指针或者慢指针到达了null则表示遍历完毕,没有环
                return false;
            }
            slow = slow.next;       //慢指针每次移动1步
            fast = fast.next.next;  //快指针每次移动2步

        }
        return true;       //跳出循环则表示快指针与慢指针相等了,则表示有环
    }

    /**
     * 测试链表中环的检测
     */
    @Test
    public void testcCheckCircle() {
        Node node5 = new Node(5, null);
        Node node4 = new Node(4, node5);
        Node node3 = new Node(3, node4);
        Node node2 = new Node(2, node3);
        Node node1 = new Node(1, node2);

        System.out.println("无环链表:");
        LinkedListAlgo.printAll(node1);
        Boolean flag = LinkedListAlgo.checkCircle(node1);
        System.out.println(flag);

        node5.next = node3;

        System.out.println("有环链表:");
        flag = LinkedListAlgo.checkCircle(node1);
        System.out.println(flag);
    }

    /**
     * 3.两个有序的链表合并
     */
    public static Node mergeTwoLists(Node list1, Node list2){
        Node soldier = new Node(0,null);  //设置哨兵链表
        Node p = soldier;  //设置工作指针指向哨兵

        while(list1 != null && list2 != null){   //只要有一个链表遍历完 就结束循环
            if(list1.data < list2.data){  //若链表1的值较小
                p.next = list1;            //将链表1的节点放入p链表
                list1 = list1.next;     //链表1后移一个节点
            }else {
                p.next = list2;
                list2 = list2.next;
            }
            p = p.next;   //移动工作指针
        }
        //结束循环后,链表1还有多余的节点,则此时p的指针域指向链表1
        if (list1 != null){
            p.next = list1;
        }
        //结束循环后,链表2还有多余的节点,则此时p的指针域指向链表2
        if (list2 != null){
            p.next = list2;
        }
        //返回哨兵节点的下一个节点即为结果(因为第一个节点为0)
        return soldier.next;

    }

    /**
     * 测试两个有序的链表合并
     */
    @Test
    public void testMergeTwoLists(){
        Node node9 = new Node(9, null);
        Node node7 = new Node(7, node9);
        Node node5 = new Node(5, node7);
        Node node3 = new Node(3, node5);
        Node node1 = new Node(1, node3);

        Node node10 = new Node(10, null);
        Node node8 = new Node(8, node10);
        Node node6 = new Node(6, node8);
        Node node4 = new Node(4, node6);
        Node node2 = new Node(2, node4);

        System.out.print("合并前的链表1:");
        LinkedListAlgo.printAll(node1);
        System.out.print("合并前的链表2:");
        LinkedListAlgo.printAll(node2);
        Node mergedTwoLists = LinkedListAlgo.mergeTwoLists(node1, node2);
        System.out.print("合并后的新链表:");
        LinkedListAlgo.printAll(mergedTwoLists);
    }

    /**
     * 4.删除链表倒数第n个结点
     */
    public static Node removeLastKth(Node head, int n){
        Node soldier = new Node(0,head);   //设置哨兵协助
        Node first = soldier, second = soldier;   //设置第一个节点

        for (int i = 0 ; i <= n; i ++){   //使first指针-second指针 = n+1
            first = first.next;
        }

        while (first != null){          //一旦first移动到了尾节点的null指针域,就结束
            first = first.next;         //移动first
            second = second.next;       //移动second
        }   //这样是为了使second移动到要删除节点的上一个节点

        //删除节点操作
        second.next = second.next.next;

        //返回去除虚拟头节点的链表即可
        return soldier.next;
    }

    /**
     * 测试删除链表倒数第n个结点
     */
    @Test
    public void testRemoveLastKth(){
        Node node9 = new Node(9, null);
        Node node7 = new Node(7, node9);
        Node node5 = new Node(5, node7);
        Node node3 = new Node(3, node5);
        Node node1 = new Node(1, node3);

        System.out.print("删除前的链表:");
        LinkedListAlgo.printAll(node1);

        Node newNode1 = LinkedListAlgo.removeLastKth(node1,3);
        System.out.print("删除后的链表:");
        LinkedListAlgo.printAll(newNode1);

    }

    /**
     * 5.求链表的中间结点
     */
    public static Node findMiddleNode(Node head){
        Node fast = head;   //初始化快慢指针
        Node slow = head;

        //当链表长度为偶数时 截止条件为fast = null
        //当链表长度为奇数时 截止条件为fast.next = null
        while (fast != null && fast.next != null){
            slow = slow.next;       //慢指针每次移动一步
            fast = fast.next.next;  //快指针每次移动两步
        }
        return slow;    //此时慢指针即为所求
    }

    /**
     * 测试求链表的中间结点
     */
    @Test
    public void testFindMiddleNode(){
        Node node9 = new Node(9, null);
        Node node7 = new Node(7, node9);
        Node node5 = new Node(5, node7);
        Node node3 = new Node(3, node5);
        Node node1 = new Node(1, node3);

        System.out.println("原链表:");
        LinkedListAlgo.printAll(node1);
        Node middle = LinkedListAlgo.findMiddleNode(node1);
        System.out.println("中间节点:");
        System.out.println(middle.getData());
    }
}

发布了39 篇原创文章 · 获赞 15 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/sinat_35667067/article/details/104761623