双向循环链表实现

1.1 基本介绍

双向循环链表就是在双线链表的基础上首尾相连(第一个节点的prev指向最后一个节点,最后一个节点的next指向第一个节点)。

1.2 添加操作

1、思路分析

头部插入

当整个链表都为空时,添加操作。

头结点和尾节点都指向自己。

当链表不为空时,添加操作

先把当前头节点的上一跳地址给新元素的上一跳

然后让新节点的后驱指针指向head结点,再让head的前驱指针指向新元素。

更新head结点,让head结点指向新结点,更新tail结点,让tail的下一跳重新指向head

尾部插入

将当前tail的下一跳给新节点的下一跳

tail的下一跳指向新结点,新结点的上一跳指向tail

tail重新指向新结点。

直接让head的上一跳重新指向tail

中间插入

1、离头部比较近

定义两个指针pq,找到要插入节点前驱,让p的上一跳指向新节点,让新节点的下一跳指向p

q的上一跳指向新节点,让新节点的下一跳指向q

2、离尾部比较近

q的下一跳指向新节点,让新节点的上一跳指向q

p的上一跳指向新节点,让新节点的下一跳指向p

2、代码示例

链表类: LinkedList

package cn.linkedlist.demo05;

import java.util.Iterator;

public class LinkedList<E> implements List<E>{

    // 创建Node节点
    private class Node {
        // 数据域
        E data;
        // 指向直接前驱的指针
        Node prev;
        // 指向直接后继的指针
        Node next;

        // 构造函数
        public Node() {
            this(null, null, null);
        }

        public Node(E data) {
            this(data, null, null);
        }

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

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            if (prev != null) {
                sb.append(prev.data);
            } else {
                sb.append("null");
            }

            sb.append("->").append(data).append("->");

            if (next != null) {
                sb.append(next.data);
            } else {
                sb.append("null");
            }
            return sb.toString();
        }
    }

    // 链表元素的数量
    private int size;
    // 声明头结点
    private Node head;
    // 声明尾节点
    private Node tail;

    // 初始化头结点
    public LinkedList() {
        head = null;
        tail = null;
        size = 0;
    }

    public LinkedList(E[] arr) {
        for (E e : arr) {
            add(e);
        }
    }

    //默认向表尾添加元素
    @Override
    public void add(E element) {
        add(size, element);
    }

    //在链表当中指定索引index处添加一个元素
    @Override
    public void add(int index, E element) {
        if (index < 0|| index > size) {
            throw new ArrayIndexOutOfBoundsException("add index out of bounds");
        }
        // 创建新的结点对象
        Node node = new Node(element);

        // 链表为空
        if(isEmpty()){
            head = node;
            tail = node;
            tail.next = head;
            head.prev = tail;

        }else if(index == 0){ // 在链表头部添加元素
            // 头结点的上一跳指向新节点的上一跳
            node.prev = head.prev;
            node.next = head;
            head.prev = node;
            head = node;
            tail.next = head;
        }else if(index == size){ // 在链表尾部添加元素
            node.next = tail.next;
            tail.next = node;
            node.prev = tail;
            tail = node;
            head.prev = tail;
        }else{
            // 在链表中添加元素
            Node p,q; // 定义两个指针变量
            if(index <= size / 2){
                p = head;
                for(int i =0; i < index -1 ; i++){
                    p = p.next;
                }
                q = p.next;
                p.next = node;
                node.prev = p;
                q.prev = node;
                node.next = q;
            }else{
                p = tail;
                for(int i=size -1; i > index; i--){
                    p = p.prev;
                }
                q = p.prev;
                q.next = node;
                node.prev = q;
                p.prev = node;
                node.next = p;
            }
        }
        size++;
    }

    @Override
    public int size() {
        return size;
    }

    //查找元素在链表中第一次出现的索引
    @Override
    public int indexOf(E element) {
        if(isEmpty()){
            return -1;
        }
        Node p = head;
        int index = 0;
        while (!p.data.equals(element)){
            p = p.next;
            index++;
            if(p == head){
                return -1;
            }
        }
        return index;
    }

    //在链表中判断是否包含元素element
    @Override
    public boolean contains(E element) {
        return indexOf(element) != -1;
    }

    @Override
    public boolean isEmpty() {
        return size== 0 && head == null && tail == null;
    }

    @Override
    public void clear() {
        head = null;
        tail = null;
        size = 0;
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("size=").append(size).append(", [");
        Node node = head;
        for (int i = 0; i < size; i++) {
            if (i != 0) {
                res.append(", ");
            }
            res.append(node);
            node = node.next;
        }
        res.append("]");
        return res.toString();
    }

    @Override
    public Iterator<E> iterator() {
        return new DoubleCircleLinkedListIterator();
    }

    class  DoubleCircleLinkedListIterator implements Iterator<E>{
        private Node cur = head;
        private boolean flag = true;

        @Override
        public boolean hasNext() {
            if(isEmpty()){
                return false;
            }
            return flag;
        }

        @Override
        public E next() {
            E ret = cur.data;
            cur = cur.next;
            if(cur == head){
                flag = false;
            }
            return ret;
        }
    }
}

测试类:LinkedListDemo

package cn.linkedlist.demo05;
public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        System.out.println("===链表头部插入===");
        // System.out.println(list);

        list.add(0, 1);
        list.add(0, 2);
        list.add(0, 3);
        System.out.println(list);

        System.out.println("===链表尾部插入===");
        list.add(list.size(), 4);
        list.add(list.size(), 5);
        list.add(list.size(), 6);
        System.out.println(list);
    }
}

