特点
- 存储单元可不连续(链式映像)
- 结点:本身信息+指向直接后继指针
- 存储第一个数据元素结点称为首元结点
- 若首元结点前附设头结点,头指针则指向头结点;若首元结点前不设头结点,头指针则指向首元结点
头结点便于处理首元结点,也可通过L->next == NULL 判断是否为空表 - 最后一个元素没有直接后继,指针设为NULL
具体实现
定义结构体
结点定义为结构体(包含数据+指针)
typedef struct LNode{
int data;
struct LNode* next;
}LNode,*LinkList; //Linklist 为指向LNode指针,表示整个链表头指针
初始化
生成新结点作为头结点
错误方法:
void InitList(LinkList L){
L = (LNode*)malloc(sizeof(LNode));
if(L == NULL)
return;
L->next = NULL;
}
int main(){
LinkList list = NULL;
InitList(list);
if(L == NULL)
printf("!");
最终输出
!
错误原因:init函数传参仍为值传递方式,因此malloc对list的副本分配内存,导致调用init函数后,list仍为空指针,进一步造成内存泄漏
- 改进方法1:引用传参(LinkList& L)
下文代码在更改链表时均采用该方法
void InitList(LinkList& L){
L = (LNode*)malloc(sizeof(LNode));
if(L == NULL)
return;
L->next = NULL; //设置头结点指针域为空
}
- 改进方法2:传入二级指针
void InitList(LinkList* L){ //二级指针
*L = (LNode*)malloc(sizeof(LNode));
if(*L == NULL)
return;
(*L)->next = NULL; //设置头结点指针域为空
}
int main(){
LinkList L = NULL;
InitList(&L);
}
取值
从首元结点依次访问
int GetElem(LinkList L,int i){
int e;
p = L->next; //首元结点
int j = 1;
while(p && j < i){
p = p->next; //p指向下一个结点
++j;
}
if(!p || j > i)
return; //i值可能不合法
e = p->data;
return e;
}
时间复杂度为 O ( n ) O(n) O(n)
查找
从首元结点依次比对
LNode *LocateElem(LinkList L,int e){
LNode *p = L->next;
while(p && p->data != e){
p = p->next;
}
return p;
}
插入
- 生成新结点s,将s插入在第i个结点后:
p从首元结点开始
void ListInsert(LinkList& L,int i,int e){
LNode *p = L->next;
int j = 1;
while(p && j < i){
p = p->next;
j++;
}
if(!p || j > i)
return;
LNode* s = (LNode*)malloc(sizeof(LNode));;
s->data = e;
s->next = p->next;
p->next = s;
}
- 将s插入在第i个位置,即第i个结点前:
p从头结点开始
void ListInsert(LinkList& L,int i,int e){
LNode *p = L;
int j = 0;
while(p && j < i-1){
p = p->next;
j++;
}
if(!p || j > i-1)
return;
LNode* s = (LNode*)malloc(sizeof(LNode));;
s->data = e;
s->next = p->next;
p->next = s;
}
注意二者辨析
删除
注意释放被删除节点空间
void ListDelete(LinkList& L,int i){
LNode *p = L;
int j = 0;
while(p && j < i-1){
p = p->next;
j++;
}
LNode *q = p->next;
p->next = q->next;
delete q; //释放被删除节点空间
}
创建链表
- 前插法
每个元素都在头结点与首元结点间插入
void CreateList_H(LinkList L,int n){
for(int i = 0;i < n;i++){
LNode *p = (LNode*)malloc(sizeof(LNode));
p->data = i;
p->next = L->next;
L->next = p; //在L与首元结点间执行插入操作
}
}
- 后插法
void CreateList_R(LinkList& L,int n){
LNode *r = L; //定义尾指针
for(int i = 0;i < n;i++){
LNode *p = new LNode;
p->data = i;
p->next = NULL;
r->next = p;
r = p; //尾指针指向p
}
}
CHECK(补充desplay函数)
void desplay(LinkList L){
LNode *p = L->next;
while(p){
printf("%d\t",p->data);
p = p->next;
}
printf("\n");
}
int main(){
//初始化
LinkList L = NULL;
InitList(L);
//前,后插
printf("前插法:\n");
CreateList_H(L,5);
desplay(L);
/*printf("后插法:\n");
CreateList_R(L,5);
desplay(L);*/
printf("取值:\n");
printf("%d\n",GetElem(L,1));
printf("查找:\n");
LNode *p = LocateElem(L,4);
printf("%d\n",p->next->data);
printf("插入:\n");
ListInsert(L,1,100);
desplay(L);
printf("删除:\n");
ListDelete(L,1);
desplay(L);
}
输出:
前插法:
4 3 2 1 0
取值:
4
查找:
3
插入:
100 4 3 2 1 0
删除:
4 3 2 1 0