数据结构-链表的那些事(上)(一)

简介: 数据结构-链表的那些事

一、先来唠一唠?


   上一篇泛型适宜本意还想继续往下写写,来一下协变与逆变,奈何不知道怎么该往下写,等等我思考一下,在继续写~接下来聊聊数据结构这一话题,想到数据结构就想起了那一年我在工院上课就睡觉的时光,真实暴遣天物呀,不扯开始话题,数据结构可能在我们工作的时候用的不算太多,但是实际上不管java或者C#都将这些封装到我们常用的类里面,就比如说集合就是数据结构的真实写照~


二、数组先来扯一扯?


   数组不算是集合,但是还是想用来他的东西来说明数据结构的一些东西,不算是偏题哈~我们都知道数组的所有元素都是存储在操作系统分配的内存块中,是一块连续的存储空间,可以通过特定的元素的索引作为数组的下标,我使用一个图来表示一下数组中在内存中的存储方式,大家看了下图基本就明白了,这里要引入一个概念线性表,也是数据结构里面最简单的关系,一对一,零个或多个数据元素的有限序列,线性表分成2种存储结构一种是顺序表另外一种是链表,这里我们先说顺序表,数组也是我们顺序表的具体体现。前面我们已经提到他的概念,重点一块连续的空间排列,元素与其相邻的元素是在物理上相邻,这里我们看出当我们访问一个数组元素的话的只要知道下标那么他的访问速度是最快的,但是这里也是有缺点的,数组大小固定,另外如果做插入和删除的操作的时候每个位置还需要偏移,这样在时间开销上很大,这也是我们在使用数组的时候需要注意的问题,另外就是如果数组刚开始声明的值比较大的时候会比较占用内存空间,造成空间浪费,既然出


1005447-20170620214425366-1993139711.png现了这个问题java是如何帮我们解决的?


  在java里面有专门的API支持扩容,如下所示,这样我们只要指定合理的扩容长度那么就不会在空间上照成浪费喽,当然还有更好的,那就是集合,既然引进了动态数组的概念我们不得不谈下ArryList,俺不才某天看了遍文章,有了自己看看源码的冲动,最近也在看数据结构因此有了这篇和接下来的文章,集合也是程序中很重要的一部分,那就我们一起携手探秘集合。

 

int[] arry={1,3,4};
int[] arry2=Arrays.copyOf(arry, arry.length+5);

  看源码第一步看继承结构

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

AbstractList<E>是一个抽象的泛型类,是对List<E>接口的实现,RandomAccess这个是一个空接口,实现这个接口可以支持快速访问;具体的定义可以跳进去看下Java的解释,其实

我感觉就是单链表和顺序表之间访问元素的差距,不知道我理解的对不对,有问题希望大神给我指出来,小树的成长还需要你们,我写了一个demo比较二者的差距大家可以试着运行下,差距

还是很大,下面是我实现了大致上的对比,剩下的2个接口一个是实现浅拷贝,另外一个就是序列化;

实现了RandomAccess接口

实现了RandomAccess接口需要5秒

未实现了RandomAccess接口

未实现了RandomAccess接口需要100秒

package com.wtz.demo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.RandomAccess;
public class DemotTest{
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        List arrayList=new ArrayList();
        List linkeList=new LinkedList<>();
        for (int i = 0; i < 10000000; i++) {
            arrayList.add(i);
            linkeList.add(i);
        }
        getAllTime(arrayList);
        getAllTime(linkeList);
    }
    private static void getAllTime(List list) {
        long startTime;
        long endTime;
        if(list instanceof RandomAccess){
            System.out.println("实现了RandomAccess接口");
            startTime=System.currentTimeMillis();
            for (int i = 0; i < list.size(); i++) {
                list.get(i);
            }
            endTime=System.currentTimeMillis();
            System.out.println("实现了RandomAccess接口需要"+(endTime-startTime)+"秒");
        }else {
            System.out.println("未实现了RandomAccess接口");
            startTime=System.currentTimeMillis();
            for (Iterator iterator=list.iterator();iterator.hasNext();) {
                iterator.next();
            }
            endTime=System.currentTimeMillis();
            System.out.println("未实现了RandomAccess接口需要"+(endTime-startTime)+"秒");
        }
    }
}


