Java数据结构和算法-链表(4-双向链表)

传统的链表沿链表进行反向遍历是很困难的,比如:

current = current.next

可以很快的找到下一个链接点,但是却没有方法返回前一个链接点。根据应用的不同,这个限制可能为会引发一些问题。比如在之前提到过的要删除链表当中的最后一个链接点,虽然在双端链表中,可以通过last的引用很快的找到最后一个链接点,但是由于倒数第二个链接点无法快速找到(删除最后一个链接点时,需要将last指向倒数第二个链接点),所以仍然需要通过遍历来查找,这样的效率是非常低的。

双向链表

对于传统链表的反向遍历问题,在双向链表中能够得到很好的解决。双向链表即允许向前遍历,也允许反向遍历。因为在双向链表中,每一个链接点都有两个指向其它链接点的引用,一个指向下一个链接点,一个指向上一个链接点。而对于表头他也有first指向第一个链接点和last指向最后一个链接点两个引用。如下图显示:
这里写图片描述
在双向链表当中的对于链接点的开头申明如下:

class Link {
    private int iData;
    private Link next;
    private Link previous;
}

在双向链表的插入和删除中,要处理四个链接点,而不是两个。其中两个链接点是指向后一个链接点的,另外两个指向前一个链接点。也正是由于这个原因,链接点所占用的内存空间也就变大了。
双向链表不必包含双端链表(表头有对最后一个链接点的引用)的性质,不过这在一些情况下是有用的,所以在下面的介绍中都会包含这个性质。

遍历

对于双向链表的遍历有两种方式,一种是向前遍历(即从first开始遍历),这种方式与之前的链表的displayList相同。而向后遍历则是从last开始,通过每一个previous域不断向前遍历。

current = last
while(current != null) {
    System.out.println(current.getIData());
    current = current.previous;
}

另外有一点要说明的是,next,previous也可以用left和right来代替。这看自己的理解。

插入

双向链表的插入分为三种情况:insertFirst、insertAfter和insertLast,insertFirst和insertLast是比较简单的情况,与之前其他链表类似。而insertAfter是向链表中某一特定元素的后面插入一个链接点。
除非链表为空,否则insertFirst就把first的previous指向新的链接点,把新链接点的next指向前者,如下图:
这里写图片描述
但如果链表是空的,那么改变的就不是first的previous,而是last:

if(first == null)
    last = newLink;
else {
    first.previous = newLink;
    newLink.next = first;
}
first = newLink

insertLast的处理逻辑和insertFirst的类似,只不过insertLast插入在尾部,它的方法和insertFirst镜像。
insertAfter的处理会有点复杂,它是在特定元素值的链接点后面插入链接点,所以排除在表头插入的情况,可以分为两种情况,一个是在链表中插入,一个是在表尾插入,代码和过程图如下:

if(current == last) {
    last = newLink;
} else {
    current.next.previous = newLink;
}
newLink.next = current.next;
current.next = newLink;
newLink.previous = current;

这里写图片描述

删除

删除也有三种方法:deleteFirst、deleteKey和deleteLast,在表头和表尾删除比较容易,而deleteKey则要分情况讨论。如果查找到要删除的链接点是在表中删除,那么只需要将要删除的链接点current的前一个链接点current.previous的next指向要删除的链接点current的后一个链接点current.next,然后将current的后一个链接点current.next的previous指向要删除的链接点current的前一个链接点current.previous即可,即语句如下:

current.previous.next = current.next;
current.next.previous = current.previous;

这里写图片描述
不过如果要删除的链接点为第一个或最后一个,那么就会有些许不同,代码如下:

if(current == first)
    first = current.next
else
    current.previous.next = current.next;

if(current == last)
    last = current.previous;
else
    current.next.previous = current.previous;

双向链表的Java代码

经过之前的讨论,将这些操作都封装到类DoubleList当中,如下:

class Link {
    private int iData;
    private Link next;
    private Link previous;

    //...   
}

class DoubleList {
    private Link first;
    private Link last;

    public Link findKey(int d) {
        Link current = first;
        while(current != null && current.getIData() != d) {
            current = current.next;
        }

        return current;
    }

    public void insertFirst(int d) {
        Link newLink = new Link(d);

        if(first == null)
            last = newLink;
        else
            first.previous = newLink;
        newLink.next = first;
        first = newLink;
    }

    public void insertLast(int d) {
        Link newLink = new Link(d);

        if(last == null)
            first == newLink;
        else
            last.next = newLink;
        newLink.previous = last;
        last = newLink;
    }

    public void insertAfter(int key, int d) {
        Link newLink = new Link(d);
        Link current = findKey(key);

        if(current != null) {
            if(current.next == null)
                insertLast(d);
            else {
                newLink.next = current.next;
                current.next.previous = newLink;
                newLink.prevous = current;
                current.next = newLink;
            }
        }
    }

    public Link deleteFirst() {
        Link temp = first;
        if(first.next == null) {
            last = null;
        }else {
            first.next.previous = null;
        }
        first = first.next;

        return temp;
    }

    public Link deleteLast() {
        Link temp = last;
        if(last.previous == null)
            first = null;
        else
            last.previous.next = null;
        last = last.previous;

        return temp;
    }

    public Link deleteKey(int key) {
        Link current = findKey(key);

        if(current == first)
            first = current.next;
        else
            current.previous.next = current.next;

        if(current == last)
            last = current.previous;
        else
            current.next.previous = current.previous;

        return current;
    }

    public void displayForward() {
        Link current = first;
        while(current != null) {
            System.out.println(current.getIData());
            current = current.next;
        }
    }

    public void displayBackward() {
        Link current = last;
        while(current != null) {
            System.out.println(current.getIData());
            current = current.previous;
        }
    }

    public boolean isEmpty() {
        return (first == null);
    }
}

基于双向链表的双端队列

在前面所讲过的双端队列可以利用双向链表来实现,因为双向链表具有两端删除和插入的能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值