数据结构(五)——线性结构之链表Linked List

一.链表

典型的链表结构,链表中每个结点都应该包括如下内容:

  • 数据部分,保存的是该结点的实际数据;
  • 地址部分,保存的是下一个结点的地址。

由于采用了引用来指示下一个数据的地址。因此在链表结构中,逻辑上相邻的结点在内存中不一定相邻,逻辑相邻关系通过地址部分的引用变量来实现。

(1)链表优点:

         链表最大的好处是结点之间不要求连续存放,因此在保存大量数据的同时,不需要分配一块连续的存储空间。

         用户可以用new函数动态分配结点的存储空间,当删除某个结点时,给该结点赋值null,释放其占用的内存空间。

(2)链表缺点:

         浪费存储空间。对于每个结点数据,都要额外保存一个引用变量。

(3)链表访问方式:

         从表头逐个查找,即通过head头引用找到第一个结点,再从第一个结点找到第二个结点....这样逐个比较一直找到需要的结点为止。而不能像顺序表一样随机访问。

二.单链表

1.单链表定义

单向链表(又名单链表、线性链表)是链表的一种,其特点是链表的链接方向是单向的(每个结点中只包含一个引用),对链表的访问要通过从头部开始,依序往下读取。

2.单链表的操作

单链表的主要操作有以下几个:

(1)在链表结尾追加结点;

(2)获取下一个结点;

(3)获取最后一个结点;

(4)判断当前结点是否为最后结点;

(5)删除下一个结点;

(6)展示链表的所有结点;

(7)插入一个结点作为当前结点的下一个结点。

package cn.kimtian.linkedlist;

/**
 * 这是链表的一个结点
 *
 * @author kimtian
 */
public class Node {

    /**
     * 结点内容(存任何类型都可以)
     */
    private int data;

    /**
     * 下一个结点
     */
    Node next;

    /**
     * 创建结点的时候,给一个value
     *
     * @param data
     */
    public Node(int data) {
        this.data = data;
    }

    /**
     * 为结点追加结点(在链表的结尾)
     *
     * @param node
     */
    public Node appendNode(Node node) {
        //开始是当前结点
        Node currentNode = this;
        //循环向后找
        while (true) {
            //取出下一个节点,赋值给当前结点
            Node nextNode = currentNode.next;
            //结束标记,如果下一个结点为null,当前结点已经是最后一个结点了
            if (nextNode == null) {
                break;
            }
            currentNode = nextNode;
        }
        //把需要追加的结点追加到找到的当前结点的下一个结点
        currentNode.next = node;
        //将自身返回出去
        return this;
    }

    /**
     * 获取结点的下一个结点
     */
    public Node nodeNext() {
        return this.next;
    }

    public int getData() {
        return this.data;
    }

    /**
     * 获取结点的最终结点
     */
    public Node nodeFinal() {
        //开始是当前结点
        Node currentNode = this;
        //循环向后找
        while (true) {
            //取出下一个节点,赋值给当前结点
            Node nextNode = currentNode.next;
            //结束标记,如果下一个结点为null,当前结点已经是最后一个结点了
            if (nextNode == null) {
                break;
            }
            currentNode = nextNode;
        }
        //将自身返回出去
        return currentNode;
    }

    /**
     * 判断当前结点是否为最后结点
     */
    public boolean isLast() {
        return this.next == null;
    }

    /**
     * 删除下一个结点
     */
    public void removeNext() {
        //取出下下一个结点
        Node newNext = next.next;
        //把下下一个结点设置为当前结点的下一个结点
        this.next = newNext;
    }

    /**
     * 插入一个结点作为当前结点的下一个结点(在链表中间)
     *
     * @param node 插入的结点
     */
    public void addNode(Node node) {
        //取出下一个结点作为下下一个结点
        Node nextNode = next;
        //把新结点插入为当前结点的下一个结点
        this.next = node;
        //把下下一个结点设置为新结点的下一个结点
        node.next = nextNode;
    }