接下来看看构造函数,常量那些应该结合方法看没必要先就看,但是构造函数相对比较重要的一点,这是我们调用方法的第一步所以必须认识清楚;下面是我自己的一些理解,


public ArrayList(int initialCapacity) {
        //初始化你提供的值大小的数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            //为0就为空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            //小于0就抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    public ArrayList() {
        //初始化为一个为10数组
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
   //集合转化为Object[]
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            //如果返回的不是object[]数组转化为object数组
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

接下来就是方法了主要是List接口的实现,一些什么add,size这些方法我下面的单链表就有实现,在这里我不做过多介绍,这里我就说下一些比较有意思的方法

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        if (minCapacity - elementData.length > 0)
            //扩容
            grow(minCapacity);
    }
    //这个地方就比较有意思这里涉及到JVM内存方面的东西了下面这个连接搞定我们的问题
    //https://www.ibm.com/developerworks/java/library/j-codetoheap/index.html
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        //这个也比较有意思这个地方会将老数组的位置减半,就是为了内存不浪费
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //扩容一半也不够就直接最大
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果最大超过最大值那么就溢出
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) 
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

ArrayList基本就说到这里,相信大家剩下基本都能读懂,我这个我也参考了好多才有点明白,大家努力起来哈哈~~转行还是有点小不容易,,等等基础差不都以后还是要拜读下JVM~~


三、链表来扯一扯?


  什么链表?初始化时分配一个元素的存储空间,另外就是需要有指针指向另外的元素,这里谈一下链表的好处,这里无需直接分配内存,克服了数组缺点,但是失去数组读取快的优点,另外因为有指针的存在,空间开销比较大。根据指针的类型可分为:单向链表,双向链表以及循环链表。这里是我自己的理解哈,没有人这么说,这里说一下单链表的特性,还是先用图表示什么是单链表?这下面就是一个单链表,单链表的特点就是可以包含多个节点,每个节点指向后续的元素,最后一个节点指向为NULL,接下来我们用代码表示一下

1005447-20170620223919226-328617047.png

  1.上面我们已经对单链表的一个节点进行声明接下来的我们说一说链表的操作

      1、遍历链表

      2、链表中添加一个元素

      3、链表中删除一个元素

      4、查询链表中的一个元素

   2.单链表的遍历

      1.沿着指针遍历

      2.遍历的时候显示该节点的内容

      3.直到指针为NULL的时候结束

   3.单链表的插入

       单链表的插入的情况可分为3种:

      1.在元素的开始插入一个新的节点,新节点指向原来的第一个节点

      2.在元素的中间随机插入一个节点,上一个节点指向插入节点,本节点指向插入的下一个个节点

      3.在元素的结尾插入一个节点,上一个节点的Next指向插入的这个节点,本节点指向Null

   4.单链表中删除一个元素  

       单链表的删除的情况可分为3种:

      1.在元素的开始删除

      2.在元素的中间删除,next指向下一个节点

      3.在元素的结尾删除,next指向null

     下面是主要用Java实现的主要代码

