语言——链表
链表的定义
链表是顺序表的链式表示,它由一个个结点组成(也可理解为它是由一个个结构体用指针串起来的),它包含两个域,其中存储信息的称为数据域,存储后继结点位置的域称为指针域。
typedef struct Node{
int data;
struct Node*next;
}Node,*PNode;
typedef是关键字,它的作用是类型重命名;把struct Node重命名为Node,把struct Node*重命名为PNode。类似于#define
上述结构体重命名也可写成;
typedef struct Node Node;
typedef struct Node* PNode;
相当于
#define struct Node Node
链表的图示
0x111 0x222表示该结构体自身的地址,其中数据域存储数据,指针域存储下一个结点(结构体)的地址。
链表的初始化
当创建一个变量时,我们都需对它初始化,初始化就是对每一个变量赋值;结构体,数组等也不例外。在链表中,则需对头结点(第一个结构体)初始化。
对于整个链表而言,每个结点(结构体)都需要初始化,这个函数只是对头结点进行初始化。其他结点初始化在头插或者尾插时初始化。plist为头指针(指向第一个结构体)
void Init_list(struct Node*plist)/*函数中传入参数为struct Node*plist是一个
指向Node这个结构体的指针指针名称为plist,也可写做Node*plist和PNode lsit*/
{assert(plist!=NULL);//断言传进来的指针plist不能为空
//struct Node*plist=(struct Node*)malloc(sizeof(struct Node));
plist->next=NULL;
}
链表的头插
将地址为0x431的这个结构体头插到链表中;必须先让这个结构体的指针域指向下一个结点的地址(plist->next);再将头结点的指针域指向这个新结
bool Insert_head(PNode plist,int val) {
//断言参数检测
//申请新的结点,初始化
//与其他结点连接
assert(plist != NULL);
PNode pnewnode = (PNode)malloc(sizeof(Node));
pnewnode->data = val;
pnewnode->next = plist->next;
plist->next = pnewnode;
return true;
}
链表的尾插
图示,将结构体地址为0x413的结点插入该链表中,首先应该先创建尾节点结构体为0x413的结点,并需要找到0x444这个结点,使它的指针域指向该尾插结点的地址。该尾插结点的指针域为空。
难点遍历寻找0x444这个结点。
首先把头结点赋值给指针p,
bool Insert_tail(Node*plist,int val){
assert(plist!=NULL);
Node*pnewnode=(Node*)malloc(sizeof(Node));
assert(pnewnode!=NULL);
Node*p=plist;
while(p->next!=NULL){
p=p->next;
}
p->next=pnewnode;
pnewnode->data=val;
pnewnode->next=NULL;
return true;
}
按位置插入
如图设置int变量pos为插入的位置,默认pos=0为头插,则该图表示pos=1,在第一个结点(除头结点)后面插入数据。需要先遍历该链表找到0x222指针指向的结点,将该结点的next域指向待插入的结点的地址,待插入结点的next域指向后一个结点。
bool Insert_pos(struct Node*plist,int pos,int val){
assert(plist!=NULL);
//创建待插入的结点,并初始化
struct Node*pnewnode=(struct Node*)malloc(1*sizeof(Node));
assert(pnewnode!=NULL);
pnewnode->data=val;
pnewnode->next=NULL;
struct Node*p=plist;
for(;pos<0;pos--){
p=p->next;
}
pnewnode->next=p->next;
p->next=pnewnode;\
return true;
}
链表的输出
把头指针赋值给指针p,用指针p来遍历整个链表,把数据域的信息打印出来
void Show(Node*plist){
assert(plist!=NULL);
Node*p=plist->next;
while(p!=NULL){
printf("%d ",p->data);
p=p->next;
}
链表的尾删
如图,删除最后这个结点,只需把上一个结点的指针域赋值为空。并且需要把最后这个结点malloc申请的内存删除。上一个结点需要遍历链表找
bool Del_tail(PNode plist){
assert(plist!=NULL);
PNode p=plist;
while(((p->next)->next=NULL){
p=p->next;
}
p->next=NULL;
free(p->next);
return true;
}
链表的头删
如图所示,将除头结点外的第一个删除,只需让头结点的next域指向第二个结点。
bool Del_head(PNode plist){
assert(plist!=NULL);
PNode p=plist->next;
plist->next=p->next;
free(p);
return true;
}
链表按位置删
如图,需要删除pos=1,位置的结点。一般方法为定义两个结构体指针指向待删除结构体的前面和后面的结点(需要遍历链表找到)。
bool Del_pos(Node*plist,int pos){
assert(plist!=NULL);
Node*p=plist;
for(int i=0;i<pos;i++){
p=p->next;
}//此处p为待删除结点的上一个结点的地址
struct Node*q=p->next;//q为待删除结点的地址
p->next=q->next;
free(q);
return 1;
}
链表的清空
就是将链表中的数据都清空,只剩下头结点。并释放所开辟的内存。方法是每次头删一个结点并释放它的内存。
void Destory(PNode plist){
while(plist->next!=NULL){
struct Node*p=plist->next;
plist->next=p->next;
free(p);
}
}