数据结构与算法(5)-链表,双端链表,有序链表,双向链表,迭代器

1.普通链表

链表结构的每个节点汇包含一个数据域和一个引用域,引用域直接存放下一个节点的地址.
如图所示:
这里写图片描述

  • 代码示例
//链节点
public class Link {
    private String data;
    public Link next;

    public Link(String data){
        this.data = data;
    }

    public String getData(){
        return this.data;
    }

    public void display(){
        System.out.println("节点是"+data+",它的下一个节点是"+(null==next?"null":this.next.getData()));
    }
}
//链表
public class LinkList {
    private Link first; //最前节点

    public LinkList(){
        first = null;
    }

    //新增节点
    public void insert(Link link){
        link.next = first;  //新节点的next指向first
        first = link;   //first变为新节点
        System.out.print("插入新节点:");
        link.display();

    }

    //获取节点,并未实际删除
    public Link delete(){
        Link result = null;
        if(!isEmpty()){
            result = first;
            first = result.next;
        }
        if(null!=result){
            System.out.print("删除的节点是:");
            result.display();
        }else{
            System.out.println("已无节点可删除!!!");
        }
        return result;
    }

    //是否为空
    public boolean isEmpty(){
        return null==first;
    }

    //查找指定节点
    public Link find(String key){
        Link temp = first;
        while(key!=temp.getData()){
            temp = temp.next;
            if(null == temp){
                break;
            }
        }
        if(null == temp){
            System.out.println("未找到节点,返回null!");
        }else{
            System.out.print("找到的节点是:");
            temp.display();
        }
        return temp;
    }

    //删除指定节点
    public Link findAndDelete(String key){
        Link cur = first;
        Link pre = first;

        while(key != cur.getData()){
            if(null == cur.next){
                //如果当前节点未匹配,下一个节点为空,则未查找到.
                System.out.println("未找到节点!!!");
                return null;
            }else{
                //pre为cur的上一个节点
                pre = cur;
                //cur变为下一个节点
                cur = cur.next;
            }
        }
        //将cur移除(直接将pre的下个节点指向cur的下一个节点)
        pre.next = cur.next;
        if(null!=cur){
            System.out.print("删除的节点是:");
            cur.display();
        }else{
            System.out.println("已无节点可删除!!!");
        }
        return cur;
    }

    //查找指定节点并删除
    public static void main(String[] args) {
        LinkList list = new LinkList();
        list.insert(new Link("001"));
        list.insert(new Link("002"));
        list.insert(new Link("003"));
        list.insert(new Link("004"));
        list.insert(new Link("005"));

        System.out.println("====================");
        list.delete();
        list.delete();

        System.out.println("====================");
        list.find("002");

        System.out.println("====================");
        list.findAndDelete("002");

        System.out.println("====================");
        list.delete();
        list.delete();
    }
}

输出:

插入新节点:节点是001,它的下一个节点是null
插入新节点:节点是002,它的下一个节点是001
插入新节点:节点是003,它的下一个节点是002
插入新节点:节点是004,它的下一个节点是003
插入新节点:节点是005,它的下一个节点是004
====================
删除的节点是:节点是005,它的下一个节点是004
删除的节点是:节点是004,它的下一个节点是003
====================
找到的节点是:节点是002,它的下一个节点是001
====================
删除的节点是:节点是002,它的下一个节点是001
====================
删除的节点是:节点是003,它的下一个节点是001
删除的节点是:节点是001,它的下一个节点是null

2.双端链表
first和last初始都为空,两端都可以插入,只有一个节点时,既是first也是last.
如图所示:
这里写图片描述

  • 代码示例
//双端链表
public class FirstLastLinkList {
    private Link first;
    private Link last;

    public FirstLastLinkList(){
        first = null;
        last = null;
    }

    //从头插入
    public void insertFirst(Link link){
        if(isEmpty()){  //链表为空时,first和last都是这个节点
            last = link;
        }
        link.next = first;  //不为空时,操作与last无关
        first = link;
        System.out.print("插入新节点:");
        link.display();
    }

    //从尾插入
    public void insertLast(Link link){
        if(isEmpty()){  //链表为空时,first和last都是这个节点.直接赋值即可
            first = link;
        }else{
            last.next = link;   //不为空时,操作与first无关.
        }
        last = link;
        System.out.print("插入新节点:");
        link.display();
    }

    //从头删除一个
    public Link deleteFirst(){
        if(isEmpty()){
            System.out.println("链表为空!!!");
            return null;
        }

        Link temp = first;
        if(null == first.next){ //如果只有一个元素,last也要置空
            last = null;
        }
        first = first.next;
        System.out.print("删除的节点是:");
        temp.display();
        return temp;
    }


    //判断是否为空
    public boolean isEmpty(){
        return null==first;
    }

