【数据结构与算法(Java)】双向链表(按顺序添加)

1. 应用场景

  • 当不知道数据量大小的时候,可以使用“链表”来动态储存和处理数据
  • 需要对链表进行反向操作时

2. 思路

  • 分类:(本文使用 “有头节点”)
    • 有头节点 链表
    • 无头节点 链表
  • 存储方式:节点
  • 节点结构
    • 数据域(Data):存储 当前节点数据
    • 地址域1(Next):指向 下一个节点地址
    • 地址域2(Pre):指向 上一个节点地址
    • 头指针:数据域无意义,只有指向下一节点的地址域
    • 不一定 是连续存储
    • 本文章按照节点值的 从小到大 顺序添加
      有序双向链表示意图

3. 数据结构(类结构):

【节点 DoubleNode】

(1)成员变量(Field)

  • data: int,节点所储存的数据
  • nextNode: Node,所指向的下一个节点
  • preNode: Node,所指向的上一个节点
    private int data;
    private DoubleNode nextNode;
    private DoubleNode preNode;

(2)初始化 / 构造器(Constructor)

  • 参数传入节点所要储存的数据
  • 初始化节点数据
    public DoubleNode(int data) {
        this.data = data;
    }

(3)方法(Methods)

- 三个成员变量的getter和setter
    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public DoubleNode getNextNode() {
        return nextNode;
    }

    public void setNextNode(DoubleNode nextNode) {
        this.nextNode = nextNode;
    }

    public DoubleNode getPreNode() {
        return preNode;
    }

    public void setPreNode(DoubleNode preNode) {
        this.preNode = preNode;
    }
- toString方法
    @Override
    public String toString() {
        return "DoubleNode{" +
                "data=" + data +
                '}';
    }

【有序双向链表 OrderedDoubleLinkedList】

(1)成员变量(Field)

  • headPointer: Node,头节点,不储存数据,只通过自身的nextNode负责储存第一个Node的地址。当有序单链表创建时直接实例化,数据域为0。
    private DoubleNode headPointer = new DoubleNode(0);

(2)初始化 / 构造器(Constructor)

  • 默认构造器

(3)方法(Methods)

- 添加新节点

addNode(Node) -> void

  1. 遍历查找插入点
  2. 将新节点插入到当前节点和下一节点之间
    /**
     * 添加新节点到双向链表
     * @param newNode - 要添加的新节点
     */
    public void addNode(DoubleNode newNode) {
        // 1. 遍历查找插入点
        // 1.1 设置节点变量,用来在遍历时储存当前节点
        DoubleNode currentNode = this.headPointer;
        // 1.2 遍历双向链表,寻找插入点,找到时结束遍历
        while (true) {
            // (1) 到达末尾 or 当前节点的next节点值大于新节点值 时,找到插入点(当前值的下一位),结束遍历
            if (currentNode.getNextNode() == null || currentNode.getNextNode().getData() > newNode.getData()) {
                break;
            }
            // (2) 找到相同值,抛出异常
            else if (currentNode.getNextNode().getData() == newNode.getData()){
                throw new RuntimeException("The node is existing!");
            }

            // (3) 当前节点的next节点值小于新节点值,继续遍历
            currentNode = currentNode.getNextNode();
        }

        // 2. 将新节点插入到当前节点和下一节点之间
        // 2.1 设置临时节点变量,用来储存当前节点的下一节点
        DoubleNode nextNode = currentNode.getNextNode();
        // 2.2 连接当前节点和新节点
        currentNode.setNextNode(newNode);
        newNode.setPreNode(currentNode);
        // 2.3 连接当前节点和next节点
        if (nextNode != null) {
            newNode.setNextNode(nextNode);
            nextNode.setPreNode(newNode);
        }
    }
- 查找某节点是否存在

isNodeExist(Node) -> boolean

  1. 若链表为空,抛出异常
  2. 遍历查找相同值
  3. 返回boolean变量
    /**
     * 查找节点是否存在于双向链表中
     * @param node - 要查找的节点
     * @return boolean - 是否存在
     */
    public boolean isNodeExist(DoubleNode node) {
        // 1. 若链表为空,抛出异常
        if (this.headPointer.getNextNode() == null) {
            throw new RuntimeException("The list is empty! ");
        }

        // 2. 遍历查找相同值
        // 2.1 设置boolean变量,用来标识是否找到相同值
        boolean found = false;
        // 2.2 设置节点变量,用来在遍历时储存当前节点
        DoubleNode currentNode = this.headPointer.getNextNode();
        // 2.3 循环遍历,查找相同值,找到or到达末尾时,结束遍历
        while (currentNode != null) {
            // (1) 若当前节点值等于输入节点值,boolean变量设置为true,结束遍历
            if (currentNode.getData() == node.getData()) {
                found = true;
                break;
            }

            // (2) 否则移动到下一节点,继续循环
            currentNode = currentNode.getNextNode();
        }

        // 3. 返回boolean变量
        return found;
    }
