Java数据结构和算法-链表(3-有序链表)

链表的效率

在表头当中插入和删除的速度是很快的,仅仅需要一到两个引用值,和链接点的多少无关,只需O(1)的时间。
不过,查找和删除指定的链接点和在指定位置插入链接点仍然需要搜索一半的链接点,所以比较时间为O(N)。虽然,数组的查找、插入和删除的比较时间也为O(N),但链表在插入和删除链接点的时候不需要移动任何东西,这样增加的效率是显著的,特别是在复制时间比比较时间大的多时。
还有一点就是链表的内存大小是不固定的,而数组的内存大小需要在初始化时就固定其大小,数组内存空间太大会使得效率降低,但数组内存空间太小又会产生空间溢出的情况。向量是一种可扩展的数组,他通过可变长度来解决这个问题,但是它经常只允许已固定大小的增量扩展(比如在快要溢出时,将数组容量扩大一倍),不过这种方式相对于链表而言效率还是很低的。

有序链表

在链表中,当数据是按顺序排列时,链表被称为有序链表。一般来说有序链表的删除只在表头进行删除最大或最小值,有时会在链表中删除特定的值。
在用到有序数组的地方都可以用有序链表代替,有序链表由于有序数组的地方是它的插入速度(不需要移动和复制)以及数组的大小是固定的,而链表的大小不受限制。不过,有序链表的实现会比有序数组的实现困难一些。

有序链表的Java代码

在下面的代码中,有序链表通过SortedList类来实现,链表中的数据是按由小到大的顺序排列的。链接点的实现有之前是类似的不再详述:

class Link {
    private int iData;
    private Link next;

    //...
}

class SortedList {
    private Link first;

    public void insert(int data) {
        Link current, previous;
        previous = null;
        current = first;

        while(current != null && data > current.getIData()) {
            previous = current;
            current = current.next;
        }

        Link newLink = new Link(data);
        if(previous == null) {
            first = newLink;
        } else {
            newLink.next = current;
            previous.next = newLink;
        }
    }

    public int deleteFirst() {
        Link temp = first;
        first = first.next;
        return temp.getIData();
    }

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

    public void displayList() {
        Link current
        current = first;
        while(current != null) {
            System.out.println("the Link: " + current.getIData());
            current = current.next;
        }
    }
}

在有序链表中插入数据时,首先定义两个链接点current表示当前检索到的结点和previous表示之前一个链接点,并且初始时previous为null,current从first开始。依次检索下去,直到找到合适的存放位置。在程序当中,current为null的情况有两种,一个是表为空时,一个是检索到了表尾,如果表为空也就是previous为null的情况,那么直接将新链接点赋值给first。

有序链表的效率

有序链表的插入和删除特定的某一些都要平均查找N/2个数据,所以时间为O(N)。不过有序链表的删除最小(大)值,那么只需要O(1)的时间。如果一个应用程序频繁的查找删除最值,且不需要快速的插入数据,选择使用有序链表是一个不错的选择。比如,优先级队列可以选择有效链表实现。

表插入排序

利用表插入排序,是一种高效的排序机制。比如,对于一个无序数组,可以将其插入到有序链表当中,然后再将数据从有序链表中删除,重新赋值到数组中,此时数组当中的数据就是有序的了。
这种排序机制相对于直接利用数组进行排序要高效许多,因为它在复制上少了很多的时间。对于数据的比较而言,两者是一样的,如果有N个数据,插入一个新数据都要比较一半的数据,所以总的要消耗O(N^2)的时间。但是对于复制来说,在之前的简单排序的章节中已经提到,数组总共复制要消耗O(N^2),而表插入排序数组到链表,再从链表到数组,总共需要复制2*N,所以消耗的时间为O(N),两者比较,表插入排序要少得多。

表插入排序的Java代码

在下面的代码中是将存放了链接点Link的无序数组进行排序:

class Link {
    private int iData;
    private Link next;

    //...
}

class SortedList {
    private Link first;

    private SortedList(Link[] arr) {
        for(int i = 0; i < arr.length(); i ++) {
            insert(arr[i]);
        }
    }

    private void insert(Link data) {
        Link current, prevoius;
        previous = null;
        current = first;

        while(current != null && data.getIData() > current.getIData()) {
            previous = current;
            current = current.next;
        }

        if(previous == null) {
            first = data;
        } else {
            data.next = current;
            previous.next = data;
        }
    } 

    public Link delete() {
        Link temp = first;
        first = first.next;
        return temp;
    }

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

表插入排序相对于数组的插入排序而言,虽然有缺点,就是要开辟差不多两倍的内存空间,有序链表和数值必须同时存在于内存空间当中。不过如果有现成的链表可用,表插入排序对不太大的数组进行排序是比较便利的。

你可能感兴趣的:(Java数据结构和算)