3、执行结果

1.3 删除操作

1、思路分析

删除头结点

node节点为head.next,最终node是新的头结点。

head的下一跳置空。

head的上一跳,给node的上一跳。

head的上一跳置空!!head重新指向node

最后让尾指针下一跳重新指向head即可。

删除尾节点

nodetail.pre前驱,最终node是新的尾结点。

tail的上一跳置空。

tail的下一跳给node的下一跳,tail的下一跳置空。

tail重新指向node

head的上一跳重新指向tail

删除中间节点

1、离头部比较近

定义三个指针,p是要删除节点的前驱,q是要删除节点,r是要删除节点的后继,p指针移动到要删除节点的前驱。

p的下一跳直接指向r,让r的上一跳重新指向p

q的上一跳和q的下一跳直接置空。

删除成功。

2、离尾部比较近

定义三个节点指针,p是要删除节点的前驱,q是要删除节点,R是要删除节点的后继。

R的上一跳指向p,让p的下一跳指向R,让q的两边同时置空!!!

最后删除成功!!!

2、代码示例

链表类: LinkedList

//删除链表中指定的元素element
@Override
public void remove(E element) {
    int index = index0f(element);
    if(index != -1){
        remove(index);
    }
}

//删除链表中指定角标处index的元素
@Override
public E remove(int index) {
    if (index < 0|| index > size) {
        throw new ArrayIndexOutOfBoundsException("remove index out of bounds");
    }
    // 定义ret变量
    E ret = null;
    Node node;
    // 当链表只剩一个元素
    if(size ==1){
        ret = head.data;
        head = null;
        tail = null;
        // 删除表头
    }else if(index == 0){
        ret = head.data;
        node = head.next;
        head.next = null;
        node.prev = head.prev;
        head.prev = null;
        head = node;
        tail.next = head;
        // 删除表尾
    }else if(index == size -1){
        ret = tail.data;
        node = tail.prev;
        tail.prev = null;
        node.next = tail.next;
        tail.next = null;
        tail = node;
        head.prev = tail;
    }else{
        // 删除链表中间的某一个元素
        Node p, q, r;
        if(index <= size / 2){
            p = head;
            for(int i=0; i < index-1; i++){
                p = p.next;
            }
            q = p.next;
            ret = q.data;
            r = q.next;
            p.next = r;
            r.prev = p;
            q.next = null;
            q.prev = null;
        }else{
            p = tail;
            for(int i = size -1; i > index + 1; i--){
                p = p.prev;
            }
            q = p.prev;
            ret = q.data;
            r = q.prev;
            r.next = p;
            p.prev = r;
            q.next = null;
            q.prev = null;
        }
    }
    size --;
    return ret;
}

测试类:LinkedListDemo

package cn.linkedlist.demo05;
public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        System.out.println("===链表头部插入===");
        //System.out.println(list);

        list.add(0, 1);
        list.add(0, 2);
        list.add(0, 3);
        System.out.println(list);

        System.out.println("===链表尾部插入===");
        list.add(list.size(), 4);
        list.add(list.size(), 5);
        list.add(list.size(), 6);
        System.out.println(list);

        System.out.println("==删除元素==");
        System.out.println(list.remove(3));
        System.out.println(list);
    }
}

3、执行结果

1.4 修改和获取操作

1、代码示例

链表类: LinkedList

//获取链表中指定索引处的元素
@Override
public E get(int index) {
    if (index < 0|| index > size) {
        throw new ArrayIndexOutOfBoundsException("get index out of bounds");
    }
    // 获取头
    if(index == 0){
        return head.data;
    }else if(index == size -1){
        // 获取尾部
        return tail.data;
    }else{
        // 获取中间
        Node p = head;
        for (int i = 0; i < index; i++) {
            p = p.next;
        }
        return p.data;
    }
}

// 修改链表中指定索引index的元素为element
@Override
public E set(int index, E element) {
    if (index < 0|| index > size) {
        throw new ArrayIndexOutOfBoundsException("set index out of bounds");
    }

    E ret = null;
    // 获取头
    if(index == 0){
        // 修改头
        ret = head.data;
        head.data = element;
    }else if(index == size -1){
        // 修改尾部元素
        ret = tail.data;
        tail.data = element;
    }else{
        // 修改中间
        Node p = head;
        for (int i = 0; i < index; i++) {
            p = p.next;
        }
        ret = p.data;
        p.data = element;
    }

    return ret;
}

测试类:LinkedListDemo

package cn.linkedlist.demo05;
public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        System.out.println("===链表头部插入===");
        //System.out.println(list);

        list.add(0, 1);
        list.add(0, 2);
        list.add(0, 3);
        System.out.println(list);

        System.out.println("===链表尾部插入===");
        list.add(list.size(), 4);
        list.add(list.size(), 5);
        list.add(list.size(), 6);
        System.out.println(list);

        System.out.println("==删除元素==");
        System.out.println(list.remove(3));
        System.out.println(list);
        System.out.println("===更新元素===");
        System.out.println(list.set(2, 66));
        System.out.println(list);
        System.out.println("===获取元素===");
        System.out.println(list.get(3));
    }
}

2、执行结果

posted @ 2021-12-27 10:50  guardwhy  阅读(80)  评论(0编辑  收藏  举报