c语言链表视频教程放在前面和后面有没有影响?

C语言关于链表的问题_百度知道
C语言关于链表的问题
&nbsp://a;i.c; & & & & & &nbsp.h& q[i]=((*y); & & & & &%c& &nbsp#include &stdio. struct string *x=& &\n& & & &&nbsp.c==EOF)& &nbsp.jpg" target="_blank" title="点击查看大图" class="ikqb_img_alink"><img class="ikqb_img" src=" printf(&&nbsp./zhidao/wh%3D600%2C800/sign=3e6fd0f166d224f4acfb4;b; if((*y); & &nbsp
提问者采纳
如果出错的后果吗我说的可能很直接?2,我看了半天,一个负责链表的初始化,这样对你定位错误很难,怎么可以用它来命名结构体呢,这样你可以迅速定位问题,给你提几个建议,但你把他们都放在一起了,3,比如,main函数只负责对函数的调用,程序要分层:1,你都没有办法检测这个链构建的对不对,应该在结构体后面至少分成两个函数,这样可以防止内存泄漏.你的链表很有问题,要在他的尾指针处加上NULL,string 是关键词,你可以想象像你这样写个几万行代码,4,你应该知道,链表不是这样写的 ,另一个负责insert,对于链表,甚至都不知道是哪个出错了,你对你的链表,要尽量养成通过对函数的调用来完成任务,希望你不要介意你写的这个程序太乱了
提问者评价
其他类似问题
为您推荐:
链表的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁链表的c语言实现(一)
准备:动态内存分配
一、为什么用动态内存分配
但我们未学习链表的时候,如果要存储数量比较多的同类型或同结构的数据的时候,总是使用一个数组。比如说我们要存储一个班级学生的某科分数,总是定义一个float型(存在0.5分)数组:
float score[30];
但是,在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?
在很多的情况下,你并不能确定要使用多大的数组,比如上例,你可能并不知道该班级的学生的人数,那么你就要把数组定义得足够大。这样,你的程序在运行时就申请了固定大小的你认为足够大的内存空间。即使你知道该班级的学生数,但是如果因为某种特殊原因人数有增加或者减少,你又必须重新去修改程序,扩大数组的存储范围。这种分配固定大小的内存分配方法称之为静态内存分配。但是这种内存分配的方法存在比较严重的缺陷,特别是处理某些问题时:在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。
那么有没有其它的方法来解决这样的外呢体呢?有,那就是动态内存分配。
所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。从以上动、静态内存分配比较可以知道动态内存分配相对于景泰内存分配的特点:
1、不需要预先分配存储空间;
2、分配的空间可以根据程序的需要扩大或缩小。
二、如何实现动态内存分配及其管理
要实现根据程序的需要动态分配存储空间,就必须用到以下几个
malloc的原型为:
void *malloc (unsigned int size)
其作用是在内存的动态存储区中分配一个长度为size的连续空间。其参数是一个无符号整形数,返回&#20540;是一个指向所分配的连续存储域的起始地址的。还有一点必须注意的是,当未能成功分配存储空间(如内存不足)就会返回一个NULL。所以在调用该时应该检测返回&#20540;是否为NULL并执行相应的操作。
是一个动态分配的程序:
int count,* /*count是一个计数器,array是一个整型指针,也可以理解为指向一个整型数组的首地址*/
if((array(int *) malloc(10*sizeof(int)))==NULL)
printf(&不能成功分配存储空间。&);
for (count=0;count〈10;count++) /*给数组赋值*/
array[count]=
for(count=0;count〈10;count++) /*打印数组元素*/
printf(&%2d&,array[count]);
上例中动态分配了10个整型存储区域,然后进行赋&#20540;并打印。例中if((array(int *) malloc(10*sizeof(int)))==NULL)语句可以分为以下几步:
1)分配10个整型的连续存储空间,并返回一个指向其起始地址的整型
2)把此整型地址赋给array
3)检测返回&#20540;是否为NULL
由于内存区域总是有限的,不能不限制地分配下去,而且一个程序要尽量节省资源,所以当所分配的内存区域不用时,就要释放它,以便其它的变量或者程序使用。这时我们就要用到free。
其原型是:
void free(void *p)
作用是释放p所指向的内存区。
其参数p必须是先前调用malloc或calloc(另一个动态分配存储区域的)时返回的。给free传递其它的&#20540;很可能造成死机或其它灾难性的后果。
注意:这里重要的是的&#20540;,而不是用来申请动态内存的本身。例:
int *p1,*p2;
p1=malloc(10*sizeof(int));
free(p2) /*或者free(p2)*/
malloc返回&#20540;赋给p1,又把p1的&#20540;赋给p2,所以此时p1,p2都可作为free的参数。
malloc是对存储区域进行分配的。
free是释放已经不用的内存区域的。
所以由这两个就可以实现对内存区域进行动态分配并进行简单的管理了。
一、单链表的建立
有了动态内存分配的基础,要实现链表就不难了。
所谓链表,就是用一组任意的存储单元存储线性表元素的一种。
链表又分为单链表、双向链表和循环链表等。我们先讲讲单链表。
所谓单链表,是指数据接点是单向排列的。一个单链表结点,其结构类型分为两部分:
1、数据域:用来存储本身数据
2、链域或称为域:用来存储下一个结点地址或者说指向其直接后继的。
typedef struct node
char name[20];
struct node *
这样就定义了一个单链表的结构,其中char name[20]是一个用来存储姓名的字符型数组,*link是一个用来存储其直接后继的。
定义好了链表的结构之后,只要在程序运行的时候爱数据域中存储适当的数据,如有后继结点,则把链域指向其直接后继,若没有,则置为NULL。
下面就来看一个建立带表头(若未说明,以下所指链表均带表头)的单链表的完整程序。
#include &stdio.h&
#include &malloc.h& /*包含动态内存分配函数的头文件*/
#define N 10 /*N为人数*/
typedef struct node
char name[20];
struct node *
stud * creat(int n) /*建立单链表的函数,形参n为人数*/
stud *p,*h,*s; /* *h保存表头结点的指针,*p指向当前结点的前一个结点,*s指向当前结点*/
/*计数器*/
if((h=(stud *)malloc(sizeof(stud)))==NULL) /*分配空间并检测*/
printf(&不能分配内存空间!&);
h-&name[0]=&#39;\0&#39;; /*把表头结点的数据域置空*/
h-&link=NULL; /*把表头结点的链域置空*/
p=h; /*p指向表头结点*/
for(i=0;i&n;i++)
if((s= (stud *) malloc(sizeof(stud)))==NULL) /*分配新存储空间并检测*/
printf(&不能分配内存空间!&);
p-&link=s; /*把s的地址赋给p所指向的结点的链域,这样就把p和s所指向的结点连接起来了*/
printf(&请输入第%d个人的姓名&,i+1);
scanf(&%s&,s-&name); /*在当前结点s的数据域中存储姓名*/
s-&link=NULL;
return(h);
/*保存人数的变量*/
stud * /*head是保存单链表的表头结点地址的指针*/
head=creat(number); /*把所新建的单链表表头地址赋给head*/
这样就写好了一个可以建立包含N个人姓名的单链表了。
写动态内存分配的程序应注意,请尽量对分配是否成功进行检测。
二、单链表的基本运算
建立了一个单链表之后,如果要进行一些如插入、删除等操作该怎么办?所以还须掌握一些单链表的基本算法,来实现这些操作。单链表的基本运算包括:查找、插入和删除。下面我们就一一介绍这三种基本运算的算法,并结合我们建立单链表的例子写出相应的程序。
对单链表进行查找的思路为:对单链表的结点依次扫描,检测其数据域是否是我们所要查好的&#20540;,若是返回该结点的,否则返回NULL。
因为在单链表的链域中包含了后继结点的存储地址,所以当我们实现的时候,只要知道该单链表的头,即可依次对每个结点的数据域进行检测。
以下是应用查找算法的一个例子:
&/pre&&p&&#160;&/p&&pre class=&cpp& name=&code&&#include &stdio.h&
#include &malloc.h&
#include &string.h& /*包含一些字符串处理函数的头文件*/
#define N 10
typedef struct node
char name[20];
struct node *
stud * creat(int n) /*建立链表的函数*/
stud *p,*h,*s;
if((h=(stud *)malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
h-&name[0]=&#39;\0&#39;;
h-&link=NULL;
for(i=0;i&n;i++)
if((s= (stud *) malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
p-&link=s;
printf(&请输入第%d个人的姓名&,i+1);
scanf(&%s&,s-&name);
s-&link=NULL;
return(h);
stud * search(stud *h,char *x) /*查找链表的函数,其中h指针是链表的表头指针,x指针是要查找的人的姓名*/
stud *p; /*当前指针,指向要与所查找的姓名比较的结点*/
char *y; /*保存结点数据域内姓名的指针*/
while(p!=NULL)
if(strcmp(y,x)==0) /*把数据域里的姓名与所要查找的姓名比较,若相同则返回0,即条件成立*/
return(p); /*返回与所要查找结点的地址*/
else p=p-&
if(p==NULL)
printf(&没有查找到该数据!&);
char fullname[20];
stud *head,* /*head是表头指针,searchpoint是保存符合条件的结点地址的指针*/
head=creat(number);
printf(&请输入你要查找的人的姓名:&);
scanf(&%s&,fullname);
searchpoint=search(head,fullname); /*调用查找函数,并把结果赋给searchpoint指针*/
2、插入(后插)
假设在一个单链表中存在2个连续结点p、q(其中p为q的直接前驱),若我们需要在p、q之间插入一个新结点s,那么我们必须先为s分配空间并赋&#20540;,然后使p的链域存储s的地址,s的链域存储q的地址即可。(p-&link=s;s-&link=q),这样就完成了插入操作。
下例是应用插入算法的一个例子:
#include &stdio.h&
#include &malloc.h&
#include &string.h&
#define N 10
typedef struct node
char name[20];
struct node *
stud * creat(int n) /*建立单链表的函数*/
stud *p,*h,*s;
if((h=(stud *)malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
h-&name[0]=&#39;\0&#39;;
h-&link=NULL;
for(i=0;i&n;i++)
if((s= (stud *) malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
p-&link=s;
printf(&请输入第%d个人的姓名:&,i+1);
scanf(&%s&,s-&name);
s-&link=NULL;
return(h);
stud * search(stud *h,char *x) /*查找函数*/
while(p!=NULL)
if(strcmp(y,x)==0)
return(p);
else p=p-&
if(p==NULL)
printf(&没有查找到该数据!&);
void insert(stud *p) /*插入函数,在指针p后插入*/
char stuname[20];
stud *s; /*指针s是保存新结点地址的*/
if((s= (stud *) malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
printf(&请输入你要插入的人的姓名:&);
scanf(&%s&,stuname);
strcpy(s-&name,stuname); /*把指针stuname所指向的数组元素拷贝给新结点的数据域*/
s-&link=p-& /*把新结点的链域指向原来p结点的后继结点*/
p-&link=s; /*p结点的链域指向新结点*/
char fullname[20]; /*保存输入的要查找的人的姓名*/
stud *head,*
head=creat(number); /*建立新链表并返回表头指针*/
printf(&请输入你要查找的人的姓名:&);
scanf(&%s&,fullname);
searchpoint=search(head,fullname); /*查找并返回查找到的结点指针*/
insert(searchpoint); /*调用插入函数*/
假如我们已经知道了要删除的结点p的位置,那么要删除p结点时只要令p结点的前驱结点的链域由存储p结点的地址该为存储p的后继结点的地址,并回收p结点即可。
以下便是应用删除算法的实例:
#include &stdio.h&
#include &malloc.h&
#include &string.h&
#define N 10
typedef struct node
char name[20];
struct node *
stud * creat(int n) /*建立新的链表的函数*/
stud *p,*h,*s;
if((h=(stud *)malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
h-&name[0]=&#39;\0&#39;;
h-&link=NULL;
for(i=0;i&n;i++)
if((s= (stud *) malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
p-&link=s;
printf(&请输入第%d个人的姓名&,i+1);
scanf(&%s&,s-&name);
s-&link=NULL;
return(h);
stud * search(stud *h,char *x) /*查找函数*/
while(p!=NULL)
if(strcmp(y,x)==0)
return(p);
else p=p-&
if(p==NULL)
printf(&没有查找到该数据!&);
stud * search2(stud *h,char *x) /*另一个查找函数,返回的是上一个查找函数的直接前驱结点的指针,*/
/*h为表头指针,x为指向要查找的姓名的指针*/
/*其实此函数的算法与上面的查找算法是一样的,只是多了一个指针s,并且s总是指向指针p所指向的结点的直接前驱,*/
/*结果返回s即是要查找的结点的前一个结点*/
stud *p,*s;
while(p!=NULL)
if(strcmp(y,x)==0)
return(s);
if(p==NULL)
printf(&没有查找到该数据!&);
void del(stud *x,stud *y) /*删除函数,其中y为要删除的结点的指针,x为要删除的结点的前一个结点的指针*/
x-&link=y-&
char fullname[20];
stud *head,*searchpoint,*
head=creat(number);
printf(&请输入你要删除的人的姓名:&);
scanf(&%s&,fullname);
searchpoint=search(head,fullname);
forepoint=search2(head,fullname);
del(forepoint,searchpoint);
一、循环链表
循环链表是与单链表一样,是一种链式的存储结构,所不同的是,循环链表的最后一个结点的是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。
循环链表的运算与单链表的运算基本一致。所不同的有以下几点:
1、在建立一个循环链表时,必须使其最后一个结点的指向表头结点,而不是象单链表那样置为NULL。此种情况还使用于在最后一个结点后插入一个新的结点。
2、在判断是否到表尾时,是判断该结点链域的&#20540;是否是表头结点,当链域&#20540;等于表头时,说明已到表尾。而非象单链表那样判断链域&#20540;是否为NULL。
二、双向链表
双向链表其实是单链表的改进。
当我们对单链表进行操作时,有时你要对某个结点的直接前驱进行操作时,又必须从表头开始查找。这是由单链表结点的结构所限制的。因为单链表每个结点只有一个存储直接后继结点地址的链域,那么能不能定义一个既有存储直接后继结点地址的链域,又有存储直接前驱结点地址的链域的这样一个双链域结点结构呢?这就是双向链表。
在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域;一个存储直接前驱结点地址,一般称之为左链域。在c语言中双向链表结点类型可以定义为:
typedef struct node
/*数据域*/
struct node *llink,* /*链域,*llink是左链域,*rlink是右链域*/
当然,也可以把一个双向链表构建成一个双向循环链表。
双向链表与单向链表一样,也有三种基本运算:查找、插入和删除。
双向链表的基本运算:
假若我们要在一个带表头的双向循环链表中查找数据域为一特定&#20540;的某个结点时,我们同样从表头结点往后依次比较各结点数据域的&#20540;,若正是该特定&#20540;,则返回指向结点的,否则继续往后查,直到表尾。
下例就是应用双向循环链表查找算法的一个程序。
#include &stdio.h&
#include &malloc.h&
#define N 10
typedef struct node
char name[20];
struct node *llink,*
stud * creat(int n)
stud *p,*h,*s;
if((h=(stud *)malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
h-&name[0]=&#39;\0&#39;;
h-&llink=NULL;
h-&rlink=NULL;
for(i=0;i&n;i++)
if((s= (stud *) malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
p-&rlink=s;
printf(&请输入第%d个人的姓名&,i+1);
scanf(&%s&,s-&name);
s-&llink=p;
s-&rlink=NULL;
h-&llink=s;
p-&rlink=h;
return(h);
stud * search(stud *h,char *x)
while(p!=h)
if(strcmp(y,x)==0)
return(p);
else p=p-&
printf(&没有查找到该数据!&);
void print(stud *h)
printf(&数据信息为:\n&);
while(p!=h)
printf(&%s &,&*(p-&name));
printf(&\n&);
char studname[20];
stud *head,*
head=creat(number);
print(head);
printf(&请输入你要查找的人的姓名:&);
scanf(&%s&,studname);
searchpoint=search(head,studname);
printf(&你所要查找的人的姓名是:%s&,*&searchpoint-&name);
对于双向循环链表,我们现在可以随意地在某已知结点p前或者p后插入一个新的结点。
假若s,p,q是连续三个结点的,若我们要在p前插入一个新结点r,则只需把s的右链域指向r,r的左链域指向s,r的右链域指向p,p的左链域指向r即可。
在p,q之间插入原理也一样。
下面就是一个应用双向循环链表插入算法的例子:
#include &stdio.h&
#include &malloc.h&
#include &string.h&
#define N 10
typedef struct node
char name[20];
struct node *llink,*
stud * creat(int n)
stud *p,*h,*s;
if((h=(stud *)malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
h-&name[0]=&#39;\0&#39;;
h-&llink=NULL;
h-&rlink=NULL;
for(i=0;i&n;i++)
if((s= (stud *) malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
p-&rlink=s;
printf(&请输入第%d个人的姓名&,i+1);
scanf(&%s&,s-&name);
s-&llink=p;
s-&rlink=NULL;
h-&llink=s;
p-&rlink=h;
return(h);
stud * search(stud *h,char *x)
while(p!=h)
if(strcmp(y,x)==0)
return(p);
else p=p-&
printf(&没有查找到该数据!&);
void print(stud *h)
printf(&数据信息为:\n&);
while(p!=h)
printf(&%s &,&*(p-&name));
printf(&\n&);
void insert(stud *p)
char stuname[20];
if((s= (stud *) malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
printf(&请输入你要插入的人的姓名:&);
scanf(&%s&,stuname);
strcpy(s-&name,stuname);
s-&rlink=p-&
p-&rlink=s;
s-&llink=p;
(s-&rlink)-&llink=s;
char studname[20];
stud *head,*
head=creat(number);
print(head);
printf(&请输入你要查找的人的姓名:&);
scanf(&%s&,studname);
searchpoint=search(head,studname);
printf(&你所要查找的人的姓名是:%s\n&,*&searchpoint-&name);
insert(searchpoint);
print(head);
删除某个结点,其实就是插入某个结点的逆操作。还是对于双向循环链表,要在连续的三个结点s,p,q中删除p结点,只需把s的右链域指向q,q的左链域指向s,并收回p结点就完成了。
下面就是一个应用双向循环链表删除算法的例子:
#define N 10
typedef struct node
char name[20];
struct node *llink,*
stud * creat(int n)
stud *p,*h,*s;
if((h=(stud *)malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
h-&name[0]=&#39;\0&#39;;
h-&llink=NULL;
h-&rlink=NULL;
for(i=0;i〈n;i++)
if((s= (stud *) malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
p-〉rlink=s;
printf(&请输入第%d个人的姓名&,i+1);
scanf(&%s&,s-&name);
s-&llink=p;
s-&rlink=NULL;
h-&llink=s;
p-&rlink=h;
return(h);
stud * search(stud *h,char *x)
while(p!=h)
if(strcmp(y,x)==0)
return(p);
else p=p-&
printf(&没有查找到该数据!&);
void print(stud *h)
printf(&数据信息为:\n&);
while(p!=h)
printf(&%s &,&*(p-&name));
printf(&\n&);
void del(stud *p)
(p-&rlink)-&llink=p-&
(p-&llink)-&rlink=p-&
char studname[20];
stud *head,*
head=creat(number);
print(head);
printf(&请输入你要查找的人的姓名:&);
scanf(&%s&,studname);
searchpoint=search(head,studname);
printf(&你所要查找的人的姓名是:%s\n&,*&searchpoint-&name);
del(searchpoint);
print(head);
在这里列举了一个应用单链表基本算法的综合程序,双向链表和循环链表的综合程序大家可以自己去试一试。
#include &stdio.h&
#include &malloc.h&
#include &string.h&
#define N 10
typedef struct node
char name[20];
struct node *
stud * creat(int n)
stud *p,*h,*s;
if((h=(stud *)malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
h-&name[0]=&#39;\0&#39;;
h-&link=NULL;
for(i=0;i&n;i++)
if((s= (stud *) malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
p-&link=s;
printf(&请输入第%d个人的姓名&,i+1);
scanf(&%s&,s-&name);
s-&link=NULL;
return(h);
stud * search(stud *h,char *x)
while(p!=NULL)
if(strcmp(y,x)==0)
return(p);
else p=p-&
if(p==NULL)
printf(&没有查找到该数据!&);
stud * search2(stud *h,char *x)
stud *p,*s;
while(p!=NULL)
if(strcmp(y,x)==0)
return(s);
if(p==NULL)
printf(&没有查找到该数据!&);
void insert(stud *p)
char stuname[20];
if((s= (stud *) malloc(sizeof(stud)))==NULL)
printf(&不能分配内存空间!&);
printf(&\n请输入你要插入的人的姓名:&);
scanf(&%s&,stuname);
strcpy(s-&name,stuname);
s-&link=p-&
p-&link=s;
void del(stud *x,stud *y)
x-&link=y-&
void print(stud *h)
printf(&数据信息为:\n&);
while(p!=NULL)
printf(&%s &,&*(p-&name));
void quit()
void menu(void)
printf(&\t\t\t单链表C语言实现实例\n&);
printf(&\t\t|————————————————|\n&);
printf(&\t\t| |\n&);
printf(&\t\t| [1] 建 立 新 表 |\n&);
printf(&\t\t| [2] 查 找 数 据 |\n&);
printf(&\t\t| [3] 插 入 数 据 |\n&);
printf(&\t\t| [4] 删 除 数 据 |\n&);
printf(&\t\t| [5] 打 印 数 据 |\n&);
printf(&\t\t| [6] 退 出 |\n&);
printf(&\t\t| |\n&);
printf(&\t\t| 如未建立新表,请先建立! |\n&);
printf(&\t\t| |\n&);
printf(&\t\t|————————————————|\n&);
printf(&\t\t 请输入你的选项(1-6):&);
stud *head,*searchpoint,*
char fullname[20];
scanf(&%d&,&choose);
switch(choose)
case 1:head=creat(N);
case 2:printf(&输入你所要查找的人的姓名:&);
scanf(&%s&,fullname);
searchpoint=search(head,fullname);
printf(&你所查找的人的姓名为:%s&,*&searchpoint-&name);
printf(&\n按回车键回到主菜单。&);
getchar();getchar();
case 3: printf(&输入你要在哪个人后面插入:&);
scanf(&%s&,fullname);
searchpoint=search(head,fullname);
printf(&你所查找的人的姓名为:%s&,*&searchpoint-&name);
insert(searchpoint);
print(head);
printf(&\n按回车键回到主菜单。&);
getchar();getchar();
case 4:print(head);
printf(&\n输入你所要删除的人的姓名:&);
scanf(&%s&,fullname);
searchpoint=search(head,fullname);
forepoint=search2(head,fullname);
del(forepoint,searchpoint);
case 5:print(head);
printf(&\n按回车键回到主菜单。&);
getchar();getchar();
case 6:quit();
default:printf(&你输入了非法字符!按回车键回到主菜单。&);
getchar();
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:279次
排名:千里之外C语言链表 可以先 跳过 然后看完后面的 在返回来看?_百度知道
C语言链表 可以先 跳过 然后看完后面的 在返回来看?
了是用来考试吗?如果它是考试范围内的建议不要跳过,即便是与后面内容无关,毕竟一本书的章节安排还是有它的道理的如果是为了增长知识那更不应该跳过
其他类似问题
为您推荐:
链表的相关知识
其他3条回答
你把指针和结构体搞懂了,链表不是问题可以跳过
是数据结构吧
表示如果你跳过你后面更看不懂
C语言还没学完 在指针这章介绍 链表 看了2天 还不怎么了解 想先把后面一些C语言看 在返回来 看链表
这个没有多大关系
了了了了了了了了了了了了了了了了了了了了
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁随笔- 150&
&&&&&&&&&&&
链表的c语言实现(一)
准备:动态内存分配 一、为什么用动态内存分配 但我们未学习链表的时候,如果要存储数量比较多的同类型或同结构的数据的时候,总是使用一个数组。比如说我们要存储一个班级学生的某科分数,总是定义一个float型(存在0.5分)数组: float score[30]; 但是,在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?
在很多的情况下,你并不能确定要使用多大的数组,比如上例,你可能并不知道该班级的学生的人数,那么你就要把数组定义得足够大。这样,你的程序在运行时就
申请了固定大小的你认为足够大的内存空间。即使你知道该班级的学生数,但是如果因为某种特殊原因人数有增加或者减少,你又必须重新去修改程序,扩大数组的
存储范围。这种分配固定大小的内存分配方法称之为静态内存分配。但是这种内存分配的方法存在比较严重的缺陷,特别是处理某些问题时:在大多数情况下会浪费
大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。 那么有没有其它的方法来解决这样的外呢体呢?有,那就是动态内存分配。
所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空
间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。从以上动、静态内存分配比较可以知道动态内存分配相对于景泰内存分配的特点: 1、不需要预先分配存储空间; 2、分配的空间可以根据程序的需要扩大或缩小。 二、如何实现动态内存分配及其管理 要实现根据程序的需要动态分配存储空间,就必须用到以下几个 1、malloc malloc的原型为: void *malloc (unsigned int size) 其作用是在内存的动态存储区中分配一个长度为size的连续空间。其参数是一个无符号整形数,返回值是一个指向所分配的连续存储域的起始地址的。还有一点必须注意的是,当未能成功分配存储空间(如内存不足)就会返回一个NULL。所以在调用该时应该检测返回值是否为NULL并执行相应的操作。 下例 是一个动态分配的程序:
#include #include main(){int count,* /*count是一个计数器,array是一个整型指针,也可以理解为指向一个整型数组的首地址*/if((array(int *) malloc(10*sizeof(int)))==NULL){printf("不能成功分配存储空间。");exit(1);}for (count=0;count〈10;count++) /*给数组赋值*/array[count]=for(count=0;count〈10;count++) /*打印数组元素*/printf("%2d",array[count]);}
上例中动态分配了10个整型存储区域,然后进行赋值并打印。例中if((array(int *) malloc(10*sizeof(int)))==NULL)语句可以分为以下几步: 1)分配10个整型的连续存储空间,并返回一个指向其起始地址的整型 2)把此整型地址赋给array 3)检测返回值是否为NULL 2、free 由于内存区域总是有限的,不能不限制地分配下去,而且一个程序要尽量节省资源,所以当所分配的内存区域不用时,就要释放它,以便其它的变量或者程序使用。这时我们就要用到free。 其原型是: void free(void *p) 作用是释放p所指向的内存区。 其参数p必须是先前调用malloc或calloc(另一个动态分配存储区域的)时返回的。给free传递其它的值很可能造成死机或其它灾难性的后果。 注意:这里重要的是的值,而不是用来申请动态内存的本身。例: int *p1,*p2; p1=malloc(10*sizeof(int)); p2=p1; && free(p2) /*或者free(p2)*/ malloc返回值赋给p1,又把p1的值赋给p2,所以此时p1,p2都可作为free的参数。 malloc是对存储区域进行分配的。 free是释放已经不用的内存区域的。 所以由这两个就可以实现对内存区域进行动态分配并进行简单的管理了。
一、单链表的建立 有了动态内存分配的基础,要实现链表就不难了。 所谓链表,就是用一组任意的存储单元存储线性表元素的一种。 链表又分为单链表、双向链表和循环链表等。我们先讲讲单链表。 所谓单链表,是指数据接点是单向排列的。一个单链表结点,其结构类型分为两部分: 1、数据域:用来存储本身数据 2、链域或称为域:用来存储下一个结点地址或者说指向其直接后继的。 例: typedef struct node { char name[20]; struct node * } 这样就定义了一个单链表的结构,其中char name[20]是一个用来存储姓名的字符型数组,*link是一个用来存储其直接后继的。 定义好了链表的结构之后,只要在程序运行的时候爱数据域中存储适当的数据,如有后继结点,则把链域指向其直接后继,若没有,则置为NULL。 下面就来看一个建立带表头(若未说明,以下所指链表均带表头)的单链表的完整程序。
#include &stdio.h&#include &malloc.h& /*包含动态内存分配函数的头文件*/#define N 10 /*N为人数*/ typedef struct node{char name[20];struct node *}stud * creat(int n) /*建立单链表的函数,形参n为人数*/{stud *p,*h,*s; /* *h保存表头结点的指针,*p指向当前结点的前一个结点,*s指向当前结点*/int /*计数器*/if((h=(stud *)malloc(sizeof(stud)))==NULL) /*分配空间并检测*/{printf("不能分配内存空间!");exit(0);}h-&name[0]='\0'; /*把表头结点的数据域置空*/h-&link=NULL; /*把表头结点的链域置空*/p=h; /*p指向表头结点*/for(i=0;i&n;i++){if((s= (stud *) malloc(sizeof(stud)))==NULL) /*分配新存储空间并检测*/{printf("不能分配内存空间!");exit(0);}p-&link=s; /*把s的地址赋给p所指向的结点的链域,这样就把p和s所指向的结点连接起来了*/printf("请输入第%d个人的姓名",i+1);scanf("%s",s-&name); /*在当前结点s的数据域中存储姓名*/s-&link=NULL;p=s;}return(h);}main(){int /*保存人数的变量*/stud * /*head是保存单链表的表头结点地址的指针*/number=N;head=creat(number); /*把所新建的单链表表头地址赋给head*/}
这样就写好了一个可以建立包含N个人姓名的单链表了。 写动态内存分配的程序应注意,请尽量对分配是否成功进行检测。
二、单链表的基本运算 建立了一个单链表之后,如果要进行一些如插入、删除等操作该怎么办?所以还须掌握一些单链表的基本算法,来实现这些操作。单链表的基本运算包括:查找、插入和删除。下面我们就一一介绍这三种基本运算的算法,并结合我们建立单链表的例子写出相应的程序。 1、查找 对单链表进行查找的思路为:对单链表的结点依次扫描,检测其数据域是否是我们所要查好的值,若是返回该结点的,否则返回NULL。 因为在单链表的链域中包含了后继结点的存储地址,所以当我们实现的时候,只要知道该单链表的头,即可依次对每个结点的数据域进行检测。
以下是应用查找算法的一个例子:
#include &stdio.h&#include &malloc.h&#include &string.h& /*包含一些字符串处理函数的头文件*/#define N 10typedef struct node{char name[20];struct node *}stud * creat(int n) /*建立链表的函数*/{stud *p,*h,*s;intif((h=(stud *)malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}h-&name[0]='\0';h-&link=NULL;p=h;for(i=0;i&n;i++){if((s= (stud *) malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}p-&link=s;printf("请输入第%d个人的姓名",i+1);scanf("%s",s-&name);s-&link=NULL;p=s;}return(h);}stud * search(stud *h,char *x) /*查找链表的函数,其中h指针是链表的表头指针,x指针是要查找的人的姓名*/{stud *p; /*当前指针,指向要与所查找的姓名比较的结点*/char *y; /*保存结点数据域内姓名的指针*/p=h-&while(p!=NULL){y=p-&if(strcmp(y,x)==0) /*把数据域里的姓名与所要查找的姓名比较,若相同则返回0,即条件成立*/return(p); /*返回与所要查找结点的地址*/else p=p-&}if(p==NULL)printf("没有查找到该数据!");}main(){intchar fullname[20];stud *head,* /*head是表头指针,searchpoint是保存符合条件的结点地址的指针*/number=N;head=creat(number);printf("请输入你要查找的人的姓名:");scanf("%s",fullname);searchpoint=search(head,fullname); /*调用查找函数,并把结果赋给searchpoint指针*/}
2、插入(后插)
假设在一个单链表中存在2个连续结点p、q(其中p为q的直接前驱),若我们需要在p、q之间插入一个新结点s,那么我们必须先为s分配空间并赋值,然后
使p的链域存储s的地址,s的链域存储q的地址即可。(p-&link=s;s-&link=q),这样就完成了插入操作。 下例是应用插入算法的一个例子:
#include &stdio.h&#include &malloc.h&#include &string.h&#define N 10typedef struct node{char name[20];struct node *}stud * creat(int n) /*建立单链表的函数*/{stud *p,*h,*s;intif((h=(stud *)malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}h-&name[0]='\0';h-&link=NULL;p=h;for(i=0;i&n;i++){if((s= (stud *) malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}p-&link=s;printf("请输入第%d个人的姓名:",i+1);scanf("%s",s-&name);s-&link=NULL;p=s;}return(h);}stud * search(stud *h,char *x) /*查找函数*/{stud *p;char *y;p=h-&while(p!=NULL){y=p-&if(strcmp(y,x)==0)return(p);else p=p-&}if(p==NULL)printf("没有查找到该数据!");}void insert(stud *p) /*插入函数,在指针p后插入*/{char stuname[20];stud *s; /*指针s是保存新结点地址的*/if((s= (stud *) malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}printf("请输入你要插入的人的姓名:");scanf("%s",stuname);strcpy(s-&name,stuname); /*把指针stuname所指向的数组元素拷贝给新结点的数据域*/s-&link=p-& /*把新结点的链域指向原来p结点的后继结点*/p-&link=s; /*p结点的链域指向新结点*/}main(){intchar fullname[20]; /*保存输入的要查找的人的姓名*/stud *head,*number=N;head=creat(number); /*建立新链表并返回表头指针*/printf("请输入你要查找的人的姓名:");scanf("%s",fullname);searchpoint=search(head,fullname); /*查找并返回查找到的结点指针*/insert(searchpoint); /*调用插入函数*/}
3、删除 假如我们已经知道了要删除的结点p的位置,那么要删除p结点时只要令p结点的前驱结点的链域由存储p结点的地址该为存储p的后继结点的地址,并回收p结点即可。 以下便是应用删除算法的实例:
#include &stdio.h&#include &malloc.h&#include &string.h&#define N 10typedef struct node{char name[20];struct node *}stud * creat(int n) /*建立新的链表的函数*/{stud *p,*h,*s;intif((h=(stud *)malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}h-&name[0]='\0';h-&link=NULL;p=h;for(i=0;i&n;i++){if((s= (stud *) malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}p-&link=s;printf("请输入第%d个人的姓名",i+1);scanf("%s",s-&name);s-&link=NULL;p=s;}return(h);}stud * search(stud *h,char *x) /*查找函数*/{stud *p;char *y;p=h-&while(p!=NULL){y=p-&if(strcmp(y,x)==0)return(p);else p=p-&}if(p==NULL)printf("没有查找到该数据!");}stud * search2(stud *h,char *x) /*另一个查找函数,返回的是上一个查找函数的直接前驱结点的指针,*//*h为表头指针,x为指向要查找的姓名的指针*//*其实此函数的算法与上面的查找算法是一样的,只是多了一个指针s,并且s总是指向指针p所指向的结点的直接前驱,*//*结果返回s即是要查找的结点的前一个结点*/{stud *p,*s;char *y;p=h-&s=h;while(p!=NULL){y=p-&if(strcmp(y,x)==0)return(s);else{p=p-&s=s-&}}if(p==NULL)printf("没有查找到该数据!");}void del(stud *x,stud *y) /*删除函数,其中y为要删除的结点的指针,x为要删除的结点的前一个结点的指针*/{stud *s;s=y;x-&link=y-&free(s);}main(){intchar fullname[20];stud *head,*searchpoint,*number=N;head=creat(number);printf("请输入你要删除的人的姓名:");scanf("%s",fullname);searchpoint=search(head,fullname);forepoint=search2(head,fullname);del(forepoint,searchpoint);
一、循环链表 循环链表是与单链表一样,是一种链式的存储结构,所不同的是,循环链表的最后一个结点的是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。 循环链表的运算与单链表的运算基本一致。所不同的有以下几点: 1、在建立一个循环链表时,必须使其最后一个结点的指向表头结点,而不是象单链表那样置为NULL。此种情况还使用于在最后一个结点后插入一个新的结点。
2、在判断是否到表尾时,是判断该结点链域的值是否是表头结点,当链域值等于表头时,说明已到表尾。而非象单链表那样判断链域值是否为NULL。
二、双向链表 双向链表其实是单链表的改进。
当我们对单链表进行操作时,有时你要对某个结点的直接前驱进行操作时,又必须从表头开始查找。这是由单链表结点的结构所限制的。因为单链表每个结点只有一
个存储直接后继结点地址的链域,那么能不能定义一个既有存储直接后继结点地址的链域,又有存储直接前驱结点地址的链域的这样一个双链域结点结构呢?这就是
双向链表。 在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域;一个存储直接前驱结点地址,一般称之为左链域。在c语言中双向链表结点类型可以定义为:
typedef struct node { /*数据域*/ struct node *llink,* /*链域,*llink是左链域,*rlink是右链域*/ }JD; 当然,也可以把一个双向链表构建成一个双向循环链表。 双向链表与单向链表一样,也有三种基本运算:查找、插入和删除。
双向链表的基本运算: 1、查找 假若我们要在一个带表头的双向循环链表中查找数据域为一特定值的某个结点时,我们同样从表头结点往后依次比较各结点数据域的值,若正是该特定值,则返回指向结点的,否则继续往后查,直到表尾。 下例就是应用双向循环链表查找算法的一个程序。
#include &stdio.h&#include &malloc.h&#define N 10typedef struct node{char name[20];struct node *llink,*}stud * creat(int n){stud *p,*h,*s;intif((h=(stud *)malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}h-&name[0]='\0';h-&llink=NULL;h-&rlink=NULL;p=h;for(i=0;i&n;i++){if((s= (stud *) malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}p-&rlink=s;printf("请输入第%d个人的姓名",i+1);scanf("%s",s-&name);s-&llink=p;s-&rlink=NULL;p=s;}h-&llink=s;p-&rlink=h;return(h);}stud * search(stud *h,char *x){stud *p;char *y;p=h-&while(p!=h){y=p-&if(strcmp(y,x)==0)return(p);else p=p-&}printf("没有查找到该数据!");}void print(stud *h){intstud *p;p=h-&printf("数据信息为:\n");while(p!=h){printf("%s ",&*(p-&name));p=p-&}printf("\n");}main(){intchar studname[20];stud *head,*number=N;clrscr();head=creat(number);print(head);printf("请输入你要查找的人的姓名:");scanf("%s",studname);searchpoint=search(head,studname);printf("你所要查找的人的姓名是:%s",*&searchpoint-&name);}
2、插入 对于双向循环链表,我们现在可以随意地在某已知结点p前或者p后插入一个新的结点。 假若s,p,q是连续三个结点的,若我们要在p前插入一个新结点r,则只需把s的右链域指向r,r的左链域指向s,r的右链域指向p,p的左链域指向r即可。 在p,q之间插入原理也一样。 下面就是一个应用双向循环链表插入算法的例子:
#include &stdio.h&#include &malloc.h&#include &string.h&#define N 10typedef struct node{char name[20];struct node *llink,*}stud * creat(int n){stud *p,*h,*s;intif((h=(stud *)malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}h-&name[0]='\0';h-&llink=NULL;h-&rlink=NULL;p=h;for(i=0;i&n;i++){if((s= (stud *) malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}p-&rlink=s;printf("请输入第%d个人的姓名",i+1);scanf("%s",s-&name);s-&llink=p;s-&rlink=NULL;p=s;}h-&llink=s;p-&rlink=h;return(h);}stud * search(stud *h,char *x){stud *p;char *y;p=h-&while(p!=h){y=p-&if(strcmp(y,x)==0)return(p);else p=p-&}printf("没有查找到该数据!");}void print(stud *h){intstud *p;p=h-&printf("数据信息为:\n");while(p!=h){printf("%s ",&*(p-&name));p=p-&}printf("\n");}void insert(stud *p){char stuname[20];stud *s;if((s= (stud *) malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}printf("请输入你要插入的人的姓名:");scanf("%s",stuname);strcpy(s-&name,stuname);s-&rlink=p-&p-&rlink=s;s-&llink=p;(s-&rlink)-&llink=s;}main(){intchar studname[20];stud *head,*number=N;clrscr();head=creat(number);print(head);printf("请输入你要查找的人的姓名:");scanf("%s",studname);searchpoint=search(head,studname);printf("你所要查找的人的姓名是:%s\n",*&searchpoint-&name);insert(searchpoint);print(head);}
3、删除 删除某个结点,其实就是插入某个结点的逆操作。还是对于双向循环链表,要在连续的三个结点s,p,q中删除p结点,只需把s的右链域指向q,q的左链域指向s,并收回p结点就完成了。 下面就是一个应用双向循环链表删除算法的例子:
#include #include #include #define N 10typedef struct node{char name[20];struct node *llink,*}stud * creat(int n){stud *p,*h,*s;intif((h=(stud *)malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}h-&name[0]='\0';h-&llink=NULL;h-&rlink=NULL;p=h;for(i=0;i〈n;i++){if((s= (stud *) malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}p-〉rlink=s;printf("请输入第%d个人的姓名",i+1);scanf("%s",s-&name);s-&llink=p;s-&rlink=NULL;p=s;}h-&llink=s;p-&rlink=h;return(h);}stud * search(stud *h,char *x){stud *p;char *y;p=h-&while(p!=h){y=p-&if(strcmp(y,x)==0)return(p);else p=p-&}printf("没有查找到该数据!");}void print(stud *h){intstud *p;p=h-&printf("数据信息为:\n");while(p!=h){printf("%s ",&*(p-&name));p=p-&}printf("\n");}void del(stud *p){(p-&rlink)-&llink=p-&(p-&llink)-&rlink=p-&free (p);}main(){intchar studname[20];stud *head,*number=N;clrscr();head=creat(number);print(head);printf("请输入你要查找的人的姓名:");scanf("%s",studname);searchpoint=search(head,studname);printf("你所要查找的人的姓名是:%s\n",*&searchpoint-&name);del(searchpoint);print(head);}
在这里列举了一个应用单链表基本算法的综合程序,双向链表和循环链表的综合程序大家可以自己去试一试。
#include &stdio.h&#include &malloc.h&#include &string.h&#define N 10typedef struct node{char name[20];struct node *}stud * creat(int n){stud *p,*h,*s;intif((h=(stud *)malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}h-&name[0]='\0';h-&link=NULL;p=h;for(i=0;i&n;i++){if((s= (stud *) malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}p-&link=s;printf("请输入第%d个人的姓名",i+1);scanf("%s",s-&name);s-&link=NULL;p=s;}return(h);}stud * search(stud *h,char *x){stud *p;char *y;p=h-&while(p!=NULL){y=p-&if(strcmp(y,x)==0)return(p);else p=p-&}if(p==NULL)printf("没有查找到该数据!");}stud * search2(stud *h,char *x){stud *p,*s;char *y;p=h-&s=h;while(p!=NULL){y=p-&if(strcmp(y,x)==0)return(s);else{p=p-&s=s-&}}if(p==NULL)printf("没有查找到该数据!");}void insert(stud *p){char stuname[20];stud *s;if((s= (stud *) malloc(sizeof(stud)))==NULL){printf("不能分配内存空间!");exit(0);}printf("\n请输入你要插入的人的姓名:");scanf("%s",stuname);strcpy(s-&name,stuname);s-&link=p-&p-&link=s;}void del(stud *x,stud *y){stud *s;s=y;x-&link=y-&free(s);}void print(stud *h){stud *p;p=h-&printf("数据信息为:\n");while(p!=NULL){printf("%s ",&*(p-&name));p=p-&}}void quit(){exit(0);}void menu(void){clrscr();printf("\t\t\t单链表C语言实现实例\n");printf("\t\t|&&&&&&&&&&&&&&&&|\n");printf("\t\t| |\n");printf("\t\t| [1] 建 立 新 表 |\n");printf("\t\t| [2] 查 找 数 据 |\n");printf("\t\t| [3] 插 入 数 据 |\n");printf("\t\t| [4] 删 除 数 据 |\n");printf("\t\t| [5] 打 印 数 据 |\n");printf("\t\t| [6] 退 出 |\n");printf("\t\t| |\n");printf("\t\t| 如未建立新表,请先建立! |\n");printf("\t\t| |\n");printf("\t\t|&&&&&&&&&&&&&&&&|\n");printf("\t\t 请输入你的选项(1-6):");}main(){intstud *head,*searchpoint,*char fullname[20];while(1){menu();scanf("%d",&choose);switch(choose){case 1:head=creat(N);break;case 2:printf("输入你所要查找的人的姓名:");scanf("%s",fullname);searchpoint=search(head,fullname);printf("你所查找的人的姓名为:%s",*&searchpoint-&name);printf("\n按回车键回到主菜单。");getchar();getchar();break;case 3: printf("输入你要在哪个人后面插入:");scanf("%s",fullname);searchpoint=search(head,fullname);printf("你所查找的人的姓名为:%s",*&searchpoint-&name);insert(searchpoint);print(head);printf("\n按回车键回到主菜单。");getchar();getchar();break;case 4:print(head);printf("\n输入你所要删除的人的姓名:");scanf("%s",fullname);searchpoint=search(head,fullname);forepoint=search2(head,fullname);del(forepoint,searchpoint);break;case 5:print(head);printf("\n按回车键回到主菜单。");getchar();getchar();break;case 6:quit();break;default:printf("你输入了非法字符!按回车键回到主菜单。");clrscr();menu();getchar();}}}
阅读(...) 评论()

我要回帖

更多关于 您好 放在 前面 后面 的文章

 

随机推荐