1.结点定义
//单链表结点
typedef struct LNode{
int data;//存放数据
struct LNode *next;//指向后继续结点的指针
}LNode;
//双链表结点
typedef struct DLNode{
int data;
struct DLNode *prior;//前驱结点
struct DLNode *next;//后继结点
}DLNode;
LNode*A=(LNode*)malloc(sizeof(LNode));
关于LNode* A=(LNode*)malloc(sizeof(LNode));的解读
malloc函数分配sizeof(LNode)大小的内存,但由于malloc内存分配成功则是返回void * ,需要通过LNode* 强制类型转换将void*指针转换成需要的类型,指针A来指向(接收)这个分配的内存。
这时这个A有2个含义,一个是指向该结点的指针,另一个是结点的名字为A。例如P指向B,意思是P这个指针指向了B这个结点,实际上B也是个指针。
其实也可以new来分配内存
比如LNode * A=new LNode;这里就涉及了new和malloc的区别了,就不写了。
2.动态链表的操作
默认表为单链表,且有头结点,头插尾插都是在头结点后面开始插
头插尾插建表
- 头插法建表
思想:头插法顾名思义从头开始插,即每次在头结点L后面的结点插入
- 循环,每次构造结点时都要申请一次内存,用指针s指向这个内存,赋值
- 循环中 s的next指向头结点L的next,再L的next指向s
s->next=L>next;
L->next=s;这样就可以了
void HeadInsert(Lnode *&L){
L=(Lnode*)malloc(sizeof(Lnode));
L->next=NULL;
Lnode *s;
int n=0;
cout<<"输入个数"<<endl;
cin>>n;
while(n>0){
int i=0;
cin>>i;
s=(Lnode*)malloc(sizeof(Lnode));
s->data=i;
s->next=L->next;
L->next=s;
n--;
}
}
- 尾插法建表
思想:尾插法顾名思义从头开始插,即每次在链表尾部插入。由于链表的特性,每次插入都要找尾结点太麻烦,不如直接创建个辅助指针r时刻标记尾结点,一开始r=C;
- 循环,每次构造结点时都要申请一次内存,用指针s指向这个内存,赋值
- 循环中 r->next=s; r=r->next(或者r=s);
- 循环结束要将r-next=NULL;
void RearInsert(Lnode *&C){
C=(Lnode*)malloc(sizeof(Lnode));
C->next=NULL;
Lnode *s,*r;
r=C;
int n=0;
cout<<"输入个数"<<endl;
cin>>n;
while(n>0){
int i=0;
cin>>i;
s=(Lnode*)malloc(sizeof(Lnode));
s->data=i;
r->next=s;
r=r->next;
n--;
}
r->next=NULL;
}
链表基本操作
- 查(寻找数据为x的结点)
//查找链表中元素为x的结点
Lnode* FindLnode(Lnode *C,int x){
Lnode *p=C->next;
while(p!=NULL) {
if(x==p->data) break;
p=p->next;
}
return p;
}
- 增(在p结点后增加一个结点s)
//在元素为x的结点后插入一个结点
void AddLnode(Lnode *C,int x){
int i=0;
Lnode *p=FindLnode(C, x);//查操作,找到数据为x的结点,用p接收
cout<<"输入要插入结点的数据"<<endl;
cin>>i;
Lnode *s=(Lnode*)malloc(sizeof(Lnode));//申请空间
//增操作关键代码
s->data=i;
s->next=p->next;
p->next=s;
}
- 改
查操作查到了就可以修改 - 删(删除结点p后面的结点)
void DeleteLnode(Lnode *C,int x){
Lnode *p=FindLnode(C, x);//查操作,找到数据为x的结点,用p接收
Lnode *s=p->next;
p->next=s->next;
free(s);
}
2.关于双链表,循环单链表,循环双链表
链表这东西一通百通,其他的链表无非就是结点添个指针,多个指向,只要单链表的基本操作操作理解了,其他也没什么难的,触类旁通。