4.链表

1.基础知识

1.1线性结构比较

RnPlO.png

1.2 链表

  • 数据存储在”节点”中
  • 优点:真正的动态,不需要处理固定容量的问题
  • 缺点:丧失了随机访问的能力。

1.3数组和链表的对比

RncBq.png

2.添加元素

package com.antfin.datastructure.linkedlist;

public class LinkedList <E> {
    private class Node{
        private E e;
        private Node next;
        public  Node(E e,Node next){
            this.e=e;
            this.next=next;
        }
        public Node(E e){
            this(e,null);
        }
        public Node(){
            this(null,null);
        }
        @Override
        public String toString(){
            return  e.toString();
        }
    }
    private Node head;
    private int size;
    public LinkedList(){
        head=null;
        size=0;
    }
    //获取链表中元素个数
    public int getSize(){
        return  size;
    }
    //返回链表是否为空
    public boolean isEmpty(){
        return size==0;
    }
    //在链表头添加新的元素e
    public void addFirst(E e){
        head=new Node(e,head);
        size++;
    }
    //在链表的index(0-based)位置添加新的元素e
    //在链表中不是一个常用的操作,练习用
    public void  add(int index,E e){
        if (index<0||index>size)
            throw new IllegalArgumentException("Add failed. Illegal index.");
        if (index==0)
            addFirst(e);
        else {
            Node pre=head;
            for (int i=0;i<index-1;i++){
                pre=pre.next;
            }
            pre.next=new Node(e,pre.next);
            size++;
        }
    }
    public void addLast(E e){
        add(size,e);
    }
}

3.虚拟头结点(DummyHead)

使用虚拟节点可以使添加头结点和添加非头结点的逻辑保持一致。

  package com.antfin.datastructure.linkedlist;

public class LinkedList <E> {
    private class Node{
        private E e;
        private Node next;
        public  Node(E e,Node next){
            this.e=e;
            this.next=next;
        }
        public Node(E e){
            this(e,null);
        }
        public Node(){
            this(null,null);
        }
        @Override
        public String toString(){
            return  e.toString();
        }
    }
    private Node dummyHead;
    private int size;
    public LinkedList(){
        dummyHead=new Node();
        size=0;
    }
    //获取链表中元素个数
    public int getSize(){
        return  size;
    }
    //返回链表是否为空
    public boolean isEmpty(){
        return size==0;
    }
    //在链表头添加新的元素e
    public void addFirst(E e){
       add(0,e);
    }
    //在链表的index(0-based)位置添加新的元素e
    //在链表中不是一个常用的操作,练习用
    public void  add(int index,E e){
        if (index<0||index>size)
            throw new IllegalArgumentException("Add failed. Illegal index.");
        Node pre=dummyHead;
        for (int i=0;i<index;i++){
            pre=pre.next;
        }
        pre.next=new Node(e,pre.next);
        size++;

    }
    public void addLast(E e){
        add(size,e);
    }
}

4.包含、查询和修改

// 获得链表的第index(0-based)个位置的元素
    // 在链表中不是一个常用的操作,练习用:)
    public E get(int index){
        if (index<0||index>size-1)
            throw  new IllegalArgumentException("Get failed.Illegal index");
        Node target=dummyHead;
        for (int i=0;i<=index;i++)
            target=target.next;
        return target.e;
    }
    public E getFirst(){
        return  get(0);
    }
    public E getLast(){
        return get(size-1);
    }
    // 修改链表的第index(0-based)个位置的元素为e
    // 在链表中不是一个常用的操作,练习用:)
    public void set(int index,E e){
        if (index<0||index>size-1)
            throw new IllegalArgumentException("Set failed.Illegal index");
        Node target=dummyHead;
        for (int i=0;i<=index;i++)
            target=target.next;
        target.e=e;
    }
    // 查找链表中是否有元素e
    public boolean contain(E e){
        Node target=dummyHead.next;
        while (target!=null){
            if (target.e.equals(e))
                return true;
            target=target.next;
        }
        return false;
    }

5.删除元素

 // 从链表中删除index(0-based)位置的元素, 返回删除的元素
    // 在链表中不是一个常用的操作,练习用:)
    public E remove(int index){
        if (index <0||index>size-1)
            throw  new IllegalArgumentException("Delete failed.index illegal");
        Node pre=dummyHead;
        for (int i=0;i<index;i++){
            pre=pre.next;
        }
        Node retNode=pre.next;
        pre.next=retNode.next;
        retNode.next=null;//之前没有考虑到这一步的操作,这一步的目的在于便于垃圾回收
        size--;
        return retNode.e;
    }
    public E removeLast(){
        return remove(size-1);
    }
    public E removeFirst(){
        return remove(0);
    }

6.时间复杂度

ocGri.png

7.完整代码

package com.antfin.datastructure.linkedlist;

