数据结构-链表的那些事(下)(二)

简介: 双链表的优点:对于链表中一个节点来说,都可以从两个方向操作。当然这个问题自然就带了另外一个缺点,因为前节点的存在导致更多的内存开销,另外在插入和删除的时候会更加的麻烦,需要更多时间,所以说这个世界就是这样有利就有弊

一、谈一下博客这一问题?


     博客这个我相信已经成为大家了解和学习技术的一个手段,但是真真正正有多少人去按照这个操作了,我看到一条1000+人看的博客,我发现一个简单的小错误,但是我之前的1000多人难道没发现嘛?这个问题值得我们深思?我不是指着作者问题,因为写在作者,看在你,我们无权干涉,当然写就会出现错误,毕竟程序还有BUG,我只是想说有些时候不是只要看一遍就可以我们需要是真实操作,毕竟好记性不然烂笔头,这个问题就讨论学习这个深刻的话题上来了,就远离我们的主题了,我只是将这个问题抛出啦,引发下大家的深思,具体该怎么做,就是大家的事,因为毕竟每个人的特色都不同,当然就有自己的社会主义道路,一切按照自己的来就好,回归正题,不知道为啥我上一篇被移除去了,但是我的群里还是收获了2个小伙伴,这就是我写下的动力。


二、谈一下双链表


     双链表的优点:对于链表中一个节点来说,都可以从两个方向操作。当然这个问题自然就带了另外一个缺点,因为前节点的存在导致更多的内存开销,另外在插入和删除的时候会更加的麻烦,需要更多时间,所以说这个世界就是这样有利就有弊,接下来我们看一下双链表的结构,用张图表示:

1005447-20170630144702336-1934203092.png

 

   头部节点首部为空,尾部节点的下一个节点指向为空,中间节点上尾指下头,下头指上尾;

   1)增加一个节点,有2种情况在节点之前和节点之后;

   2)增加节点的有3种情况首节点,尾节点和中间节点;

   3)删除同增加一样;

   以下是Java实现的双链表和测试代码,大家可以看一下

   这个是定义的节点

package com.wtz.dataStruct;
/**
 * Created by wangt on 2017/6/29.
 * 定义一个双链表的节点
 */
public class Node<T> {
    public T getItem() {
        return item;
    }
    public void setItem(T item) {
        this.item = item;
    }
    public Node<T> getPervious() {
        return pervious;
    }
    public void setPervious(Node<T> pervious) {
        this.pervious = pervious;
    }
    public Node<T> getNext() {
        return next;
    }
    public void setNext(Node<T> next) {
        this.next = next;
    }
    private T item;
    private Node<T> pervious;
    private Node<T> next;
    public Node(){
    }
    public Node(T item){
        this.item=item;
    }
}

 这个是具体实现

package com.wtz.dataStruct;
/**
 * Created by wangt on 2017/6/29.
 */
