累成一条狗

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

 

posted on 2019-08-20 20:11  累成一条狗  阅读(857)  评论(0编辑  收藏  举报

导航