线性结构是程序设计当中的一种基本的数据存储结构,在线性结构中,存在唯一的起始元素和结尾元素,除了起始元素以外,每个元素都有唯一的“前驱”,除了结尾元素以外,每个元素都有唯一的“后继”,所有元素从头到尾排成一行,而非线性结构中的元素可能存在多个前驱或后继,或者不存在前驱和后继。
数组和链表都属于线性结构,但它们的内存布局和使用场景却不尽相同。
- 数组(array)
数组中所有元素是按照先后次序依次紧密排列在一块连续的内存当中(假定数组中的每个元素都是同一数据类型,也就是说每个元素占用的内存空间相等),我们只需要保存数组的第一个元素在内存中的地址,任意一个元素所在的内存地址都可以在常数时间内计算得出,因此数组是一种随机存取结构,读写元素的操作具有O(1)的时间复杂度。
由于数组中的元素紧密排列,因此在数组中插入元素或删除元素比较困难,在数组中插入元素需要将插入位置之后的元素依次向后挪动一个位置,然后再将新元素放到空出的位置上,删除操作也是类似,需要将删除元素之后的所有元素都向前挪动一位来填补删掉的元素留下的空位。可以看出,插入删除的位置越靠前,则需要挪动的元素数量越多,操作的时间复杂度达到了O(N)。
![745338d39e85b738fbcc971b87168524.gif](https://i-blog.csdnimg.cn/blog_migrate/c5733ce75548d1c3f643f7deb54f0596.gif)
数组插入操作演示
数组是非常基本的数据结构,因此绝大多数编程语言都内置了数组类型。
- 链表(linked list)
链表中的所有元素并非按照顺序连续存储,它们可以分别存储在内存的任意位置。为了能够维护元素的先后次序,每个元素需要额外的指针来保存它的后继所在的内存地址(有时也需要保存它的前驱)。只存后继的链表我们称之为单链表,同时保存前驱和后继的链表我们称之为双向链表。在实际应用中,我们只保存链表的第一个元素。如果我们需要访问第N个元素,我们需要从第一个元素开始,每次通过指向后继的指针找到后一个元素,这样重复N-1次。所以说,链表不具有随机存取的特点,访问的元素离第一个元素越远,花费的时间越多。
由于链表通过指针来连接各个相邻的元素,因此插入删除很容易,例如我们想在相邻的两个节点A和B之间插入元素C,则只需要将A的后继指向C,让C的后继指向B即可。删除也是类似。因此对于链表来说,插入和删除的操作具有O(1)的时间复杂度。
![98d060f4a12ab5d492dba6d3b647badc.png](https://i-blog.csdnimg.cn/blog_migrate/7dc275959ae59e1bbde699f131d0dd41.jpeg)
链表插入操作
在编写程序的过程中,我们需要根据数据的使用方法来选择使用数组还是链表。当线性结构中的数据被频繁读写,但是较少插入删除的情况下,我们应当使用数组,如果插入删除操作频繁,则可以考虑使用链表。