public class DoubleLinkList<T> {
    private Node<T> first;//头节点
    private int count;//总节点的个数
    public  DoubleLinkList()
    {
        //初始化为空的链表
        this.count=0;
        this.first=null;
    }
    //返回节点的个数
    public  int size(){
        return  count;
    }
    //根据索引获取节点
    public Node<T> getNodeIndex(int index){
        if(index<0&&index>=count){
            throw new IndexOutOfBoundsException("超出索引");
        }
        Node<T> tempNode=this.first;
        for (int i=0;i<index;i++){
            tempNode=tempNode.getNext();
        }
        return  tempNode;
    }
    //在未节点之后插入节点
    public void addAfter(T value){
        Node<T> node=new Node<T>(value);
        if(this.first==null){
            this.first=node;
        }else {
            Node<T> lastNode=getNodeIndex(count);
            lastNode.setNext(node);
            node.setPervious(lastNode);
            count++;
        }
    }
    //末节点之前插入
    public void addBefore(T value){
        Node<T> node=new Node<T>(value);
        if (this.first==null){
            this.first=node;
        }else {
            //获取最后一个节点和倒数第二个节点
            Node<T> lastNode=getNodeIndex(count);
            Node<T> lastTwoNode=lastNode.getPervious();
            //将倒数第二个节点的下一个节点设置为插入节点
            lastTwoNode.setNext(node);
            node.setPervious(lastTwoNode);
            //将最后一个节点上一个节点指向插入的节点
            lastNode.setPervious(node);
            node.setNext(lastNode);
            count++;
        }
    }
    //指定位置之前插入
    public void addIndexBefore(T value,int index){
        Node<T> node=new Node<T>(value);
        if(index==0){
            if(this.first==null){
                this.first=node;
            }else {
                node.setNext(first);
                first.setPervious(node);
                first=node;
                count++;
            }
        }else {
            Node<T> nextNode=getNodeIndex(index);
            Node<T> afterNode=nextNode.getPervious();
            afterNode.setNext(node);
            node.setPervious(afterNode);
            node.setNext(nextNode);
            nextNode.setPervious(node);
            count++;
        }
    }
    //指定位置之后插入
    public void addIndexAfter(T value,int index){
        Node<T> node=new Node<T>(value);
        if (index==0){
            if (first==null){
                first=node;
            }else {
                node.setPervious(first);
                node.setNext(first.getNext());
                first.setNext(node);
                count++;
            }
        }else {
            Node<T> afterNode=getNodeIndex(index);
            Node<T> nextNode=afterNode.getNext();
            afterNode.setNext(node);
            node.setPervious(afterNode);
            node.setNext(nextNode);
            nextNode.setPervious(node);
            count++;
        }
    }
    //删除指定位置
    public void  removeIndex(int index){
        if(index<0&&index>=count){
            throw new  IndexOutOfBoundsException("超出范围");
        }
        if(index==0){
            first=first.getNext();
        }else {
            Node<T> beforeNode=getNodeIndex(index-1);
            Node<T> deleteNode=beforeNode.getNext();
            Node<T> nextNode=deleteNode.getNext();
            beforeNode.setNext(nextNode);
            nextNode.setPervious(beforeNode);
        }
        count--;
    }
}

这个是测试代码

package com.wtz.dataStruct;
/**
 * Created by wangt on 2017/6/30.
 */
public class TestDemo {
    public static void main(String[] args) {
        DoubleLinkList<Integer> doubleLinkList=new DoubleLinkList<Integer>();
        //最后一个节点之后
        doubleLinkList.addAfter(2);
        doubleLinkList.addAfter(5);
        doubleLinkList.addAfter(10);
        for (int i=0;i<=doubleLinkList.size();i++){
            System.out.print(doubleLinkList.getNodeIndex(i).getItem());
        }
        System.out.print("-------------------------------------------------");
        doubleLinkList.addBefore(10);
        doubleLinkList.addBefore(13);
        doubleLinkList.addBefore(18);
        doubleLinkList.addBefore(17);
        doubleLinkList.addBefore(16);
        for (int i=0;i<=doubleLinkList.size();i++){
            System.out.print(doubleLinkList.getNodeIndex(i).getItem());
        }
        System.out.print("-------------------------------------------------");
        doubleLinkList.removeIndex(2);
        for (int i=0;i<=doubleLinkList.size();i++){
            System.out.print(doubleLinkList.getNodeIndex(i).getItem());
        }
        System.out.print("-------------------------------------------------");
        doubleLinkList.addIndexAfter(111,0);
        for (int i=0;i<=doubleLinkList.size();i++){
            System.out.print(doubleLinkList.getNodeIndex(i).getItem());
        }
        System.out.print("-------------------------------------------------");
        doubleLinkList.addIndexBefore(444,0);
        for (int i=0;i<=doubleLinkList.size();i++){
            System.out.print(doubleLinkList.getNodeIndex(i).getItem());
        }
    }
}


二、循环链表


   循环链表我们这里用单向循环列表来描述这一问题,来述说下循环链表与链表的差距,循环的意思就是从从头到尾,从尾到头连起来,我们已经知道单链表的概念,那么二者差距就很明显了,单向循环链表就是之前的尾部的空节点指向首部节点这。还是用图表示下单向循环链表:

1005447-20170630150103836-1152628613.png

套路还是和之前一样用代码实现单向循环链表,这里需要提醒下注意下循环的时候,如果不消息很容易照成死循环,大家在使用循环链表的时候需要很注意这一点。另外我们当查找最后一个节点的时候就没有必要遍历所有节点,因为尾部的指针的下一个节点是首部,这样就可以将第一个和最后一个节点控制到O(1)访问时间。下面就是代码的实现。

