链表

      链表是一种物理存储单元上非连续非顺序的存储结构,存储元素的逻辑顺序是通过链表中的指针来链接次序实现的。

单向链表

      单向链表是链表的一种,由多个结点构成,每个结点由一个数据域和一个指针域组成,数据域用来存储数据,指针域用来指向其后继结点。链表的头节点数据域不存储数据,指针域指向第一个真正存储数据的结点。

1.单向链表的API设计

2.单向链表的代码实现

public class LinkList<T>{
    //记录头结点
    private Node head;
    //记录链表长度
    private int N;

    private class Node{
        //存储数据
        T item;
        //下一个结点
        Node next;

        public Node(T item,Node next){
            this.item = item;
            this.next = next;
        }
    }
    public LinkList() {
        this.head = new Node(null,null);
        this.N = 0;
    }
    //清空链表
    public void clear(){
        head.next = null;
        this.N = 0;
    }
    //获取链表的长度
    public int length(){
        return N;
    }
    //判断链表是否为空
    public boolean isEmpty(){
        return N==0;
    }
    //获取指定位置i处的元素
    public T get(int i){
        Node n = head.next;
        for (int index = 0;index<i;index++){
            n = n.next;
        }
        return n.item;
    }
    //向链表中添加元素
    public void insert(T t){
        //找到当前的最后一个结点
        Node n = head;
        while (n.next!=null){
            n = n.next;
        }
        //创建新结点
        Node newnode = new Node(t, null);
        //让当前最后一个结点指向新结点
        n.next = newnode;
        N++;
    }
    //向指定位置i处添加元素
    public void insert(int i,T t){
        //找到i位置的前一个结点
        Node pre = head;
        for (int index = 0;index<=i-1;index++){
            pre = pre.next;
        }
        //找到i位置的结点
        Node current = pre.next;
        //创建一个新结点,并且新结点指向原来i位置的结点
        Node newNode = new Node(t, current);
        //让原来i位置的前一个结点指向新结点
        pre.next = newNode;
        N++;
    }
    //删除指定位置i处的元素,并返回被删除的元素
    public T remove(int i){
        //找到i位置的前一个结点
        Node pre = head;
        for (int index = 0;index<=i-1;index++){
            pre = pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //找到i位置的后一个结点
        Node nextNode = curr.next;
        //让i位置的前一个结点指向i位置的后一个结点
        pre.next = nextNode;
        N--;
        return curr.item;

    }
    //查找元素T在链表中第一次出现的位置
    public int indexOf(T t){
        //从头结点依次找到每一个结点比较item和t的值,如果相同则找到了,结束循环
        Node n =head;
        for (int i = 0;n.next!=null;i++){
            n = n.next;
            if (n.item.equals(t)){
                return i;
            }
        }
        return -1;
    }
}

双向链表


      双向链表是链表的一种,由多个结点构成,每个结点都有一个数据域和两个指针域组成,数据域用来存储数据,其中一个指针域用来指向后继结点,另一个指针域用来指向前驱结点,链表的头结点不存储数据,指向前驱结点的指针域值为空,指向后继结点的指针域指向第一个真正存储数据的结点。

1.双向链表的API设计

2.双向链表的代码实现

public class TowWayLinkLIst<T> {
    //首结点
    private Node head;
    //尾结点
    private Node last;

    private int N;

    private class Node{
        public T item;
        public Node pre;
        public Node next;
        public Node(T item,Node pre,Node next){
            this.item = item;
            this.pre = pre;
            this.next = next;
        }
    }

    public TowWayLinkLIst(){
        this.head = new Node(null,null,null);
        this.last = null;
        this.N = 0;
    }
    //清空链表
    public void clear(){
        this.head.next = null;
        this.last =  null;
        this.N = 0;
    }
    //获取链表长度
    public int length(){
        return N;
    }
    //判断链表是否为空
    public boolean isEmpty(){
        return N==0;
    }
    //获取第一个元素
    public T getFirst(){
        if (isEmpty()){
            return null;
        }
        return head.next.item;
    }
    //获取最后一个元素
    public T getLast(){
        if (isEmpty()){
            return null;
        }
        return last.item;
    }
    //插入元素t
    public void insert(T t){
        if (isEmpty()){
            //创建新的结点
            Node newNode = new Node(t, head, null);
            // 让新结点成为尾结点
            last = newNode;
            //让头结点指向尾结点
            head.next = last;
        }else {
            Node oldLast = last;
            //创建新结点
            Node newNode = new Node(t, oldLast, null);
            //让当前的尾结点指向新结点
            oldLast.next = newNode;
            //让新结点成为尾结点
            last = newNode;
        }
        N++;
    }
    //向指定位置i插入元素t
    public void insert(int i,T t){
        //找到i位置的前一个结点
        Node pre = head;
        for (int index = 0;index<i;index++){
            pre = pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //创建新结点
        Node newNode = new Node(t, pre, curr);
        //让i位置的前一个结点的下一个结点变为新结点
        pre.next = newNode;
        //让i位的的后一个结点的前一个结点变为新结点
        curr.pre = newNode;
        N++;
    }
    //获取指定位置i处的元素
    public T get(int i){
        Node n = head.next;
        for (int index = 0;index<i;i++){
            n = n.next;
        }
        return n.item;
    }
    //找到元素t在链表中第一次出现的位置
    public int indexOf(T t){
        Node n =head;
        for (int i = 0;n.next!=null;i++){
            n = n.next;
            if (n.item.equals(t)){
                return i;
            }
        }
        return -1;
    }
    //删除位置i处的元素,并返回该元素
    public T remove(int i){
        //找到i位置的前一个结点
        Node pre = head;
        for (int index= 0;index<i;index++){
            pre = pre.next;
        }
        //找到i位置的结点
        Node curr = pre.next;
        //找到i位置的下一个结点
        Node nextNode = curr.next;
        //让i位置的前一个结点的下一个结点变为i位置的下一个结点
        pre.next = nextNode;
        //让i位置的下一个结点的前一个结点变为i位置的前一个结点
        nextNode.pre = pre;
        N--;
        return curr.item;
    }
}

链表的时间复杂度分析

  • 每一次查询都需要从链表的头部开始依次向后查找,随着元素的增多,比较的元素也越来越多,时间复杂度为O(n)
  • 每一次插入,需要找到前一个位置的元素,随着元素的增多,查找的元素增多,时间复杂度为O(n)
  • 每一次删除,需要找到前一个位置的元素,随着元素的增多,查找的元素增多,时间复杂度为O(n)
  • 相比于顺序表,链表的插入和删除虽然和顺序表相同,但是在存储过程中不涉及扩容的操作,所以链表的插入和删除的效率更高
  • 相比于顺序表,链表的查询操作效率较低。如果以后我们的程序中查询操作较多建议使用顺序表,增删操作较多,使用链表。
posted @ 2020-07-21 18:17  zuzuzu_code  阅读(171)  评论(0)    收藏  举报