【数据结构和算法6】双向链表的 定义及操作的代码实现(带哨兵)

目录

 双向链表的实现(有哨兵)

1、双向链表的定义表示

2、头部添加数据

3、尾部添加数据

4、获取链表数据和长度

5、根据索引获取对应节点的数据

6、在指定索引的位置添加数据

7、移除链表中的元素

8、遍历链表

9、基于增强 for 进行遍历


 双向链表的实现(有哨兵)

1、双向链表的定义表示

根据双向链表的形式,定义双向链表的结构如下。

在双向链表中的每一个节点 Node 包含三部分,上一个节点,当前数据,下一个节点地址。

因为存在哨兵,这里定义了两个哨兵,head 和 tail ,没有具体含义,只起到引导作用。

/**
 * 双向链表
 * 有哨兵节点,哨兵节点是 head 节点,head 节点后面的数据才是真正的数据
 *
 * @author zjj_admin
 */
public class DoublyLinkedListSentinel implements Iterable<Integer> {
​
    /**
     * 头哨兵
     */
    private Node head;
    /**
     * 尾哨兵
     */
    private Node tail;
​
    public DoublyLinkedListSentinel() {
        //初始化头哨兵和尾哨兵
        this.head = new Node(null, Integer.MIN_VALUE, null);
        this.tail = new Node(null, Integer.MAX_VALUE, null);
        // 让头哨兵和尾哨兵相连
        head.next = tail;
        tail.prev = head;
    }
​
​
    /**
     * 链表中的节点,链表和节点是组合的关系,所以做成内部内比较合适。
     */
    private static class Node {
​
        /**
         * 上一个节点
         */
        Node prev;
        /**
         * 节点数据
         */
        int value;
​
        /**
         * 当前节点的下一个指针
         */
        Node next;
​
        public Node(Node prev, int value, Node next) {
            this.prev = prev;
            this.value = value;
            this.next = next;
        }
    }
​
}
2、头部添加数据
  
  /**
     * 在链表的头部插入一个数据
     *
     * @param value 待插入数据
     */
    public void addFirst(int value) {
        Node next = head.next;
        Node inserted = new Node(head, value, next);
        //让上一个节点的 next 指针指向带插入节点
        head.next = inserted;
        //让下一个极点的 prev 指针指向待插入节点
        next.prev = inserted;
    }

3、尾部添加数据
   /**
     * 添加数据到尾结点
     *
     * @param value
     */
    public void addLast(int value) {
        Node last = findLast();
        if (last == null) {
            //没有数据
            addFirst(value);
            return;
        }
        //最后节点的下一个节点,一般是 tail 节点
        Node next = last.next;
        Node inserted = new Node(last, value, next);
        last.next = inserted;
        //让下一个极点的 prev 指针指向待插入节点
        next.prev = inserted;
    }
​
    /**
     * 找到当前链表的最后一个有效节点
     *
     * @return
     */
    private Node findLast() {
        if (head.next == tail) {
            return null;
        }
        Node p = head.next;
        while (p != null) {
            if (p.next == tail) {
                return p;
            }
            p = p.next;
        }
        return null;
    }

4、获取链表数据和长度
   /**
     * 获取链表的有效长度
     *
     * @return
     */
    public int size() {
        Node p = head.next;
        int size = 0;
        while (p != tail) {
            size++;
            p = p.next;
        }
        return size;
    }
​
    /**
     * 获取链表中的所有数据
     *
     * @return 数据数组
     */
    public int[] values() {
        int size = size();
        int[] res = new int[size];
        Node p = head.next;
        int i = 0;
        while (p != tail) {
            res[i] = p.value;
            p = p.next;
            i++;
        }
        return res;
    }

5、根据索引获取对应节点的数据
   
 /**
     * 获取索引为 index 对应节点的数据
     *
     * @param index
     * @return
     */
    public int get(int index) {
        Node node = getNode(index);
        if (node == head || node == tail || node == null) {
            throw new IndexOutOfBoundsException("index:" + index + " 越界了");
        }
        return node.value;
    }
​
​
    /**
     * 获取索引为 index 对应节点
     * 头哨兵的索引为 -1
     *
     * @param index
     * @return
     */
    private Node getNode(int index) {
        int i = -1;
        Node p = head;
        while (p != null) {
            if (i == index) {
                return p;
            }
            i++;
            p = p.next;
        }
        return null;
    }
​

6、在指定索引的位置添加数据
   /**
     * 在指定的索引位置添加数据
     *
     * @param index
     * @param value
     */
    public void add(int index, int value) {
        //获取上一个节点
        Node prev = getNode(index - 1);
        if (prev == null) {
            throw new IndexOutOfBoundsException("index:" + index + " 越界了");
        }
        Node next = prev.next;
        //需要插入的节点
        Node inserted = new Node(prev, value, next);
        //让上一个节点的 next 指针指向带插入节点
        prev.next = inserted;
        //让下一个极点的 prev 指针指向待插入节点
        next.prev = inserted;
    }
​

7、移除链表中的元素
  
  /**
     * 移除链表中的第一个元素
     *
     * @return
     */
    public int removeFirst() {
        Node p = head.next;
        if (p == tail) {
            throw new NullPointerException("链表没有数据了");
        }
        Node next = p.next;
        head.next = next;
        next.prev = head;
        return p.value;
    }
​
    /**
     * 根据索引数据
     *
     * @param index 删除索引
     * @return 被删除的索引节点数据
     */
    public int remove(int index) {
        //获取待删除的节点
        Node removed = getNode(index);
        if (removed == null || removed == head || removed == tail) {
            throw new IndexOutOfBoundsException("index:" + index + " 越界了");
        }
        Node prev = removed.prev;
        Node next = removed.next;
        prev.next = next;
        next.prev = prev;
        return removed.value;
    }

8、遍历链表

    /**
     * 遍历链表,使用函数式接口 Consumer
     */
    public void loop(Consumer<Integer> consumer) {
        Node p = head.next;
        //当前节点不为 null 就一直循环
        while (p != tail) {
            // accept 里面传入相关的参数
            consumer.accept(p.value);
            p = p.next;
        }
    }

9、基于增强 for 进行遍历

需要实现 Iterable 接口

/**
 * 双向链表
 * 有哨兵节点,哨兵节点是 head 节点,head 节点后面的数据才是真正的数据
 *
 * @author zjj_admin
 */
public class DoublyLinkedListSentinel implements Iterable<Integer> {
    
   /**
     * 基于迭代器进行循环
     *
     * @return
     */
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            Node p = head.next;
​
            @Override
            public boolean hasNext() {
                return p != tail;
            }
​
            @Override
            public Integer next() {
                int v = p.value;
                p = p.next;
                return v;
            }
        };
    }
​
​
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值