public class CirNode<T> {
    public T getItem() {
        return item;
    }
    public void setItem(T item) {
        this.item = item;
    }
    public CirNode<T> getNext() {
        return next;
    }
    public void setNext(CirNode<T> next) {
        this.next = next;
    }
    private T item;
    private CirNode<T> next;
    public CirNode(T item){
        this.item=item;
    }
}
package com.wtz.dataStruct;
import java.util.Currency;
/**
 * Created by wangt on 2017/6/30.
 */
public class SingleCircularList<T> {
    private int count;
    private CirNode<T> pointer;//尾部节点的指针这里为什么不用节点大家思考一下哈哈
    public SingleCircularList()
    {
        this.count=0;
        this.pointer=null;
    }
    //返回节点的个数
    public  int size()
    {
        return  count+1;
    }
    //获取当前的节点
    public CirNode<T> getIndexNode(int index)
    {
        if(index<0&&index>count){
            throw  new  IndexOutOfBoundsException("超过索引范围");
        }
        CirNode<T> node=pointer;
        for (int i=0;i<index;i++){
            node=node.getNext();
        }
        return  node;
    }
    //当前节点之前插入
    public void addBefore(T value)
    {
        CirNode<T> cirNode=new CirNode<T>(value);
        if(pointer==null){
            pointer=cirNode;
            pointer.setNext(cirNode);
        }else {
            cirNode.setNext(pointer.getNext());
            pointer=cirNode;
            count++;
        }
    }
    //当前节点之后插入
    public void addAfter(T value)
    {
        CirNode<T> cirNode=new CirNode<T>(value);
        if(pointer==null){
            pointer=cirNode;
            pointer.setNext(cirNode);
        }else {
            cirNode.setNext(pointer.getNext().getNext());
            pointer.getNext().setNext(cirNode);
            count++;
        }
    }
    //在某节点之前插入
    public  void addIndexBefore(T value,int index)
    {
        CirNode<T> cirNode=new CirNode<T>(value);
        if (index==0){
            if (pointer==null){
                pointer=cirNode;
                pointer.setNext(cirNode);
            }else {
                pointer=cirNode;
                cirNode.setNext(pointer.getNext());
                count++;
            }
        }else {
            CirNode<T> nextNode=getIndexNode(index-1);
            CirNode<T> afterNode=getIndexNode(index-2);
            afterNode.setNext(cirNode);
            cirNode.setNext(nextNode);
            count++;
         }
    }
    //在某节点之后插入
    public  void addIndexAfter(T value,int index){
        CirNode<T> node=new CirNode<T>(value);
        if (index==0){
            if (pointer==null){
                pointer=node;
                pointer.setNext(node);
            }else {
                node.setNext(pointer.getNext().getNext());
                pointer.getNext().setNext(node);
                count++;
            }
        }else {
            CirNode<T> currentNode=getIndexNode(index-1);
            node.setNext(currentNode.getNext());
            currentNode.setNext(node);
            count++;
        }
    }
    public  void deleteIndex(int index){
        if(index>0&&index>count){
            throw  new  IndexOutOfBoundsException("超过索引范围");
        }
        if (index==0){
            pointer=pointer.getNext().getNext();
        }else {
            CirNode<T> beforeNode=getIndexNode(index-2);
            CirNode<T> deleteNode=beforeNode.getNext();
            CirNode<T> nextNode=deleteNode.getNext();
            beforeNode.setNext(nextNode);
            deleteNode=null;
        }
        count--;
    }
}
package com.wtz.dataStruct;
/**
 * Created by wangt on 2017/7/4.
 */
