一.定义
线性表是最简单也是编程中使用最多的一种数据结构。
例如英文字母表,成绩单 线性表(Linear List)是具有相同特性的数据元素的一个有限序列,是对线性结构的抽象 。
线性结构的特点是结构中的数据元素在位置上有序,并且元素之间存在一对一的线性关系。
(1)除第一个位置的数据元素外,其它数据元素位置的前面都只有一个数据元素;
(2)除最后一个位置的数据元素外,其它数据元素位置的后面都只有一个元素。 也就是说,数据元素是一个接一个的排列。
线性表就是位置有先后关系,一个接着一个排列的数据结构。
二.分类
1.顺序表存储
线性表的顺序存储结构。
用一块连续的存储空间一次存储线性表中的数据元素。
这种存储方式,好比改革前的银行,办理业务需要排队才能办理。
他们在逻辑上相邻,物理上也相邻。
C#中给我们提供的线性表有很多,我们先知讨论基础常用的 Array ArrayList List<T>
1.1数组
int[] arrInt = new int[5];
arrInt[2] = 5;
arrInt[4] = 3;
访问数据速度快,如果第一个元素位置为Loc,存储空间 为n,那么第i个元素的存储地址为: Loc+(i-1)*n
访问元素的时间复杂度为O(1)
1.2 ArrayList
数组有很多优点,但是缺点也非常明显,在实际编程中,经常需要对集合中元素进行添加和删除,也需要动态改变集合的大小,数组显然无法满足这种需求。
我们来介绍基于数组的另外一个顺序表ArrayList。
Add() //添加元素
Insert() //在指定索引出插入元素
RemoveAt() //删除指定索引的元素
TrimToSize() //裁剪空间,使存储空间正好适合元素个数
this[int index] 索引器,通过索引获取和设置元素
Count:获取当前元素的个数
Capacity:占用的存储空间
1.3 List<T>
ArrayList 使用object数组,所有会有一些装箱拆箱的消耗。 List<T> 使用了泛型,类型安全,比ArrayList 性能更优。
在实际编程中,建议使用List<T>代替ArrayList.
List<T>的使用和原理,我们这里不再多提了, 把这部分实现放在C# 讲泛型的时候, 原理跟ArrayList是一样的。
2.链表存储
链表是怎么表示两个数据元素逻辑上的相邻关系呢?
如何表示数据元素之间的线性关系呢?
在存储数据元素时,除了存储数据元素本身的信息外,还有一个指针,指向他的后继节点。
节点数据示意图:
链表示意图:
2.1单链表
单链表对于元素的插入和删除操作非常方便,不像顺序表那样需要移动元素。
但是元素的定位问题导致效率的下降,特别是单向链表数据增多的时候更为明显。
C# 只有一个类是单向链表 System.Collections.Specialized.ListDictionary 是基于键/值对(key/value)的集合。
微软建议:包含10个或者以下的集合,可以考虑使用。
优化:单链表里加入一个尾指针(tail),提高添加元素的性能。
面试 顺序表:存取数据快 链表:插入删除数据快
2.2双向链表
单向链表访问后继节点很方便,时间复杂度为O(1)。
但是访问前驱节点,必须从头指针开始遍历,找到一个节点的next为该节点,就是前驱节点。
因为单链表的只有一个指向后继节点的next,只能顺着一个方向寻找。 所以你如果想很方便的找到前驱节点,可以在加一个指向前驱节点的指针prev,使链表可以双向查找,这种数据结构成为双链表。
①双向链表的插入:双向链表的插入需要经过4次指针的操作,需要注意指针操作的顺序。如果当前节点为p,在p后面插入s节点的算法如下:
s.prev = p;
s.next = p.next;
p.next.prev = s;
p.next = s;
②双向链表删除:双向链表的删除需要经过2次指针的操作,删除s节点的算法如下:
s.prev.next = s.next;
s.next.prev = s.prev;
2.3循环链表
循环链表是另一种形式的链式存储结构。
在单向链表中,每个节点的指针都指向其下一个节点,最后一个节点的指针为空,表示链表结束。我们修改下数据结构,将链表最后一个节点的指针指向第一个节点,这样就形成了一个环,这种数据结构叫做单向循环列表.另外还有双向循环列表(略)。
循环链表结构中从表的任一节点出发均可以找到表中的其他节点 如果从表头节点出发,访问链表的最后一个节点,必须扫描表中 所有节点。若循环链表的头指针改用尾指针代替,则从尾指针出发 不仅可以立即访问到最后一个节点,而且十分方便的找到第一个节点。
三.线性表总结
1.示意图
2.线性表之顺序表
优点:
1.算法简单,描述逻辑关系不额外增加内存 2.索引访问数据性能更优
缺点:
1.空间需要手动分配,不足,溢出;反之,空间闲置而浪费。
2.动态数组解决了空间固定问题,但是空间浪费,频繁添加操作,会导致空间不断重新分配,影响性能。
3.删除和插入操作笨拙不便,常常导致数据大范围移动,影响算法速度。
3.线性表之链表
优点:
1.需要空间就申请,不需要就释放,空间有效利用。
2.删除和插入只需要修改几个指针即可,方便快捷。
3.双向链表双向搜索,简化算法的复杂度。
缺点:
1.算法实现复杂抽象,需要额外内存空间保存节点的逻辑关系。
2.只能顺序访问,随机访问性能不佳。
3.可能会把节点存放到托管堆的任意角落,垃圾回收需要更多开销。