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结构和使用哨兵节点,我们简化了边界条件的处理,提供了更直观的操作方法。希望对你有所帮助。

相关推荐
青衫码上行2 小时前
【Java Web学习 | 第1篇】前端 - HTML
java·前端·学习
阿金要当大魔王~~3 小时前
uniapp img 动态渲染 的几种用法
java·服务器·前端·1024程序员节
摸鱼的老谭3 小时前
Java 25 中的最佳新特性
java·1024程序员节
lang201509283 小时前
Spring Boot健康检查全解析
java·spring boot·后端
大数据张老师3 小时前
数据结构——BF算法
数据结构·算法·1024程序员节
Yupureki4 小时前
从零开始的C++学习生活 14:map/set的使用和封装
c语言·数据结构·c++·学习·visual studio·1024程序员节
一匹电信狗4 小时前
【LeetCode_876_2.02】快慢指针在链表中的简单应用
c语言·数据结构·c++·算法·leetcode·链表·stl
胖咕噜的稞达鸭4 小时前
算法入门---专题二:滑动窗口2(最大连续1的个数,无重复字符的最长子串 )
c语言·数据结构·c++·算法·推荐算法·1024程序员节
Yupureki4 小时前
从零开始的C++学习生活 15:哈希表的使用和封装unordered_map/set
c语言·数据结构·c++·学习·visual studio·1024程序员节
我是华为OD~HR~栗栗呀4 小时前
华为OD-Java面经-21届考研
java·c++·后端·python·华为od·华为·面试