zoukankan      html  css  js  c++  java
  • Java实现单链表

     一、链表

    1、什么是链表?

      链表是离散存储线性结构。
      n个节点离散分配,彼此通过指针相连,每个节点只有一个前驱节点,每个节点只有一个后续节点,首节点没有前驱节点,尾节点没有后续节点。
      每个链表都有一个头指针指向头节点(不是首节点),有个尾指针指向尾节点。只要知道一个链表的头节点,即可遍历该链表的所有节点。

    如下图:添加一个节点

    2、链表的优缺点

    (1)优点:增删数据快,没有空间限制(某种程度上)。
    (2)缺点:读取速度慢。

    3、链表分类

    (1)单向链表
      一个节点指向下一个节点
    (2)双向链表
      一个节点有两个指针域
    (3)循环链表
      能通过任何一个节点找到其他所有的节点,将两种(双向/单向)链表的最后一个结点指向第一个结点从而实现循环
    注:
      节点中指针域指向的就是一个节点!

    4、Java实现单链表

    (1)实现功能:

    1、向链表中添加数据(末尾添加),遍历链表
    2、查看当前链表中节点的个数
    3、向链表中指定位置插入一个数据
    4、向链表中指定位置删除一个数据
    5、找到链表中倒数第K个节点
    6、查询链表的中间节点(快慢节点)
    7、反转链表

    (2)代码实现

    package linkedList;
    
    /**
     * 定义一个节点类,其包括数据域,指针域(即节点域)
     *
     */
    class Node {
        private String data; // 数据域,用于保存节点的数据
        private Node next; // 指针域,用于保存指针指向的下一个节点
    
        public Node() {
        }
    
        public Node(String data) {
            this.data = data;
        }
    
        public Node(String data, Node next) {
            this.data = data;
            this.next = next;
        }
    
        public String getData() {
            return data;
        }
    
        public void setData(String data) {
            this.data = data;
        }
    
        public Node getNext() {
            return next;
        }
    
        public void setNext(Node next) {
            this.next = next;
        }
    
        @Override
        public String toString() {
            return "Node [data=" + data + ", next=" + next + "]";
        }
    }
    
    /**
     * 定义一个节点操作类,用于操作节点
     */
    class NodeUtil {
        private static int size = 1; // 用于保存链表的节点总长度
    
        /**
         * 向单链表中的末尾增加节点
         * 
         * @param head
         *            头节点
         * @param data
         *            节点数据
         */
        public static void addNode(Node head, String data) {
            Node newNode = new Node(data); // 定义一个新节点
            Node tempNode = head; // 定义一个临时节点,用于存储头节点
            // 遍历链表,直至链表末尾
            while (tempNode.getNext() != null) {
                tempNode = tempNode.getNext();
            }
            // 向链表末尾插入一个节点
            tempNode.setNext(newNode);
            size++;
        }
    
        /**
         * 在单链表中的末尾删除节点
         * 
         * @param head
         *            头节点
         */
        public static void deleteLastNode(Node head) {
            Node tempNode = head; // 定义一个临时节点,用于存储头节点
            // 遍历链表,直至链表末尾倒数第二位
            while (tempNode.getNext().getNext() != null) {
                tempNode = tempNode.getNext();
            }
            // 在链表末尾删除一个节点
            tempNode.setNext(null);
            size--;
        }
    
        /**
         * 遍历输出整个链表
         * 
         * @param head
         *            头节点
         */
        public static void toPrintNode(Node head) {
            Node tempNode = head;
            System.out.print(tempNode.getData());
            while (tempNode.getNext() != null) {
                tempNode = tempNode.getNext();
                System.out.print(" --> " + tempNode.getData());
            }
            System.out.println();
        }
    
        /**
         * 返回单链表的节点总长度
         */
        public static int size() {
            return size;
        }
    
        /**
         * 在指定位置插入节点
         * 
         * @param head
         *            头节点
         * @param index
         *            插入的位置
         * @param data
         *            插入的数据
         */
        public static void insert(Node head, int index, String data) {
            // 判断插入节点的位置是否越界
            if (index < 1 || index > size) {
                throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
            }
    
            // 若在末尾添加一个节点
            if (index == size) {
                addNode(head, data);
                return;
            }
    
            // 不在末尾添加
            Node tempNode = head; // 定义一个临时节点,用于存储头节点
            Node newNode = new Node(data); // 定义一个新节点
            int currentPoint = 0; // 保存当前节点指向的位置
            // 循环遍历链表
            while (tempNode.getNext() != null) {
                // 找到要插入的位置
                if (currentPoint == (index - 1)) {
                    // 向指定位置插入某节点,比如 A->B中插入 C, 即 A->C->B,此时,先让C指向B,再让A指向C
                    newNode.setNext(tempNode.getNext());
                    tempNode.setNext(newNode);
                    size++;
                    return;
                }
                // 未匹配到位置,当前位置向后移
                currentPoint++;
                tempNode = tempNode.getNext();
            }
        }
    
        /**
         * 删除指定位置的节点
         * 
         * @param head
         *            头节点
         * @param index
         *            删除的位置
         */
        public static void delete(Node head, int index) {
            // 判断删除节点的位置是否越界
            if (index < 1 || index > size) {
                throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
            }
    
            // 若删除的是最后一个
            if (index == size) {
                deleteLastNode(head);
                return;
            }
    
            // 若删除的不是最后一个
            Node tempNode = head; // 定义一个临时节点,用于存储头节点
            int currentPoint = 0; // 保存当前节点指向的位置
            // 循环遍历链表
            while (tempNode.getNext() != null) {
                // 找到要删除的位置
                if (currentPoint == (index - 1)) {
                    // 在指定位置删除某节点,比如 A->C->B 中删除 C,此时,直接让A指向B,即A->B即可。
                    Node deleteNode = tempNode.getNext();
                    tempNode.setNext(deleteNode.getNext());
                    deleteNode.setNext(null);
                    size--;
                    return;
                }
                // 未匹配到位置,当前位置向后移
                currentPoint++;
                tempNode = tempNode.getNext();
            }
        }
    
        /**
         * 找到链表中倒数第K个节点。
         * 
         * 通过两个节点,一个节点A比另一个节点B始终快k个节点,A,B同时向后遍历,当A遍历完成后,B遍历的位置即为倒数第K个节点
         * 
         * @param head
         *            头节点
         * @param k
         *            查询的位置
         * @return 倒数第K个节点
         */
        public static Node fingKNode(Node head, int k) {
            // 判断删除节点的位置是否越界
            if (k < 1 || k > size) {
                throw new IndexOutOfBoundsException("Index: " + k + ", Size: " + size);
            }
            Node nodeA = head;// 保存当前节点
            Node nodeB = head;// 保存提前k个节点的节点
            for (int i = 0; i < k - 1; i++) {
                nodeB = nodeB.getNext(); // 定位到第K个节点的位置
            }
            // 遍历完成后,返回的即为倒数第K个节点
            while (nodeB.getNext() != null) {
                nodeA = nodeA.getNext();
                nodeB = nodeB.getNext();
            }
            return nodeA;
        }
    
        /**
         * 查询链表的中间节点。
         * 
         * 通过两个节点,一个节点A比另一个节点B始终快1个节点,A,B同时向后遍历,当A遍历完成后,B遍历的位置即为中间节点
         * 
         * @param head
         *            头节点
         * @return 中间节点
         */
        public static Node findMiddleNode(Node head) {
            Node nodeA = head; // 保存A节点
            Node nodeB = head; // 保存B节点
            // 循环遍历A节点,A节点每次都比B节点快一个节点(每次多走一个节点),所以当A遍历完成后,B节点所处位置即为中间节点。
            while (nodeA.getNext() != null && nodeA.getNext().getNext() != null) {
                nodeA = nodeA.getNext().getNext();
                nodeB = nodeB.getNext();
            }
            return nodeB;
        }
    
        /**
         * 反转链表
         * 
         * 把每个节点的指针域由原来指向下一个节点变为指向其前一个节点。但由于单链表没有指向前一个节点的指针域,
         * 因此我们需要增加一个指向前一个节点的指针beforeNode,用于存储每一个节点的前一个节点。此外,
         * 还需要定义一个保存当前节点的指针currentNode,
         * 以及下一个节点的afterNode。定义好这三个指针后,遍历单链表,将当前节点的指针域指向前一个节点,之后将定义三个指针往后移动,
         * 直至遍历到最后一个节点停止(最后一个节点作为新的头节点)。
         * 
         * @param head
         *            头节点
         */
        public static Node reserveLinkedList(Node head) {
            // 单链表为空或只有一个节点,直接返回原单链表
            if (head == null || head.getNext() == null) {
                return head;
            }
            // 前一个节点指针
            Node beforeNode = null;
            // 当前指针
            Node currentNode = head;
            // 下一个指针
            Node afterNode = null;
            // 遍历当前指针,遍历结束,则表示最后一个将转为头节点
            while (currentNode.getNext() != null) {
                afterNode = currentNode.getNext(); // 获取下一个节点
                currentNode.setNext(beforeNode); // 将当前节点指向上一个节点
                // 将节点后移一个节点
                beforeNode = currentNode;
                currentNode = afterNode;
            }
            currentNode.setNext(beforeNode);
            return currentNode;
        }
    
    }
    
    /**
     * 测试单链表功能,
     * 
     * 1、向链表中添加数据(末尾添加),遍历链表
     * 
     * 2、查看当前链表中节点的个数
     * 
     * 3、向链表中指定位置插入一个数据
     * 
     * 4、向链表中指定位置删除一个数据
     * 
     * 5、找到链表中倒数第K个节点
     * 
     * 6、查询链表的中间节点(快慢节点)
     * 
     * 7、反转链表
     */
    public class SingleLinkedListDemo {
        public static void main(String[] args) {
            Node head = new Node("start");
            NodeUtil.addNode(head, "node1");
            NodeUtil.addNode(head, "node2");
            NodeUtil.addNode(head, "node3");
            NodeUtil.addNode(head, "end");
            System.out.println("当前链表为:");
            NodeUtil.toPrintNode(head);
    
            System.out.println();
            System.out.println("当前链表长度为:");
            System.out.println(NodeUtil.size());
    
            NodeUtil.insert(head, 1, "node4");
            System.out.println();
            System.out.println("向第一个位置插入一个数据后,当前链表为:");
            NodeUtil.toPrintNode(head);
            System.out.println("当前链表长度为:");
            System.out.println(NodeUtil.size());
    
            NodeUtil.delete(head, 5);
            System.out.println();
            System.out.println("删除最后一个位置的数据,当前链表为:");
            NodeUtil.toPrintNode(head);
            System.out.println("当前链表长度为:");
            System.out.println(NodeUtil.size());
    
            System.out.println();
            System.out.println("找到链表中倒数第2个节点,节点为:");
            System.out.println(NodeUtil.fingKNode(head, 2).getData());
            NodeUtil.toPrintNode(head);
            System.out.println("当前链表长度为:");
            System.out.println(NodeUtil.size());
    
            System.out.println();
            System.out.println("找到链表中间节点,节点为:");
            System.out.println(NodeUtil.findMiddleNode(head).getData());
            NodeUtil.toPrintNode(head);
            System.out.println("当前链表长度为:");
            System.out.println(NodeUtil.size());
    
            System.out.println();
            System.out.println("反转链表,当前链表为:");
            NodeUtil.toPrintNode(NodeUtil.reserveLinkedList(head));
            System.out.println("当前链表长度为:");
            System.out.println(NodeUtil.size());
        }
    }

    (3)输出结果

    当前链表为:
    start --> node1 --> node2 --> node3 --> end
    
    当前链表长度为:
    5
    
    向第一个位置插入一个数据后,当前链表为:
    start --> node4 --> node1 --> node2 --> node3 --> end
    当前链表长度为:
    6
    
    删除最后一个位置的数据,当前链表为:
    start --> node4 --> node1 --> node2 --> node3
    当前链表长度为:
    5
    
    找到链表中倒数第2个节点,节点为:
    node2
    start --> node4 --> node1 --> node2 --> node3
    当前链表长度为:
    5
    
    找到链表中间节点,节点为:
    node1
    start --> node4 --> node1 --> node2 --> node3
    当前链表长度为:
    5
    
    反转链表,当前链表为:
    node3 --> node2 --> node1 --> node4 --> start
    当前链表长度为:
    5
  • 相关阅读:
    样条之CatmullRom
    分形之树(Tree)
    B样条
    样条之贝塞尔(Bezier)
    插值与样条
    windows 下的Python虚拟环境(vitrualen)pycharm创建Django项目
    VS2010专业版和旗舰版(中文版)下载
    PHP课程环境安装总结文档
    原码、反码、补码知识详细讲解(此作者是我找到的讲的最细最明白的一个)
    C语言中size_t类型详细说明【转载】
  • 原文地址:https://www.cnblogs.com/l-y-h/p/11385295.html
Copyright © 2011-2022 走看看