数据结构——双向链表循环链表

Posted 龍弟-idea

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构——双向链表循环链表相关的知识,希望对你有一定的参考价值。

目录

双链表的实现                                                                                                     

java中LinkedList实现

链表的复杂度分析

循环链表


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

双链表的实现                                                                                                     

                                                                                                          结点API设计

类名Node
构造方法Node(T t,Node pre,Node next)
成员变量

T item :存储数据

Node pre:指向上一个结点

Node next:指向下一个结点

                                                                                                        双向链表API设计

类名LoopLinkList
构造方法LoopLinkList():创建对象
成员方法

1.public void clear():清空线性表

2.public boolean isEmpty():判断线性表是否为空

3.public int length():获取线性表中元素的个数

4.public  T get(int i)获取第i个元素的值

5.public void insert(T t):在线性表中添加一个元素

6.public void insert(int i,T t):在第i个元素之前插入一个值为t的元素

7.public T remove(int i):删除第i个数据元素

8.public int indexOf(T t):返回线性表中首次出现该元素的索引值
9.public T getFirst():获取第一个元素

10.public T getLast():获取最后一个元素

成员内部类private class Node:结点类
成员变量

1.private Node first:记录首结点

2.private Node last:记录尾结点

3.private int N:记录链表的长度

public class LoopLinkList<T> implements Iterable<T>
    public static void main(String[] args) 
        // 创建双向链表对象
        LoopLinkList<String> sl= new LoopLinkList<>();
        // 测试插入
        sl.insert("龍弟");
        sl.insert("龍龍");
        sl.insert("龍哥");
        sl.insert(1,"龍帝");
        for(String s: sl)
            System.out.println(s);
        
        System.out.println("-----------------");

        // 测试获取
        String getResult = sl.get(1);
        System.out.println("获取索引1处的结果为:"+getResult);
        // 测试删除
        String removeResult = sl.remove(0);
        System.out.println("删除的元素:" + removeResult);



        System.out.println("---------------------");
        System.out.println("第一个元素是"+sl.getFirst());
        System.out.println("最后一个元素是:" + sl.getLast());

        // 测试清空
        sl.clear();
        System.out.println("清空后的线性表中的元素个数为:"+sl.length());
    

    //首结点
    private Node head;
    // 最后一个结点
    private Node last;

    //链表的长度
    private int N;

    // 结点类
    private class Node 
        public Node(T item, Node pre, Node next) 
            this.item = item;
            this.pre = pre;
            this.next = next;
        
        //存储数据
        public T item;
        // 指向上一个结点
        public Node pre;
        //  指向下一个结点
        public Node next;
    

    public LoopLinkList() 
        //初始化头节点和尾结点
        this.head = new Node(null, null, null);
        this.last = null;
        // 初始化元素结点
        this.N = 0;
    

    // 清空链表
    public void clear() 
        this.head.next = null;
        this.head.item = 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;
        
        //当前结点
        Node curr = pre.next;
        //构建新节点
        Node newNode = new Node(t, pre, curr);
        pre.next = newNode;
        curr.pre = newNode;
        //长度+1
        N++;
    
    // 获取指定位i处的元素
    public T get(int i)
        Node n = head.next;
        for(int index = 0; index < i; index++)
            n = n.next;
        
        return n.item;
    

    // 找到元素t在链表中第一次出现的位置
    public int indexOf(T t)
        Node n = head;
        for(int index = 0; index<N; index++)
            n = n.next;
            if(n.next.equals(t))
                return index;
            
        
        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;
        pre.next = nextNode;
        nextNode.pre = pre;
        // 长度减一
        N--;
        return curr.item;
    

    @Override
    public Iterator<T> iterator() 
        return new TIterator();
    
    private class TIterator implements Iterator
        private Node n;
        public TIterator()
            this.n = head;
        

        @Override
        public boolean hasNext() 
            return n.next != null;
        

        @Override
        public Object next() 
            n = n.next;
            return n.item;
        
    



java中LinkedList实现

java中LinkedList集合也是使用双向链表实现,并提供了增删改查等相关方法

1.底层是否用双向链表实现;
2.结点类是否有三个域

链表的复杂度分析

get(int i):每一次查询,都需要从链表的头部开始,依次向后查找,随着数据元素N的增多,比较的元素越多,时间复杂度为O(n)
insert(int i,T t);每一次插入,需要先找到i位置的前一个元素,然后完成插入操作,随着数据元素N的增多,查找的元素越多,时间复杂度为O(n);
remove(int i):每一次移除,需要先找到i位置的前一个元素,然后完成插入操作,随着数据元素N的增多,查找的元素越多,时间复杂度为O(n)
相比较顺序表,链表插入和删除的时间复杂度虽然一样,但仍然有很大的优势,因为链表的物理地址是不连续的,它不需要预先指定存储空间大小,或者在存储过程中涉及到扩容等操作..同时它并没有涉及的元素的交换。
相比较顺序表,链表的查询操作性能会比较低。因此,如果我们的程序中查询操作比较多,建议使用顺序表;增删操作比较多,建议使用链表。

循环链表

循环链表,也就是链表整体要形成一个圆环状。在单向链表中,最后一个节点的指针为null ,不指向任何结点,因为没有下一个元素了。要实现循环链表,我们只需要让单向链表的最后一个节点的指针指向头结点即可。

 

    public static void main(String[] args) 
        //构建结点
        Node<Integer> first=new Node<Integer>(5,null);
        Node<Integer> second=new Node<Integer>(6,null);
        Node<Integer> third=new Node<Integer>(7,null);
        Node<Integer> fourth=new Node<Integer>(7,null);
        Node<Integer> fifth=new Node<Integer>(7,null);
        //生成单链表
        first.next=second;
        second.next=third;
        third.next=fourth;
        fourth.next=fifth;
        //构成循环链表,让最后一个结点指向第一个结点
        fifth.next=first;
    

以上是关于数据结构——双向链表循环链表的主要内容,如果未能解决你的问题,请参考以下文章

静态链表循环链表双向链表

数据结构05——静态链表循环链表双向链表

数据结构与算法笔记—— 链表(单链表循环链表双向链表)

[读书笔记]-大话数据结构-3-线性表-静态链表循环链表和双向链表

单链表循环链表双向链表的比较

《图解数据结构与算法》(Java代码实现注释解析算法分析)