Java-实现双向环形链表

双向链表是一种常用的数据结构,其特点是每个节点不仅包含数据,还持有指向前一个节点和后一个节点的指针。与普通双向链表不同的是,它的哨兵节点的prev指向最后一个元素,而最后一个元素的next指向哨兵。

具体双向普通链表可以参考我的上篇文章,这里是传送门

什么是双向环形链表?

双向环形链表不仅支持双向遍历,还形成一个闭合环,即最后一个节点的next指针指向链表的头部,第一个节点的prev指针指向链表的尾部。使用这种结构可以实现更加灵活的循环遍历操作。

基本结构

在Java实现中,常见地使用一个"哨兵节点"来简化对表头和表尾的处理逻辑,使链表的实现更加一致和高效。

以下是一个基本的双向环形链表的结构模块:

java 复制代码
private static class Node {
    Node prev;
    Node next;
    int data;

    public Node(Node prev, Node next, int data) {
        this.prev = prev;
        this.next = next;
        this.data = data;
    }
}

private Node sentinel = new Node(null, null, -1);

基本操作

1. 初始化

在构造函数中,我们需要初始化哨兵节点,使其nextprev指向自身,从而形成一个初始的空环。

java 复制代码
 public BidirectionlRingList() {
        sentinel.next = sentinel;
        sentinel.prev = sentinel;
    }

2. 添加元素

我们提供了添加元素的方法addfirstaddlast和add,这三个方法分别更新哨兵节点的指针,使新节点插入链表的相应位置。

java 复制代码
   public void addfirst(int data) {
        Node newNode = new Node(sentinel, sentinel.next, data);
        sentinel.next.prev = newNode;
        sentinel.next = newNode;
    }

    public void addlast(int data) {
        Node a = sentinel.prev;
        Node b = sentinel;
        Node newNode = new Node(a, b, data);
        a.next = newNode;
        b.prev = newNode;
    }

    public void add(int index, int data) {
        Node a = findNode(index-1);
        Node b = a.next.next;
        Node newNode = new Node(a, b, data);
        a.next = newNode;
        b.prev = newNode;
        if (a ==sentinel || b == sentinel){
            throw new RuntimeException("Index out of range");
        }
    }

3. 删除元素

删除元素方法removefirstremovelast和removeByIndex,首先检查链表是否为空,然后更新相关指针去除节点。

java 复制代码
    public void removefirst() {
        Node removedNode = sentinel.next;
        if (removedNode == sentinel) {
            throw new RuntimeException("List is empty");
        }
        removedNode.next.prev = sentinel;
        sentinel.next = removedNode.next;
    }

    public void removelast() {
        Node removedNode = sentinel.prev;
        if (removedNode == sentinel){
            throw new RuntimeException("List is empty");
        }
        removedNode.prev.next = sentinel;
        sentinel.prev = removedNode.prev;
    }

    public void removeByindex(int index) {
        Node removedNode = findNode(index);
        if(removedNode == null){
            throw new RuntimeException("Index out of range");
        }
        removedNode.prev.next = removedNode.next;
        removedNode.next.prev = removedNode.prev;
    }

4. 遍历

遍历操作通过trverse实现,它遍历链表中的所有节点并打印节点数据,让用户可以观察链表中存储的数据。

java 复制代码
 public void trverse(){
        for(Node p = sentinel.next; p != sentinel; p = p.next){
            System.out.println(p.data + " ");
        }

    }

5.修改

我们可以根据索引修改值

java 复制代码
  public void setdata(int index, int data){
        Node node = findNode(index);
        node.data = data;
        if (node == null){
            throw new RuntimeException("Index out of range");
        }
    }

源码

java 复制代码
package school.bidirectionlRingList;

import java.util.Iterator;

/**
 * 文件名: null.java
 * 作者: 20526
 * 创建时间: 2024/9/10 10:35
 * 描述:双向环形链表
 */
public class BidirectionlRingList implements Iterable<Integer> {

