单链表
本人水平有限,若有问题还请多指教
关于链表的一些问题
链表结点一般用new或者malloc创建,那可不可以用calloc?
可以
calloc和malloc都是动态分配的函数
两个函数返回的都是一个void*类型的指针,所以要强制类型转换成LNode * 类型
L = (LNode*)calloc(1,sizeof(LNode));
L = (LNode*)malloc(sizeof(LNode));
L = new LNode;
三种其实是差不多的,
只是new是在自由储存区动态分配内存,分配失败会报异常
malloc和calloc在堆里开辟,分配失败返回NULL
链表创建为什么使用动态内存分配?
链表创建为什么需要使用内存分配?
指向结构体的指针由于结构体里面的基本类型的种类和数量不确定才动态分配的,内存越多,地址越长,指针占用的字节就越多
而且,想要在全局使用链表,就必须动态分配内存,静态分配的话,是无法再拓展的
链表执行时错误?
- 创建新结点时没有写LNode *p=new LNode;(或者malloc,calloc也可以)
- 空指针问题
顺便讲一下什么是空指针
int *p=NULL;
p就是空指针,这时候
printf("%d\n",p);
就会发生异常
对空指针还想了解的可以看看这个
C语言空指针NULL以及void指针
对于还想了解野指针通用指针可以看看这个[破涕为笑]
关于空指针NULL、野指针、通用指针
结点的声明
typedef struct LNode{
int data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
可以看到上面有3个LNode
,用123来代替第一二三个LNode
1必须与2一致,因为2的指针是指向1的
3相当于是struct LNode
的一个小名,可以更换
注意一下,
LNode *p=new LNode;
和
LinkList p=new LNode;
是一样的
其实就是少写了一个* , LinkList和LNode*相互代替也是可以的
如果这是一个对象,可以甚至可以这样写
LinkList p=new LNode();
LNode *p=new LNode();
如果你还对结构体的初始化有问题,可以来这里看看
stackoverflow
功能介绍
- createList 创建n个结点的链表
- printList 打印链表–0
- findI 根据序号查找 ,返回序号为i(序号从1开始)的结点的地址,找不到返回NULL --1
- findV 根据值查找 返回值为e的第一个节结点的地址,找不到返回NULL --2
- findIP 根据序号查找前驱 返回序号为i(序号从1开始)的结点的前驱结点的地址,找不到返回NULL --3
- findVP 根据值查找前驱 返回值为e的第一个结点的前驱结点的地址,找不到返回NULL --4
- insert 在位置i(从1开始)插入元素e 成功返回1,失败返回0 --5
- del 删除位置i的元素 成功返回1,失败返回0 --6
我们来看代码
main函数
int main(){
LinkList L;
int cmd, e, n, i;
LNode *p;
scanf("%d", &n);
createList(L, n);
while (scanf("%d", &cmd) != EOF ) { // 选择操作
switch (cmd) {
case 0:
printList(L);
break;
case 1: // 根据序号查找
scanf("%d", &i);
p = findI(L, i);
if ( p!=NULL) printf("#%d\n", p->data);
else printf("#Not Found\n");
break;
case 2: // 根据值查找
scanf("%d", &e);
p = findV(L, e);
if ( p!=NULL) printf("#%d\n", p->data);
else printf("#Not Found\n");
break;
case 3: // 根据序号查找前驱
scanf("%d", &i);
p = findIP(L, i);
if ( p!=NULL) printf("#%d\n", i-1);
else printf("#Not Found\n");
break;
case 4: // 根据值查找前驱
scanf("%d", &e);
p = findVP(L, e);
if ( p!=NULL) printf("#%d\n", p->next->data);
else printf("#Not Found\n");
break;
case 5: // 在位置i插入元素e
scanf("%d %d", &i, &e);
re = insert(L, i, e);
if (re) printf("#OK\n");
else printf("#ERROR\n");
break;
case 6: // 删除位置i的元素
scanf("%d", &i);
re = del(L, i);
if (re) printf("#OK\n");
else printf("#ERROR\n");
printList(L);
break;
default:
printf("#Unknow Command.\n");
break;
}
}
return 0;
}
初始化长度为n的链表
void createList(LinkList &L, int n)
{
L=new LNode;
LNode *p=L;
p->next=NULL;
while(n--){
LNode *w = new LNode;//创建新的结点
p->next=w;//上一个结点的指针域指向w
scanf("%d",&w->data);//赋值给w的数据域
w->next=NULL;//w的指针域指向NULL
p=p->next;//p移到w方便下次操作
}
}
打印链表
void printList(LinkList L)
{
int k=0;
while(L->next!=NULL)//不是空链表就打印
{
L=L->next;
printf("%d ",L->data);
k++;
}
if(k!=0)//格式
printf("\n");
}
根据序号查找
LNode* findI(LinkList L, int i)
{
LinkList p=L;//i如果<=0或者超过链表长度
for(int j=1;p->next!=NULL;j++){//指针域不是NULL就继续,直到链表结尾
p=p->next;
if(j==i){
return p;//p代表该结点的地址
}
}
return p->next;
// return NULL;//也可以
}
根据值查找
LNode* findV(LinkList L, int e)
{
LinkList p=L;
while(p->next!=NULL){//指针域不是NULL就继续
p=p->next;//移到下一个结点
if(p->data==e){//结点数据域的值与e相等的话返回地址p
return p;
}
}
return p->next;//p->next就是NULL
}
改进
LNode* findV (LinkList L,ElemType e) {
p=L->next;
while(p &&p->data!=e)
p=p->next;
return p;
}
根据序号寻找前驱
指针p所指结点的后继结点为p->next
指针p所指结点的后继结点的数据域为p->next->data
位置1的前驱结点为头结点
长度为n的链表,位置n+1的前驱结点为第n个结点(尾结点)
LNode* findIP(LinkList L, int i)//3
{
LNode* p=L;
int j=1;//用来计数
if(i==1){//开头的情况要分出来,因为后面使用的是j+1==i
if(p->next!=NULL){
return p;
} else{//空表的情况
return NULL;
}
}
while(p->next!=NULL)
{
if(j+1==i){
return p;
}
j++;
p=p->next;
}
return NULL;
}
根据值寻找前驱
LNode* findVP(LinkList L, int e)//4
{
LNode* p=L;
while(p->next!=NULL)
{
if(p->next->data==e){//p的后驱的数据域的值等于e的话就返回p的地址
return p;
}
p=p->next;
}
return NULL;
}
在位置i(从1开始)插入元素e
int insert(LinkList &L, int i, int e)
{
LinkList p = L;
int j=1;
while(p->next!=NULL)
{
if(j==i){
LNode *y=new LNode;//创建一个结点
y->data=e;//新结点数据域赋值e
y->next=p->next;//新结点的指针域指向p的指针域
p->next=y;//p的指针域指向新结点y的地址,注意,这一步和上一步不能调换
return 1;
}
p=p->next;
j++;
}
if(p->next==NULL&&j==i){//i在末尾的情况
LinkList t=new LNode;
t->next=NULL;//新结点的指针域指向NULL
t->data=e;
p->next=t;
return 1;
}
return 0;
}
删除位置i的元素
int del(LinkList &L, int i)
{
LinkList p = L;
int j=1;
while(p->next!=NULL)
{
if(i==1){//开头
L=p->next;//让L指向p的下一个结点,让下一个结点当头结点
free(p);//释放原本的头结点p
return 1;
}
p=p->next;
if(j==i-1){
if(p->next==NULL){
return 0;
}
else{
LNode *r=p->next;//r用来记录p的下一个结点
p->next=p->next->next;//p的指针域指向p的下下个结点
r->next=NULL;//被删除的r的指针域指向NULL
free(r);//释放r
return 1;
}
}
j++;
}
return 0;
}
或者我们可以优化一下
变成这样可以顺带判断i==1的情况
int del(LinkList &L, int i)
{
LinkList p = L;
int j=1;
while(p->next!=NULL)
{
if(j==i){
if(p->next==NULL){//空表的情况或者位置i超过链表范围
return 0;
}
else{
LNode *r=p->next;//r用来记录p的下一个结点
p->next=p->next->next;//p的指针域指向p的下下个结点
r->next=NULL;//被删除的r的指针域指向NULL
free(r);//释放r
return 1;
}
}
p=p->next;
j++;
}
return 0;
}