数据结构学习笔记(一)

版权声明:转载请注明出处 https://blog.csdn.net/ty13572053785/article/details/85092064

一.数组

      数组用一块连续的内存空间,来存储形同类型的一组数据,最大的特点就是支持随机访问,但插入,删除操作也因此变得比较低效(为了保持内存数据的连续性),平均情况时间复杂度为O(n)。在平时的业务开发中,我们可以直接使用编程语言提供的容器类,但是,如果是特别底层的开发,直接使用数组可能会更合适。
ArrayList:
      1.Java ArrayList无法存储基本类型,比如int,long,需要封装为Integer,Long类,而Autoboxing,Unboxing则有一定的性能消耗。如果特别关注性能,或者希望使用基本类型,就可以选用数组。
      2.如果数据大小事先已知,并且对数据的操作非常简单,用不到ArrayList提供的大部分方法,也可以直接使用数组。
      3.ArrayList可以实现动态扩容

二.链表

      双向链表可以支持O(1)的时间复杂度找到前驱节点,正是这样的特性使得双向链表在某些情况下进行插入、删除等操作时比单链表高效。
例如:
      要删除某个节点,已经找到了要删除的节点。但是删除节点需要知道前驱节点,对于这种情况单链表需要从头再遍历一次链表,找到要删除节点的前驱节点。而双向链表就不存在这种问题,直接可以获取前驱节点。

三、链表与数组的区别

      1.数组简单易用,在实现上利用的是连续的内存空间,可以借助CPU的缓存机制,预读数组中的数据,所以访问效率更高。而链表在内存中并不是连续存储,所以对CPU缓存不友好,没办法预读。
      2.数组的缺点是大小固定,一经声明就要占用整块连续内存空间。而链表本身没有大小的限制,天然地支持扩容。
      3.如果对内存的使用非常苛刻,那数组就更加适合。因为链表每个节点都要占用空间去存储下一个节点的指针,所以内存消耗会翻倍。而且,对链表进行频繁的插入、删除操作,会导致频繁的内存申请和释放 ,容易造成内存碎片,有可能导致频繁的GC。

四、数组与CPU缓存机制

      CPU在从内存中读取数据的时候,会把读取到的数据加载到CPU缓存中。CPU每次从内存中读取数据并不是读取那个特定要访问的地址,而是读取一个数据块并保存到CPU缓存中。然后下次需要访问数据的时候会先从缓冲中查找,如果找不到就在内存中取。这样就实现了比内存访问更快的机制,也就是CPU缓冲存在的意义:为了弥补内存访问速度过慢与CPU执行速度快之间的差异而引入。对于数组来说,存储空间是连续的,所以在加载某个下标的时候可以把以后的几个元素也加载到CPU缓冲中,这样执行速度会快于存储空间不连续的链表存储。

五、基于链表实现LRU缓冲

      维护一个有序单链表,越靠近链表尾部的结点是越早之前访问的。当有一个新的数据被访问时,我们从链表头开始顺序遍历链表:
      如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。
      如果此数据没有在缓存链表中,又可以分为两种情况:

  • 如果此时缓存未满,则将此结点直接插入到链表的头部
  • 如果此时缓存已满,则链表尾节点结点删除,将新的数据结点插入链表的头部。

猜你喜欢

转载自blog.csdn.net/ty13572053785/article/details/85092064