public class TestCirTest {
    public static void main(String[] args) {
        SingleCircularList<Integer> singleCircularList = new SingleCircularList<Integer>();
        singleCircularList.addBefore(5);
        singleCircularList.addBefore(2);
        for (int i=0;i<singleCircularList.size();i++){
            System.out.print(singleCircularList.getIndexNode(i).getItem());
        }
        System.out.print("-------------------------------------------------");
        singleCircularList.addAfter(9);
        singleCircularList.addAfter(6);
        for (int i=0;i<singleCircularList.size();i++){
            System.out.print(singleCircularList.getIndexNode(i).getItem());
        }
        System.out.print("-------------------------------------------------");
        singleCircularList.addIndexAfter(3,2);
        for (int i=0;i<singleCircularList.size();i++){
            System.out.print(singleCircularList.getIndexNode(i).getItem());
        }
        System.out.print("-------------------------------------------------");
        singleCircularList.addIndexBefore(4,2);
        for (int i=0;i<singleCircularList.size();i++){
            System.out.print(singleCircularList.getIndexNode(i).getItem());
        }
        System.out.print("-------------------------------------------------");
        singleCircularList.deleteIndex(2);
        for (int i=0;i<singleCircularList.size();i++){
            System.out.print(singleCircularList.getIndexNode(i).getItem());
        }
        System.out.print("-------------------------------------------------");
    }
}

三、LinkedList源码解读


   老套路先看继承结构,

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

 AbstractSequentialList这个实现了get,set,add,remove等这些方法,都可以随机访问集合,这些操作允许将链接列表用作堆栈、队列或双端队列。接下来说下List接口这个接口与ArrayList一样实现List接口,只是ArrayList是List接口的大小可变数组的实现,LinkedList是List接口链表的实现。这样的结构使得插入和删除会优于ArryList,但是在读取的时候会比List差,这个在上一篇那个对比的时候相信大家可以看出来。Deque这个接口相当于是对这个扩展,提供先进先出队列操作,以及其他堆栈和双端队列操作。

 接下来看构造函数:一个空参构造,一个是集合,这里会调用addAll的方法添加到列表中;

public LinkedList() {
    }
    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }

接下来我们看一下内部类:


private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

  这里我们很快就想起我们那个双链表结构了,不多说。

  接下来我们看一下属性:

transient int size = 0;
    //元素的个数
    transient Node<E> first;
    //头节点
    transient Node<E> last;
    //尾节点

接下来的API接口是没有什么好说的都比较简单,只要能看懂我上面双链表的应该看这个不是问题,这里不做过多说明。


四、结束语


   最近在搞一些基本Liunx命令和SSH,感觉进度优点缓慢,转还是有点费劲,不过集合写完还会有多线程,这两块还是必须搞定的,还是推荐下QQ群,上次有2个小伙伴加入了,动力呀438836709


相关文章
|
27天前
|
存储 缓存 算法
数据结构-链表(一)
链表(Linked List)是一种常见的数据结构,用于存储和组织数据。与数组不同,链表的元素(节点)在内存中不必连续存储,而是通过指针链接在一起。 链表由多个节点组成,每个节点包含两部分:数据(存储实际的元素值)和指针(指向下一个节点的引用)。链表的第一个节点称为头节点,最后一个节点称为尾节点,尾节点的指针通常指向空值(null)。
31 1
|
29天前
|
存储 C++
数据结构第六弹---带头双向循环链表
数据结构第六弹---带头双向循环链表
|
1月前
|
存储
【单链表】数据结构单链表的实现
【单链表】数据结构单链表的实现
|
1月前
|
C++
从0开始回顾数据结构---链表与堆
#include <iostream> #include <algorithm> #include <string.h> using namespace std; const int N = 100010; int h[N], ph[N], hp[N], cnt; void heap_swap(int a, int b) { swap(ph[hp[a]],ph[hp[b]]); swap(hp[a], hp[b]); swap(h[a], h[b]); } void down(int u) { int t = u; if (u * 2 <= cnt &&
|
1月前
|
存储
【数据结构】双向带头循环链表的实现
【数据结构】双向带头循环链表的实现
数据结构—链表(超详细)(山东大学)(数据结构实验三)
数据结构—链表(超详细)(山东大学)(数据结构实验三)
数据结构|双向链表|带头结点|头插|尾插|尾删|头删
数据结构|双向链表|带头结点|头插|尾插|尾删|头删
|
12天前
数据结构--链表刷题(一)快慢指针(上)
数据结构--链表刷题(一)快慢指针
16 0
|
21天前
|
缓存 算法 搜索推荐
【数据结构】链表(单链表与双链表实现+原理+源码)
【数据结构】链表(单链表与双链表实现+原理+源码)
|
25天前
|
存储 编译器 C语言
【数据结构】深入浅出理解链表中二级指针的应用
【数据结构】深入浅出理解链表中二级指针的应用
27 0