- 删除某节点

deleteNode(Node) -> void

  1. 若链表为空,抛出异常
  2. 遍历查找待删除节点,找到后删除(无需辅助节点,直接删除)
    /**
     * 删除双向链表中的某节点
     * @param node -- 要删除的节点
     */
    public void deleteNode(DoubleNode node) {
        // 1. 若链表为空,抛出异常
        if (this.headPointer.getNextNode() == null) {
            throw new RuntimeException("The list is empty");
        }

        // 2. 遍历查找待删除节点,找到后删除
        // 2.1 设置节点变量,用来在遍历时储存当前节点
        DoubleNode currentNode = this.headPointer.getNextNode();
        // 2.2 循环遍历,查找待删除节点,找到删除,找不到结束循环
        while (currentNode != null) {
            if (currentNode.getData() == node.getData()) {
                // (1) 设置上一节点的next节点为下一节点
                currentNode.getPreNode().setNextNode(currentNode.getNextNode());
                // (2) 若下一节点不为null,设置下一节点的pre节点为上一节点
                if (currentNode.getNextNode() != null) {
                    currentNode.getNextNode().setPreNode(currentNode.getPreNode());
                }
                break;
            }
            currentNode = currentNode.getNextNode();
        }
    }
- 获取全部节点的计数

countNodes() -> int

  1. 遍历统计节点个数
  2. 返回统计数
    /**
     * 统计双向链表的有效节点数
     * @return int - 有效节点数
     */
    public int countNodes() {
        // 1. 遍历统计节点个数
        // 1.1 设置临时int变量,用来储存节点个数
        int count = 0;
        // 1.2 设置临时节点变量,用来储存当前变量
        DoubleNode currentNode = this.headPointer.getNextNode();
        // 1.3 循环遍历,统计所有节点数
        while (currentNode != null) {
            // count加1
            count++;
            // 移动到下一节点
            currentNode = currentNode.getNextNode();
        }

        // 2. 返回统计数
        return count;
    }
- 打印全部节点的数据

displayAllNodes() -> void

    /**
     * 打印双向链表中的所有节点值
     */
    public void displayAllNode() {
        // 1. 判断链表是否为空,为空抛出异常
        if (this.headPointer.getNextNode() == null) {
            throw new RuntimeException("The double linked list is empty!");
        }

        // 2. 设置节点变量,用来在遍历时储存当前节点
        DoubleNode currentNode = this.headPointer.getNextNode();
        // 3. 遍历双向链表,打印每个节点值
        while (currentNode != null) {
            // (1) 打印节点值
            System.out.println(currentNode.toString());

            // (2) 移动节点到下一位
            currentNode = currentNode.getNextNode();
        }

    }

4. 完整实现

/**
 * 双向链表(按节点值从小到大添加)
 * @author hoy 
 */
public class OrderedDoubleLinkedList {
    private DoubleNode headPointer = new DoubleNode(0);

    /**
     * 添加新节点到双向链表
     * @param newNode - 要添加的新节点
     */
    public void addNode(DoubleNode newNode) {
        // 1. 遍历查找插入点
        // 1.1 设置节点变量,用来在遍历时储存当前节点
        DoubleNode currentNode = this.headPointer;
        // 1.2 遍历双向链表,寻找插入点,找到时结束遍历
        while (true) {
            // (1) 到达末尾 or 当前节点的next节点值大于新节点值 时,找到插入点(当前值的下一位),结束遍历
            if (currentNode.getNextNode() == null || currentNode.getNextNode().getData() > newNode.getData()) {
                break;
            }
            // (2) 找到相同值,抛出异常
            else if (currentNode.getNextNode().getData() == newNode.getData()){
                throw new RuntimeException("The node is existing!");
            }

            // (3) 当前节点的next节点值小于新节点值,继续遍历
            currentNode = currentNode.getNextNode();
        }

        // 2. 将新节点插入到当前节点和下一节点之间
        // 2.1 设置临时节点变量,用来储存当前节点的下一节点
        DoubleNode nextNode = currentNode.getNextNode();
        // 2.2 连接当前节点和新节点
        currentNode.setNextNode(newNode);
        newNode.setPreNode(currentNode);
        // 2.3 连接当前节点和next节点
        if (nextNode != null) {
            newNode.setNextNode(nextNode);
            nextNode.setPreNode(newNode);
        }
    }