public class LinkedList <E> {
    private class Node{
        private E e;
        private Node next;
        public  Node(E e,Node next){
            this.e=e;
            this.next=next;
        }
        public Node(E e){
            this(e,null);
        }
        public Node(){
            this(null,null);
        }
        @Override
        public String toString(){
            return  e.toString();
        }
    }
    private Node dummyHead;
    private int size;
    public LinkedList(){
        dummyHead=new Node();
        size=0;
    }
    //获取链表中元素个数
    public int getSize(){
        return  size;
    }
    //返回链表是否为空
    public boolean isEmpty(){
        return size==0;
    }
    //在链表头添加新的元素e
    public void addFirst(E e){
       add(0,e);
    }
    //在链表的index(0-based)位置添加新的元素e
    //在链表中不是一个常用的操作,练习用
    public void  add(int index,E e){
        if (index<0||index>size)
            throw new IllegalArgumentException("Add failed. Illegal index.");
        Node pre=dummyHead;
        for (int i=0;i<index;i++){
            pre=pre.next;
        }
        pre.next=new Node(e,pre.next);
        size++;

    }
    public void addLast(E e){
        add(size,e);
    }
    // 获得链表的第index(0-based)个位置的元素
    // 在链表中不是一个常用的操作,练习用:)
    public E get(int index){
        if (index<0||index>size-1)
            throw  new IllegalArgumentException("Get failed.Illegal index");
        Node target=dummyHead;
        for (int i=0;i<=index;i++)
            target=target.next;
        return target.e;
    }
    public E getFirst(){
        return  get(0);
    }
    public E getLast(){
        return get(size-1);
    }
    // 修改链表的第index(0-based)个位置的元素为e
    // 在链表中不是一个常用的操作,练习用:)
    public void set(int index,E e){
        if (index<0||index>size-1)
            throw new IllegalArgumentException("Set failed.Illegal index");
        Node target=dummyHead;
        for (int i=0;i<=index;i++)
            target=target.next;
        target.e=e;
    }
    // 查找链表中是否有元素e
    public boolean contain(E e){
        Node target=dummyHead.next;
        while (target!=null){
            if (target.e.equals(e))
                return true;
            target=target.next;
        }
        return false;
    }
    // 从链表中删除index(0-based)位置的元素, 返回删除的元素
    // 在链表中不是一个常用的操作,练习用:)
    public E remove(int index){
        if (index <0||index>size-1)
            throw  new IllegalArgumentException("Delete failed.index illegal");
        Node pre=dummyHead;
        for (int i=0;i<index;i++){
            pre=pre.next;
        }
        Node retNode=pre.next;
        pre.next=retNode.next;
        retNode.next=null;//之前没有考虑到这一步的操作,这一步的目的在于便于垃圾回收
        size--;
        return retNode.e;
    }
    public E removeLast(){
        return remove(size-1);
    }
    public E removeFirst(){
        return remove(0);
    }
    @Override
    public String toString() {
        StringBuilder res=new StringBuilder();
        for (Node tar=dummyHead.next;tar!=null;tar=tar.next){
            res.append(tar+"-->");
        }
        res.append("NULL");
        return res.toString();
    }

}

8.链表应用1:栈

ocMxy.png

package com.antfin.datastructure.linkedlist;

public class LinkedListStack <E>  implements Stack<E>{
    private  LinkedList<E> list;
    public LinkedListStack(){
        list=new LinkedList<>();
    }
    @Override
    public int getSize(){
        return list.getSize();
    }
    @Override
    public boolean isEmpty(){
        return  list.getSize()==0;
    }
    @Override
    public void push(E e){
        list.addFirst(e);
    }
    @Override
    public E pop(){
        return list.removeFirst();
    }
    @Override
    public E peak(){
        return  list.getFirst();
    }
    @Override
    public String toString(){
        StringBuilder res=new StringBuilder();
        res.append("Stack :top");
        res.append(list);
        return res.toString();
    }
}

与ArrayStack比较

  • 时间复杂度在同一级别
  • ArrayStack耗时在于开辟新的空间
  • LinkedListStack耗时在于new操作

9.链表应用2:队列 (使用尾指针)

oc1qh.png

两端插入元素都很容易,但是tail端删除元素不容易,所以head为队首,tail端为队尾。

package com.antfin.datastructure.queue;

public class LinkedListQueue<E> implements  queue<E> {
    private class Node{
        private E e;
        private Node next;
        public  Node(E e,Node next){
            this.e=e;
            this.next=next;
        }
        public Node(E e){
            this(e,null);
        }
        public Node(){
            this(null,null);
        }
        @Override
        public String toString(){
            return  e.toString();
        }
    }
    private Node head,tail;
    private int size;
    public LinkedListQueue(){
        head=null;
        tail=null;
        size=0;
    }
    @Override
    public int getSize(){
        return size;
    }
    @Override
    public boolean isEmpty(){
        return size==0;
    }
    @Override
    public void enqueue(E e){
        if (tail==null){
            tail=new Node(e);
            //tail为空,那么head应该也为空
            head=tail;
        }else {
            tail.next=new Node(e);
            tail=tail.next;
        }
        size++;
    }
    @Override
    public E dequeue(){
        if (isEmpty())
            throw new IllegalArgumentException("queue is empty,cannot dequeue");
        Node retNode=head;
        head=head.next;
        retNode.next=null;
        if (head==null)
            tail=null;
        size--;
        return retNode.e;
    }
    @Override
    public E getFront(){
        if(isEmpty())
            throw new IllegalArgumentException("Queue is empty.");
        return head.e;
    }

    @Override
    public String toString(){
        StringBuilder res = new StringBuilder();
        res.append("Queue: front ");

        Node cur = head;
        while(cur != null) {
            res.append(cur + "->");
            cur = cur.next;
        }
        res.append("NULL tail");
        return res.toString();
    }
    public static void main(String[] args){

        LinkedListQueue<Integer> queue = new LinkedListQueue<>();
        for(int i = 0 ; i < 10 ; i ++){
            queue.enqueue(i);
            System.out.println(queue);

            if(i % 3 == 2){
                queue.dequeue();
                System.out.println(queue);
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/endlessseaofcrow/article/details/81148174
今日推荐