    public static void main(String[] args) {
        FirstLastLinkList list = new FirstLastLinkList();
        list.insertFirst(new Link("001"));
        list.insertFirst(new Link("002"));
        list.insertFirst(new Link("003"));
        list.insertLast(new Link("002"));
        list.insertLast(new Link("001"));

        list.deleteFirst();
        list.deleteFirst();
        list.deleteFirst();
        list.deleteFirst();
        list.deleteFirst();
    }
}

输出:

插入新节点:节点是001,它的下一个节点是null
插入新节点:节点是002,它的下一个节点是001
插入新节点:节点是003,它的下一个节点是002
插入新节点:节点是002,它的下一个节点是null
插入新节点:节点是001,它的下一个节点是null
删除的节点是:节点是003,它的下一个节点是002
删除的节点是:节点是002,它的下一个节点是001
删除的节点是:节点是001,它的下一个节点是002
删除的节点是:节点是002,它的下一个节点是001
删除的节点是:节点是001,它的下一个节点是null
  • 链表的效率
    插入和删除 O(1)
    查找O(N)

  • 链表的优点
    和数组相比,删除操作不需要节点的移动,效率有一定的提升.
    另一个优点,链表的内存利用率高,有几个节点就占用多少内存,不需要指定节点数据.而数组需要预先开辟出一定长度的内存.虽然数组支持动态扩展,仍然是按比例的,比如长度为10扩展到20.

  • 链表的缺点
    链表相对数组的缺点:随机查找不方便.数组可以根据角标随机取出一个位置的数.而链表无法这么做,只能从头开始遍历.

  • 链表的用途
    普通链表实现栈,从first插入,从first取出即可,且理论上长度不限.
    双端链表实现队列,从last插入,从first取出.很好地避免了用数组实现时的指针回绕问题.
    有序链表可以用于实现优先级队列.

3.有序链表
节点在链表中依大小有序排列.

  • 有序链表的优缺点
    有序列表相对于有序数组的优点:
    插入,删除效率较高,不用移动节点
    内存利用率高.

  • 有序链表的效率
    删除O(1) 插入O(N)

  • 有序链表的用途
    有序链表可以用于实现优先级队列
    有序链表可以用于数组的排序: 以插入排序为例,时间复杂度为O(N的平方).

  • 代码示例

//链节点
public class Link {
    private int data;
    public Link next;

    public Link(int data){
        this.data = data;
    }

    public int getData(){
        return this.data;
    }

    public void display(){
        System.out.println("节点是"+data+",它的下一个节点是"+(null==next?"null":this.next.getData()));
    }
}
//有序链表
public class SortedLinkList {
    private Link first;

    public SortedLinkList(){
        this.first = null;
    }

    //插入时进行排序
    public void insert(Link link){
        Link pre = null;
        Link cur = first;

        while(cur!=null && link.getData() <= cur.getData()){
            pre = cur;
            cur = cur.next;
        }

        if(null == pre){    //如果cur没有移动
            first = link;
        }else{
            pre.next = link;
        }
        link.next = cur;
        System.out.println("插入节点:"+link.getData());
    }

    //删除一般只删除表头节点
    public Link remove(){
        Link temp = first;
        if(null!=first){
            first = first.next;
        }

        System.out.println("取出节点:"+(null==temp?"null":temp.getData()));
        return temp;
    }

    public static void main(String[] args) throws Exception{
        SortedLinkList list = new SortedLinkList();
        list.insert(new Link(20));
        list.insert(new Link(70));
        list.insert(new Link(60));

        list.remove();
        list.remove();
        list.remove();
    }
}

输出:

插入节点:20
插入节点:70
插入节点:60
取出节点:70
取出节点:60
取出节点:20

4.双向链表
双向链表节点中有两个引用域,分别指向上一个节点和下一个节点,双向链表也可以是双端链表.
如图所示:

  • 双向链表的应用
    如文本编辑器中,光标不仅可以向前移动还可以向后移动.

  • 代码示例
    这里写图片描述

//链表
public class Link {
    private int data;   
    public Link next;
    public Link pre;

    public Link(int data){
        this.data = data;
    }

    public int getData(){
        return this.data;
    }

    public void display(){
        System.out.println("节点是"+data+",它的下一个节点是"+(null==next?"null":this.next.getData())+",它的上一个节点是"+(null==pre?"null":this.pre.getData()));
    }
}
//双向链表
public class DoublyLinkedList {
    private Link first;
    private Link last;

    public DoublyLinkedList(){
        first = null;
        last = null;
    }

    //判断是否为空
        public boolean isEmpty(){
            return null == first;
        }

    //在头部插入
    public void insertFirst(Link link){
        if(isEmpty()){
            last = link;
        }else{
            first.pre = link;
            link.next = first;

        }
        System.out.print("节点被增加:");
        link.display();
        first = link;
    }

    //在尾部插入
    public void insertLast(Link link){
        if(isEmpty()){
            first = link;
        }else{
            last.next = link;
            link.pre = last;
        }
        System.out.print("节点被增加:");
        link.display();
        last = link;
    }

