链表本质就是在逻辑上连续,在物理内存上不是连续存储的存储数据的方式。链表是由头指针和一个一个节点组成,每个节点会存储一个数据和指向下一个节点的地址(指针),也就是数据域和指针域。
1.单链表(带头结点)
L为头指针(所谓的头指针其实就是头节点的地址),通过这个头指针可以访问到头节点。头节点的数据域没有值,指针域用来存放下一个节点的地址。
定义一个单链表
typedef struct LNode{
int data;//定义了节点的数据域
struct LNode *next;//定义了节点的指针域
}LNode,*LinkList;
其中的LNode是struct LNode的别名,LinkList是struct LNode *的别名(其含义为单链表的指针)
初始化单链表
bool InitList(LinkList &L){
L=(LinkList)malloc(sizeof(LNode));//分配了一个头节点的空间,并把节点的地址赋给了头指针
if(L==NULL)return 0;//如果头指针为空则分配头节点空间失败
L->next=NULL;//使头节点的指针域为空,即没有下一个节点
return 1;
}
int main(){
LinkList L;//定义一个链表的指针即为头指针
InitList(L);
}
在链表中插入元素
bool ListInsert(LinkList &L,int i,int e){
LNode *p,*s;//定义两个LNode型的指针
int j;
p=L;//p指针等于头指针
j=0;//头节点为第0个节点
while(p&&j<i-1){
p=p->next;//当j等于i-1时p为第i-1个节点的地址(指针)
j++;
}
if(!p||j>i-1)return falus;//若P为空则超出链表范围
s=(LinkList)malloc(sizeof(LNode));//分配一个节点的地址
s->next=p->next;//让s的下一个地址等于p的下一个地址
s->data=e;//将传入的数据e赋值给s节点的数据域
p->next=s;//让p指向S
return true;
}
获得链表中的元素
bool GetElem(LinkList L,int i,int &e){
int j;
LNode *p;
p=L->next;//将指针p指向第一个节点(头节点为第0个节点)
j=1;
while(p&&j<i){
p=p->next;//当j等于i时p指向第i个节点
j++;
}
if(!p||j>i) return falus;//若p为空则超出范围
e=p->data;//将指针p指向的数据域赋值给e
return true;
}
删除单链表中的元素
bool DeleteList(LinkList &L,int i){
if(i<1)return 0;//判断删除位置是否合法
int j=0;//头节点为第0个节点
LinkList p=L;//将头指针赋值给p
while(j<i-1){
p=p->next;//当j等于i-1时跳出循环同时p为第i-1个元素的地址
j++;
}
if((p->next)==NULL)return 0;//判断第i个元素是否存在
LinkList q=p->next;//将第i个元素的地址赋值给指针变量q
p->next=q->next;//令第i-1的下一个元素为第i个元素的下一个元素
free(q);//释放第i个元素的空间
return 1;
}
2.单链表(不带头节点)
依然是先定义一个单链表
typedef struct LNode{
int data;
struct LNode* next;
}LNode,*LinkList;
向链表中插入一个节点
bool ListInsert(LinkList &L,int i,int e){
if(i<1)return false;//判断插入位置是否合法
if(i==1){//当要将插入的节点作为第一个节点时单独判断
LinkList s=(LinkList)malloc(sizeof(LNode));//分配一个节点的空间并把空间地址给指针s
s->data=e;//将e赋值给s指向的数据域
s->next=L;//将头指针的地址赋值给新分配的节点的指针域
L=s;//将头指针存储的地址变成新分配的节点的地址
return true;
}else{//若i!=0
int j=1;//头指针指向的节点为第一个节点
LinkList p;//定义一个指针
p=L;//将头节点的地址赋值给p
while(p!=NULL&&j<i-1){//若在循环中第j个节点的地址p为空则跳出循环
p=p->next;
j++;//循环结束时j=i-1,且p为第i-1个节点的地址
}
if(p==NULL)return false;//跳出循环后返回false
LinkList s=(LinkList)malloc(sizeof(LNode));//分配一个节点的空间把地址赋值给s指针
if(s==NULL)return false;//判断是否分配空间成功
else{
s->data=e;//将e赋值给s对应的数据域
s->next=p->next;//新节点的下一个节点为原来第i-1个节点的下一个节点
p->next=s;//将新节点的地址赋值给第i-1个节点的指针域
return true;
}
}
}
获取第i个节点的数据域的值
bool GetElem(LinkList L,int i,int &e){
if(i<1)return false;//判断获取节点的是否合法
LinkList p;//定义一个指针
p=L;//将头节点存储的地址赋值给p
int j=1;//头节点对应的为第1个节点
while(p!=NULL&&j<i){//判断所获取节点是否超出链表长度
p=p->next;
j++;//循环结束时p指针存储的为第i个节点的地址
}
if(p==NULL)return false;//若超出范围则报错
e=p->data;//将第i个节点的数据域的值赋值给e
return true;
}
删除一个节点
bool ListDelete(LinkList &L,int i){
if(i<1)return false;//判断删除是否合法
int j=1;//头指针对应第一个节点
LinkList p,q;
p=L;
if(i==1){//若删除第一个节点则单独操作
LinkList s;
s=L;//将第一个节点的地址赋值给指针s
L=L->next;//将头指针存储的地址修改为第二个节点的地址
free(s);//释放掉第一个节点的空间
return true;
}else{//若删除的不是第一个节点
while(p!=NULL&&j<i-1){//循环的过程中判断时否超出范围
p=p->next;
j++;//循环结束时j=i-1,p对应的是第i-1个节点的地址
}
if(p==NULL)return false;//若超出范围则返回错误
q=p->next;//将第i个节点的地址存储在q中
p->next=q->next;
//将第i-1个节点的指针域的值修改为第i个节点的指针域的值,也就是第i-1的下一个节点是第i+1个节点
free(q);//释放第i个节点的空间
}
return true;//返回true
}
更新一个节点的数据域
bool ListUpdata(LinkList &L,int i,int a){
LinkList p;
p=L;
int j=1;
while(j<i){
p=p->next;
j++;//跳出循环时p为第i个节点的地址
}
p->data=a;//将第i个节点的数据域的值取为a
return true;
}
主函数中的内容如下可以参考
int main(){
int e;
LinkList L;
ListInsert(L,1,5);
ListInsert(L,2,2);
ListInsert(L,3,4);
ListDelete(L,2);
GetElem(L,2,e);
printf("%d\n",e);
ListUpdata(L,1,7);
GetElem(L,1,e);
printf("%d",e);
}