1 package com.wtz.singleLinkedList;
  2
  3 /**
  4  * Created by wangt on 2017/6/22.
  5  */
  6 public class MySingleLinkedList<T> {
  7     private Node<T> first;//第一节点
  8
  9     public MySingleLinkedList(){
 10         this.first=null;
 11     }
 12
 13     /**
 14      * 添加第一个子节点
 15      * @param data
 16      */
 17     public void addFirstNode(T data){
 18         Node<T>  node=new Node<T>(data);
 19         node.setNext(first);
 20         first=node;
 21     }
 22
 23     /**
 24      * 移除第一个节点
 25      * @return
 26      */
 27     public Node removeFirstNode(){
 28         Node<T> tempNode=first;
 29         first=tempNode.getNext();
 30         return  tempNode;
 31     }
 32
 33     /**
 34      * 增加中间的节点
 35      * @param index
 36      * @param data
 37      */
 38     public void add(int index,T data)
 39     {
 40         Node<T> node=new Node<T>(data);
 41         Node<T> current=first.getNext();
 42         Node<T> previous=first.getNext();
 43         int count=1;
 44         while (count<index){
 45             previous=current;
 46             current=current.getNext();
 47             count++;
 48         }
 49         node.setNext(current);
 50         previous.setNext(node);
 51     }
 52
 53     /**
 54      * 删除中间的节点
 55      * @param index
 56      * @return
 57      */
 58     public Node<T> remove(int index){
 59         Node<T> current=first.getNext();
 60         Node<T> previous=first.getNext();
 61         int count=1;
 62         while (count<index){
 63             count++;
 64             previous=current;
 65             current=current.getNext();
 66         }
 67         if(current==first){
 68             first=first.getNext();
 69         }else {
 70             previous.setNext(current.getNext());
 71         }
 72         return  current;
 73     }
 74
 75     /**
 76      * 展示所有的节点
 77      */
 78     public void showAllNode()
 79     {
 80         Node<T> current=first;
 81         while (current!=null){
 82             System.out.print(current.getItem());
 83             current=current.getNext();
 84         }
 85     }
 86
 87     /**
 88      * 根据索引查询节点的信息
 89      * @param index
 90      * @return
 91      */
 92     public  Node<T> findNode(int index){
 93         Node<T> current=first;
 94         int count=0;
 95         while (count<index){
 96             count++;
 97             current=current.getNext();
 98         }
 99         System.out.print(current.getItem());
100         return  current;
101     }
102 }

下面是子节点的定义


package com.wtz.singleLinkedList;
/**
 * Created by wangt on 2017/6/22.
*/
public class Node<T> {//定义一个节点
    public Node<T> getNext() {
        return next;
    }
    public void setNext(Node<T> next) {
        this.next = next;
    }
    private  Node<T> next;
    public T getItem() {
        return item;
    }
    public void setItem(T item) {
        this.item = item;
    }
    private T item;
    public  Node(T item)
    {
        this.item=item;
    }
}

下面是测试代码


package com.wtz.singleLinkedList;
import org.junit.Test;
/**
 * Created by wangt on 2017/6/22.
*/
public class TestNode {
    public static void main(String[] args){
        MySingleLinkedList<Integer> mySingleLinkedList=new MySingleLinkedList<Integer>();
        mySingleLinkedList.addFirstNode(1);
        mySingleLinkedList.addFirstNode(6);
        mySingleLinkedList.addFirstNode(5);
//        mySingleLinkedList.showAllNode();
//        mySingleLinkedList.removeFirstNode();
//        mySingleLinkedList.showAllNode();
//        mySingleLinkedList.add(2,10);
//        mySingleLinkedList.showAllNode();
//        mySingleLinkedList.remove(2);
//        mySingleLinkedList.showAllNode();
        mySingleLinkedList.findNode(2);
    }
}


四、说下心得


   上面说的一些东西侧面还是发现自己基础还是不太好,还要努力,另外还是再推下我的java群,438836709欢迎大家,,,看着反应写下一篇,我感觉写一篇还是挺累的

相关文章
|
1月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
60 4
|
2天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
29天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
55 5
|
1月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
99 4
|
1月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
1月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
50 0
|
2月前
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
33 7
|
2月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
28 3
|
2月前
|
算法 Java
数据结构与算法学习五:双链表的增、删、改、查
双链表的增、删、改、查操作及其Java实现,并通过实例演示了双向链表的优势和应用。
27 0
数据结构与算法学习五:双链表的增、删、改、查

热门文章

最新文章