     private Node sentinel = new Node(null, null, -1);

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            Node current = sentinel.next;
            @Override
            public boolean hasNext() {
                return current != sentinel;
            }

            @Override
            public Integer next() {
                int data = current.data;
                current = current.next;
                return data;
            }
        };
    }

    private static class Node {
        Node prev ;
        Node next ;
        int data ;

        public Node(Node prev, Node next, int data) {
            this.prev = prev;
            this.next = next;
            this.data = data;
        }
    }

    public BidirectionlRingList() {
        sentinel.next = sentinel;
        sentinel.prev = sentinel;
    }


    public void addfirst(int data) {
        Node newNode = new Node(sentinel, sentinel.next, data);
        sentinel.next.prev = newNode;
        sentinel.next = newNode;
    }

    public void addlast(int data) {
        Node a = sentinel.prev;
        Node b = sentinel;
        Node newNode = new Node(a, b, data);
        a.next = newNode;
        b.prev = newNode;
    }

    public void add(int index, int data) {
        Node a = findNode(index-1);
        Node b = a.next.next;
        Node newNode = new Node(a, b, data);
        a.next = newNode;
        b.prev = newNode;
        if (a ==sentinel || b == sentinel){
            throw new RuntimeException("Index out of range");
        }
    }



    public void removefirst() {
        Node removedNode = sentinel.next;
        if (removedNode == sentinel) {
            throw new RuntimeException("List is empty");
        }
        removedNode.next.prev = sentinel;
        sentinel.next = removedNode.next;
    }

    public void removelast() {
        Node removedNode = sentinel.prev;
        if (removedNode == sentinel){
            throw new RuntimeException("List is empty");
        }
        removedNode.prev.next = sentinel;
        sentinel.prev = removedNode.prev;
    }
    public Node findNode(int index){
        int i = 0;
        for(Node p = sentinel.next; p != sentinel; p = p.next,i++){
            if(index == i){
                return p;
            }
        }
        return null;
    }

    public void removeByindex(int index) {
        Node removedNode = findNode(index);
        if(removedNode == null){
            throw new RuntimeException("Index out of range");
        }
        removedNode.prev.next = removedNode.next;
        removedNode.next.prev = removedNode.prev;
    }

    public void trverse(){
        for(Node p = sentinel.next; p != sentinel; p = p.next){
            System.out.println(p.data + " ");
        }

    }
    public void setdata(int index, int data){
        Node node = findNode(index);
        node.data = data;
        if (node == null){
            throw new RuntimeException("Index out of range");
        }
    }




}

总结

双向环形链表是编程中一种强大且灵活的数据结构,它可用于在循环访问中保持高效。通过合理设计Node结构和使用哨兵节点,我们简化了边界条件的处理,提供了更直观的操作方法。希望对你有所帮助。

相关推荐
喝养乐多长不高1 分钟前
Rabbit MQ:概述
java·rabbitmq·mq·amqp
拾忆,想起15 分钟前
Dubbo异步调用实战指南:提升微服务并发性能
java·服务器·网络协议·微服务·云原生·架构·dubbo
Xiaochen_1224 分钟前
有边数限制的最短路:Bellman-Ford 算法
c语言·数据结构·c++·程序人生·算法·学习方法·最简单的算法理解
q***318929 分钟前
微服务生态组件之Spring Cloud LoadBalancer详解和源码分析
java·spring cloud·微服务
熬了夜的程序员2 小时前
【LeetCode】114. 二叉树展开为链表
leetcode·链表·深度优先
敏姐的后花园3 小时前
模考倒计时网页版
java·服务器·前端
Dcs5 小时前
Java 中 UnaryOperator 接口与 Lambda 表达式的应用示例
java·后端
君不见,青丝成雪6 小时前
网关整合验签
大数据·数据结构·docker·微服务·系统架构
bagadesu7 小时前
使用Docker构建Node.js应用的详细指南
java·后端