    /**
     * 查找节点是否存在于双向链表中
     * @param node - 要查找的节点
     * @return boolean - 是否存在
     */
    public boolean isNodeExist(DoubleNode node) {
        // 1. 若链表为空,抛出异常
        if (this.headPointer.getNextNode() == null) {
            throw new RuntimeException("The list is empty! ");
        }

        // 2. 遍历查找相同值
        // 2.1 设置boolean变量,用来标识是否找到相同值
        boolean found = false;
        // 2.2 设置节点变量,用来在遍历时储存当前节点
        DoubleNode currentNode = this.headPointer.getNextNode();
        // 2.3 循环遍历,查找相同值,找到or到达末尾时,结束遍历
        while (currentNode != null) {
            // (1) 若当前节点值等于输入节点值,boolean变量设置为true,结束遍历
            if (currentNode.getData() == node.getData()) {
                found = true;
                break;
            }

            // (2) 否则移动到下一节点,继续循环
            currentNode = currentNode.getNextNode();
        }

        // 3. 返回boolean变量
        return found;
    }

    /**
     * 统计双向链表的有效节点数
     * @return int - 有效节点数
     */
    public int countNodes() {
        // 1. 遍历统计节点个数
        // 1.1 设置临时int变量,用来储存节点个数
        int count = 0;
        // 1.2 设置临时节点变量,用来储存当前变量
        DoubleNode currentNode = this.headPointer.getNextNode();
        // 1.3 循环遍历,统计所有节点数
        while (currentNode != null) {
            // count加1
            count++;
            // 移动到下一节点
            currentNode = currentNode.getNextNode();
        }

        // 2. 返回统计数
        return count;
    }

    /**
     * 删除双向链表中的某节点
     * @param node -- 要删除的节点
     */
    public void deleteNode(DoubleNode node) {
        // 1. 若链表为空,抛出异常
        if (this.headPointer.getNextNode() == null) {
            throw new RuntimeException("The list is empty");
        }

        // 2. 遍历查找待删除节点,找到后删除
        // 2.1 设置节点变量,用来在遍历时储存当前节点
        DoubleNode currentNode = this.headPointer.getNextNode();
        // 2.2 循环遍历,查找待删除节点,找到删除,找不到结束循环
        while (currentNode != null) {
            if (currentNode.getData() == node.getData()) {
                // (1) 设置上一节点的next节点为下一节点
                currentNode.getPreNode().setNextNode(currentNode.getNextNode());
                // (2) 若下一节点不为null,设置下一节点的pre节点为上一节点
                if (currentNode.getNextNode() != null) {
                    currentNode.getNextNode().setPreNode(currentNode.getPreNode());
                }
                break;
            }
            currentNode = currentNode.getNextNode();
        }
    }

    /**
     * 打印双向链表中的所有节点值
     */
    public void displayAllNode() {
        // 1. 判断链表是否为空,为空抛出异常
        if (this.headPointer.getNextNode() == null) {
            throw new RuntimeException("The double linked list is empty!");
        }

        // 2. 设置节点变量,用来在遍历时储存当前节点
        DoubleNode currentNode = this.headPointer.getNextNode();
        // 3. 遍历双向链表,打印每个节点值
        while (currentNode != null) {
            // (1) 打印节点值
            System.out.println(currentNode.toString());

            // (2) 移动节点到下一位
            currentNode = currentNode.getNextNode();
        }

    }
}

class DoubleNode {
    private int data;
    private DoubleNode nextNode;
    private DoubleNode preNode;

    public DoubleNode(int data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "DoubleNode{" +
                "data=" + data +
                '}';
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public DoubleNode getNextNode() {
        return nextNode;
    }

    public void setNextNode(DoubleNode nextNode) {
        this.nextNode = nextNode;
    }

    public DoubleNode getPreNode() {
        return preNode;
    }

    public void setPreNode(DoubleNode preNode) {
        this.preNode = preNode;
    }
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值