情诗网 >表白情话 > 正文

白话数据结构-链表

来源:情诗网    2021-04-02    分类:表白情话

【开题瞎BB】

为什么我们需要不同的数据结构?为了优化你程序的时间复杂度
虽然不同的数据结构都可以实现同一个功能,但是有时候效率差异会很大,理解每一种数据结构的特点,根据具体情况选择适合的DS(data structure)才是一个聪明的小伙伴应该做的事情。

链表,英语List,是一种常见的线性数据结构,初入门接触JAVA的时候大家都是从Array,既“数组”开始学习的,根本不知道list是什么,而且在学习数据结构的时候还会被教授严令禁止直接使用java里的list数据类型。当时确实对这两个概念很模糊纠缠,其实后来发现,万物本源皆为数组。



Array

对一个Array进行四个基本操作access, search, insert, delete的话:

  1. 因为数组对象的获取是通过index的,所以只要你知道下标,对任何一个对象的获取都是一步到位。因此时间复杂度为O(1).
  2. 搜索一个对象所需时间和数组长度线性相关,因为需要从头开始遍历数组,平均时长为(n+1)/2,取大O为O(n).
  3. 向一个数组插入数据需要把插入位之后所有的对象一个个后移,这个时间也和数组长度线性相关,平均时长也是(n+1)/2,取大O为O(n).
  4. 删除一个数组数据需要把删除位之后所有的对象一个个前移,同上,时间复杂度也为O(n)


    Array

Singly Linked List

单链表也是一组有序数据,每一个结点node按照顺序串联起来,虽然Array也是某种意义有序的,但那是数学意义上的有序,因为你知道[3]是[2]后一位,但每一个元素之间并没有指针/明文指示表示它“后面”接的是谁。

单链表的结构则不一样,


singly linked list

单链表的每一个结点都有一个明确指针指向下一个结点,是靠这样的方式串联起来的,所以没有index,而且单链表没有指向前一位的指针,所以无法从当前结点往回退。

对一个单链表进行四个基本操作access, search, insert, delete的话:

  1. 因为没有下标,想要获取一个node必须从表头开始遍历,时间和长度线性相关,写作O(n)
  2. 搜索一个对象和获取一个对象基本一致了,也是从表头开始遍历,时间复杂度O(n)
  3. 插入一个节点就很简单了,你只用“剪断”一条指针,把新节点插进去,设置新节点指针指向切断处后面的节点,再把你切断的指针指向你插进去的这个节点即可。这里说“剪断”,其实就是重写,所以只用两步。不管你的链表有多长,你在哪一位置插入节点所需要的时间都是一样的,和n无关,因此时间复杂度O(1)
    【注意,本文语境下的插入和删除是指你已经停留在你要插入的位置了!如果你只有头指针,那你还当然需要先顺序遍历到你想插入的地方再执行插入操作,这种语境除非你是在头部插入(仍然是O(1)),不然其他位置的插入与删除的时间复杂显然就都是O(n)了!所以请注意语境!】
    插入节点
  4. 删除节点的步骤和插入一样,只不过反过来,时间复杂度也为O(1)

Doubly Linked List

双向列表是单链表的变体,加上了一个指向前一节点的指针。

单链表与双向链表

虽然对于基本四项操作来说时间复杂度是完全没有区别的,而且在空间占用上还更大(多了一个存放向前指针的需要),但它解决了单链表无法倒序的缺陷,在一些特定场景下很有用(其实就是在你需要倒序搜索的时候有用)。

堆栈和队列

堆栈,就是往一个细口瓶子里放核桃,一个个放进去一个个反向倒出来,压箱底的只有你倒完其他所有核桃它才能被倒出来;
队列,就是往一个双通的管子里放核桃,放一个进去下面掉一个出来,先进去的先出来,不能后退。

一般来讲这俩都是用链表实现的,甚至我们可以说链表的存在就是为了构造堆栈和队列,因此四项基本操作时间复杂度和链表完全一样,它们可以说并不是优化时间复杂度的数据结构,而是提供特定约束条件的数据结构。


【总结】

热门文章