    /**
     * 显示所有结点信息
     */
    public void showNodes() {
        Node currentNode = this;
        while (true) {
            System.out.print(currentNode.data + " ");
            //取出下一个结点
            currentNode = currentNode.next;
            //如果是最后一个结点
            if (currentNode == null) {
                break;
            }
        }
        System.out.println();
    }
}

三.循环链表

1.单向循环链表定义:

   操作和单链表基本一致。(在单链表中,将终端结点的引用域null改为指向表头结点或开始结点即可构成单循环链表。)

   只是它的最后一个结点指向头结点,形成一个环。因此,从循环链表中的任何一个结点出发都能找到任何其他结点。

2.单向循环链表的操作:

(1)插入结点;

(2)删除结点。

package cn.kimtian.linkedlist;

/**
 * 循环链表
 *
 * @author kimtian
 */
public class LoopNode {

    /**
     * 结点内容(存任何类型都可以)
     */
    private int data;

    /**
     * 下一个结点
     */
    LoopNode next = this;

    /**
     * 创建结点的时候,给一个value
     *
     * @param data
     */
    public LoopNode(int data) {
        this.data = data;
    }


    /**
     * 获取结点的下一个结点
     */
    public LoopNode nodeNext() {
        return this.next;
    }

    public int getData() {
        return this.data;
    }

    /**
     * 删除下一个结点
     */
    public void removeNext() {
        //取出下下一个结点
        LoopNode newNext = next.next;
        //把下下一个结点设置为当前结点的下一个结点
        this.next = newNext;
    }

    /**
     * 插入一个结点作为当前结点的下一个结点(在链表中间)
     *
     * @param node 插入的结点
     */
    public void addNode(LoopNode node) {
        //取出下一个结点作为下下一个结点
        LoopNode nextNode = next;
        //把新结点插入为当前结点的下一个结点
        this.next = node;
        //把下下一个结点设置为新结点的下一个结点
        node.next = nextNode;
    }

}

3.双向循环链表定义

若每个结点包含两个引用,一个指向下一个结点,另一个指向上一个结点,这就是双向链表。

4.双向链表操作

(1)增加一个结点;

(2)获取上一个结点;

(3)获取下一个结点;

package cn.kimtian.linkedlist;

/**
 * 这是一个双向链表
 *
 * @author kimtian
 */
public class DoubleNode {
    /**
     * 结点内容(存任何类型都可以)
     */
    private int data;

    public int getData() {
        return data;
    }

    /**
     * 上一个结点
     */
    DoubleNode preDoubleNode = this;
    /**
     * 下一个结点
     */
    DoubleNode nextDoubleNode = this;

    /**
     * 创建结点的时候,给一个value
     *
     * @param data
     */
    public DoubleNode(int data) {
        this.data = data;
    }

    /**
     * 增加一个结点
     *
     * @param doubleNode 结点
     */
    public void addDoubleNode(DoubleNode doubleNode) {

        //取出下一个结点作为下下一个结点
        DoubleNode nextNode = nextDoubleNode;
        //把新结点插入为当前结点的下一个结点
        this.nextDoubleNode = doubleNode;
        //把当前结点作为新结点的前一个结点
        doubleNode.preDoubleNode = this;
        //把下下一个结点设置为新结点的下一个结点
        doubleNode.nextDoubleNode = nextNode;
        //把下下一个结点的上一个结点设置为新结点
        nextNode.preDoubleNode = doubleNode;
    }

    /**
     * 获取下一个结点的方法
     */
    public DoubleNode getNext() {
        return this.nextDoubleNode;
    }

    /**
     * 获取上一个结点的方法
     */
    public DoubleNode getPre() {
        return this.preDoubleNode;
    }
}

5.多重链的循环链表:

如果将表中的结点链在多个环上,将构成多重链的循环链表。

猜你喜欢

转载自blog.csdn.net/third_/article/details/85470477
今日推荐