    //在头部删除
    public Link deleteFirst(){
        if(isEmpty()){  //如果为空
            System.out.println("链表已为空!!!");
            return null;
        }
        Link temp = first;

        if(null == first.next){ //如果只有一个节点
            last = null;
        }else{
            first.next.pre = null;//删除第一个节点前先把第二个节点的前节点置空
        }
        first = first.next;
        System.out.print("节点被删除:");
        temp.display();
        return temp;
    }

    //在尾部删除
    public Link deleteLast(){
        if(isEmpty()){
            System.out.println("链表已为空!!!");
            return null;
        }

        Link temp = last;
        if(null == first.next){ //只有一个节点
            first = null;
        }else{
            last.pre.next = null;   //删除最后一个节点先将倒数第二个节点的后节点置空
        }
        last = last.pre;
        System.out.print("节点被删除:");
        temp.display();
        return temp;
    }

    public static void main(String[] args) {
        DoublyLinkedList list = new DoublyLinkedList();
        list.insertFirst(new Link(10));
        list.insertFirst(new Link(20));
        list.insertLast(new Link(40));
        list.insertLast(new Link(30));

        list.deleteFirst();
        list.deleteFirst();
        list.deleteLast();
        list.deleteLast();
    }
}

输出:

节点被增加:节点是10,它的下一个节点是null,它的上一个节点是null
节点被增加:节点是20,它的下一个节点是10,它的上一个节点是null
节点被增加:节点是40,它的下一个节点是null,它的上一个节点是10
节点被增加:节点是30,它的下一个节点是null,它的上一个节点是40
节点被删除:节点是20,它的下一个节点是10,它的上一个节点是null
节点被删除:节点是10,它的下一个节点是40,它的上一个节点是null
节点被删除:节点是30,它的下一个节点是null,它的上一个节点是40
节点被删除:节点是40,它的下一个节点是null,它的上一个节点是null

5.迭代器
将集合的和遍历相关操作委托给迭代器,通过迭代器中指针的移动进行相关操作.

  • 代码示例
public class Link {
    private int data;
    public Link next;

    public Link(int data){
        this.data = data;
    }

    public void display(){
        System.out.print(data+">");
    }
}
public class LinkList {
    private Link first;

    public LinkList(){
        first = null;
    }

    //获取第一个节点
    public Link getFirst(){
        return first;
    }

    //设置第一个节点
    public void setFirst(Link f){
        first = f;
    }

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

    //获取迭代器
    public ListIterator getIterator(){
        return new ListIterator(this);
    }

    //展示list
    public void displayList(){
        Link cur = first;
        while(null != cur){
            cur.display();
            cur = cur.next;
        }
        System.out.println();
    }
}
//迭代器的主要作用是将集合的一些遍历操作委托给迭代器
public class ListIterator {
    private Link cur;
    private Link pre;
    private LinkList list;

    public ListIterator(LinkList list){
        this.list = list;
        reset();
    }

    //重置迭代器
    public void reset(){
        cur = list.getFirst();
        pre = null;
    }

    //判断是否在末尾
    public boolean atEnd(){
        return null == cur.next;
    }

    //指针移动到下一个
    public void goNext(){
        pre = cur;
        cur = cur.next;
    }

    //获取当前指针
    public Link getCur(){
        return cur;
    }

    //在当前指针后插入
    public void insertAfterCur(Link newLink){
        //如果list为空
        if(list.isEmpty()){
            list.setFirst(newLink);
            cur = newLink;
        }else{
            newLink.next = cur.next;
            cur.next = newLink;
            goNext();
        }
    }

    //在当前指针前插入
    public void insertBeforeCur(Link newLink){
        if(null == pre){    //指针在链头
            pre.next = list.getFirst();
            list.setFirst(newLink);
            reset();
        }else{
            newLink.next = pre.next;
            pre.next = newLink;
            cur = newLink;
        }
    }

    //获取当前指针
    public Link deleteCur(){
        Link temp = cur;
        if(list.isEmpty()){
            System.out.println("链表为空!!!");
            return null;
        }
        if(null == pre){//指针在链头
            list.setFirst(cur.next);
            reset();
        }else{
            pre.next = cur.next;
            if(atEnd()){    //指针在链尾,直接移到链头
                reset();
            }else{
                cur = cur.next;
            }
        }

        return temp;
    }

    public static void main(String[] args) {
        LinkList list = new LinkList();
        ListIterator iterator = list.getIterator();

        iterator.insertAfterCur(new Link(10));
        iterator.insertAfterCur(new Link(20));
        iterator.insertBeforeCur(new Link(30));
        iterator.insertBeforeCur(new Link(40));
        list.displayList();
        iterator.deleteCur();
        list.displayList();
    }
}

输出:

10>40>30>20>
10>30>20>

猜你喜欢

转载自blog.csdn.net/chixiaoen/article/details/79793003