链表及其算法
一,线性表
N个元素的有限序列,典型的表现为数组。
(A,B,C,D……)
典型操作:
1, InitList(&L) 构造一个空的线性表
2, DestroyList(&L) 销毁线性表
3, ClearList(&L) 将线性表重置为空表
4, ListEmpty(L) 判断线性表是否为空表
5, ListLength(L) 返回线性表的元素个数
6, GetElem(L,I,&e) 用e返回线性表中第i个元素的值
7, LocateElem(L,e,compare()) 返回线性表中第一个与e满足compare()关系的元素。若不存在,则返回0.
8, PriorElem(L,cur_e,&pre_e) 若cur_e为线性表中的元素且不是第一个,pre_e返回他的前驱,否则操作失败。
9, NextElem(L,cur_e,&next_e) 与8对应,获取后继。
10, ListInsert(&L,I,e) 在线性表i位置插入新的元素e,线性表长度加1
11, ListDelete(&L,I,&e) 与上对应,删除元素,线性表长度减1.
12, ListTraverse(L,visit()) 依次对每个元素调用visit(),visit()失败,操作失败。
线性表的顺序表示
用连续的存储单元存储线性表中的元素。
实现方法:定义线性表的存储结构
Typedef struct{
ElemType *elem;//存储空间基址
Int length;//长度
Int listsize;//当前分配的存储容量,单位为sizeof(ElemType)
}SqList;
二,线性链表
将线性表以链式存储,以任意的存储单元存储线性表的数据元素。因此,每一个数据元素需要保存其本身信息和其后继的储存位置信息。这两组信息组成的数据映像在链表中称为结点。
链表相关算法实现:
算法一 读取链表中某一元素的值
算法实现:初始化,取链表第一个结点,初始化一个计数器。顺指针向后查找到计数器位置。增加没找到的错误处理判断。
算法二 在链表中插入及删除元素
算法实现:为要操作的元素创建一个结点,找到插入位置的前后结点,修改结点中的指针。
算法三 创建一个单链表
算法实现:
链表的基本结构:
Typedef struct LNode{
ElemType data;
Struct LNode *next;
}LNode, *LinkList;
创建函数:
Void CreateList (LinkList &L,int n){ //n为结点数
L = (LinkList)malloc(sizeof(LNode)); //开辟头结点空间
L->next = NULL;//建立一个带头结点的单链表
For(i = n;I > 0;--i){
P = (LinkList)malloc(sizeof(LNode));//生成新结点
Scanf(&p -> data);//输入元素值
P ->next = L ->next;//插入到表头
L ->next =p;
}
}
算法四 合并两个有序链表为一个有序链表
类似合并线性表。
有对静态链表进行的讨论,不过静态链表适用于非指针环境,这里就不多讨论了。
三,循环链表
循环链表是另一种链式存储结构,它的特点是最后一个结点的指针指向头结点,整个链表形成一个环。这样从任意一个结点出发,均可找到其他节点。
循环链表的算法基本与线性链表一致,唯一不同的是for循环条件的判定不是p或p->next是否为空,而是它们是否等于头指针。
四,双向链表
顾名思义,双向链表与线性链表的不同就是在节点中加入了指向前驱的指针。
Code中很多结构是以链表表示的
目前发现的EFI_LIST_ENTRY
typedef struct _EFI_LIST_ENTRY {
struct _EFI_LIST_ENTRY *ForwardLink;
struct _EFI_LIST_ENTRY *BackLink;
} EFI_LIST_ENTRY;
如果一个变量定义的是EFI_LIST_ENTRY类型,那么不用想了,它就是一个双向链表。
Code中的protocol,event等等的集大多是以双向链表的形式储存。原因很简单,双向链表中查询,添加,删除元素十分的方便。熟悉了双向链表算法的操作,trace code会轻松很多。