线性表
由零个或者多个数据元素组成的有限序列,每个元素最多只有一个前驱和后继。
List的抽象数据类型定义:
- ADT 线性表(List)
- Data 数据
- Operation 操作
InitList(*L)//初始化
ListEmpty(L)//判断是否为空表
//增删改查
ClearList(*L)//将线性表清空
GetElem(L,i,*e)//将线性表i位置元素返回给e
线性表的顺序存储结构
需要封装三个属性:
- 存储空间的起始位置
- 最大存储容量
- 线性表当前长度
查找算法时间复杂度为O(1)
增删改的算发时间复杂度为O(n)
线性表的链式存储结构
单链表:
第一个节点存储位置叫做头结点,最后一个结点指针为空
头指针存在于头结点,链表必须存在头指针,在每一个节点中,存储数据和指向下一个节点的指针
C语言描述:
typedef struct Node{
ElemType data;
struct Node *Next;
} Node;
建立单链表:
1、头插法建立无头节点单链表
2、尾插法建立单链表
单链表的整表删除:
从头结点开始逐个删除
查找链表第i个数据:
需要声明指向头结点的指针p,通过移动p到i位置,得到链表数据
所以时间复杂度为O(n)
增删改只需要通过指针的修改也是O(n)
但是相对于顺序结构存储,增删改操作越是频繁,单链表结构更具有优势,查找操作越多,顺序表更具有优势。
题目:快速找到未知长度单链表的中间节点
普通方法:遍历单链表确定长度L,再遍历找到L/2处节点的位置 O(3L/2)
优化:快慢指针(标尺思想)
设置两个指针,慢指针步长为1,快指针步长为2 ,当快指针到达链表尾部,慢指针就到达中间节点
O(L/2)
判断单链表是否有环
方法一:通过两个指针p,q,q以步长为1遍历到链表尾,q每移动一个位,更新q移动的距离Q,p遍历到q指针的位置,并得到p指针移动的距离P,如果P!=Q,那么单链表有环。
方法二:通过快慢指针,q每次走一步,p每次走两步,p每走一步都进行判断,如果p==q了,那么就意味着有环。
静态链表
利用数组代替指针建立游标,存放下一节点数据的位置
通常数据数组的第一个和最后一个元素不存放数据,未使用的数据数组称之为备用链表。
数据数组已存放数据的最后一个元素的游标为0
游标第一个元素存放备用链表的第一个节点下标
游标最后一个元素存放第一个有数值元素的下标,相当于单链表的头结点。
其思想极为巧妙
循环链表
将单链表的尾部节点的由空指针指向头结点,循环链表和单链表的主要区别是空表的判断,单链表判断头指针下一元素是不是等于null,循环链表则是判断是不是等于头指针。
问题: 用循环链表模拟约瑟夫问题。
双向链表
将单链表节点再增加一个前驱节点,就形成了双向链表。
C语言描述:
typedef struct DualNode{
ElemType data;
struct DualNode *prior;
struct DualNode *next;
}
双向链表即时牺牲空间换取时间。