线性表-双向循环链表

双向链表:

双向链表(double liked list):就是在单向链表的每个结点中,添加一个指向前驱结点的指针域。

双向链表的存储结构:

    class DoubleNode <T>{
        DoubleNode<T> prev; //前驱
        T data; //数据域
        DoubleNode<T> next; //后驱

        public DoubleNode(DoubleNode<T> prev, T data, DoubleNode<T> next) {
            this.prev = prev;
            this.data = data;
            this.next = next;
        }
    }

双向链表循环带头结点的空链表,如图:

非空的循环带头结点的双向链表,如图

双向链表的插入操作:

插入操作不复杂,不过顺序很重要,不要写反了。

假设存储元素 e 的结点为 s,要实现将结点 s 插入 p 和 p->next 之间,需要几个步骤?

    s.prev = p;     /*将p赋值给s的前驱,即 步骤1*/
    s.next = p->next;    /*将p->next赋值给s的后驱,即 步骤2*/
    p->next.prev = s;    /*将s赋值给p->next的前驱,即 步骤3*/
    p.next = s;     /*将s赋值给p的后驱,即 步骤4*/

注意:由于第2步和第3步都用到了p->next。如果第4步先执行,这会使得p->next提前变成了s

顺序是先搞定 s 的前驱和后驱,在搞定 p->next的后驱,在搞定 p 的 后驱。

双向链表的删除操作:

删除就跟简单了,若要删除结点 p,需要两个步骤,如图:

    p->prior.next = p->next;    /*将p->next赋值给p->prior的后驱,即 步骤 1 */
    p->next.prior = p->prior;   /*将p->prior赋值给p->next的前驱,即 步骤 2 */

 

完整的实现:


/**
 * 双向链表(double linked list)可循环的
 *  是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。
 */
public class DoubleLinkedList <T> {

    private DoubleNode<T> first;    //首结点
    private DoubleNode<T> last; //尾结点
    private int size;   //有效元素个数


    /**
     * 添加元素,尾插法
     * @param data 待插入的数据
     * @return
     */
    public boolean add(T data){

        DoubleNode newNode = new DoubleNode(null, data, null);
        if (first == null) {
            newNode.prev = newNode;
            newNode.next= newNode;
            first = newNode;
        }else{
            DoubleNode l = last;
            while (l.next != first) {
                l = l.next;
            }
            newNode.prev = l;    //将前一元素赋值给新结点的元素的前驱
            newNode.next = first;   //将first赋值给新结点的后驱
            l.next = newNode;   //将新元素赋值为前一元素的后驱
            first.prev = newNode;   //first的前驱是新添加的结点
        }
        last = newNode;
        size ++;
        return true;
    }

    /**
     * 在指定位置插入元素,
     * @param data  待插入的元素
     * @param index 要插入的位置
     * @return
     */
    public boolean add(T data, int index){
        if (index - 1 == size) {//插入在最后
            return this.add(data);
        }
        rangeCheck(index);
        DoubleNode newNode = new DoubleNode(null, data, null);

        if (index == 1) {//插入在头元素
            newNode.prev = last;        //新元素的前驱是,last结点
            newNode.next = first;       //新元素的后驱是,first结点
            last.next = newNode;        //将last的后驱,跟新为新元素
            first = newNode;            //把新元素作为头结点
        }else{
            //获取前一个元素
            DoubleNode pNode = this.node(index -1);
            newNode.prev = pNode;           //把前一元素作为新元素的前驱
            newNode.next = pNode.next;      //把后一元素作为新结点的后驱
            pNode.next.prev = newNode;      //把后一元素的前驱跟改为新元素
            pNode.next = newNode;           //把前一元素的后驱跟改为新元素
        }
        size++;
        return true;
    }

    /**
     * 删除指定位置的元素
     * @param index
     * @return
     */
    public T delete(int index){

        rangeCheck(index);
        T oldValue;
        if (index == 1) {
            oldValue = first.data;
            last.next = first.next;
            first.prev = last;
            first = first.next;
        } else if (index == size) {
            oldValue = last.data;
            last.prev.next = first;
            first.prev = last.prev;
            last = last.prev;
        }else{
            DoubleNode<T> temp = this.node(index);
            oldValue = temp.data;
            temp.prev.next = temp.next;
            temp.next.prev = temp.prev;
        }
        size --;
        return oldValue;
    }

    /**
     * 返回指定位置的元素
     * @param index
     * @return
     */
    private DoubleNode<T> node(int index){
        rangeCheck(index);
        DoubleNode<T> result = null;
        if (index < size >> 1) {   //从前往后遍历
            result = first;
            for (int i = 1; i <= index -1; i++) {
                result = result.next;
            }
        }else{  //从后往前遍历
            result = last;
            for (int i = 1; i <= size - index; i++) {
                result = result.prev;
            }
        }
        return result;
    }


    public int getSize() {
        return size;
    }

    /**
     * 检测index是否合法
     * 是否不在0-链表长度范围内
     * @param index
     */
    private void rangeCheck(int index) {

        if (index < 1 || index > size){
            throw new BoxIndexOutOfBoundsException("[DoubleLinkedList] index:" + index + ", size:" + size);
        }

    }

    @Override
    public String toString() {
        String str = "DoubleLinkedList = [ ";
        DoubleNode temp = first;
        while (temp != null) {
            str += temp.data + " ";
            if (temp.next == first){
                break;
            }
            temp = temp.next;
        }
        str += "]";
        return str;
    }

    /**
     * 结点类
     */
    class DoubleNode <T>{
        DoubleNode<T> prev; //前驱
        T data; //数据域
        DoubleNode<T> next; //后驱

        public DoubleNode(DoubleNode<T> prev, T data, DoubleNode<T> next) {
            this.prev = prev;
            this.data = data;
            this.next = next;
        }
    }
}

 

总结:

双向链表在结构上比单向链表多一个指针域,所以在插入和删除操作上需要给外注意。每个结点都存储了两个指针域,使用空间来交换时间。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值