针对数据结构中的链表,用C语言实现所有函数的代码。说实话,我感觉数据结构的教材都是采用伪代码,有的函数连伪代码也没提供,对初学数据结构的小白实在不是很友好。希望这些代码能给初学者带来方便。
#include<stdio.h>
#include<stdlib.h>
//线性表的链式实现中,每一个数据元素都存在结构体节点中,每一个节点结构体需要包含数据域和指针域两个部分
//为了简化,这里数据域为int类型。指针域为指向自身结构体类型的指针变量。
typedef struct LNode{
int data;
struct LNode *next;
}LNode,*LinkList;//LinkList 就等价于 struct LNode *;所有声明为LinkList类型的变量都是一个指向这个结构体类型的指针变量
int main(){
//函数声明部分
LinkList InitList();//链表的初始化(带有头结点)
int ListInsert(LinkList ,int ,int ); //向链表的合法位置插入指定元素
void visit(int );//对数据域的访问方法
void ListTraverse(LinkList ,void (*f)(int ));//遍历链表,函数指针指向visit方法
int ListDelete(LinkList ,int ,int *);//删除合法位置的结点
void DestroyList(LinkList);//销毁链表,释放空间
int ListLength(LinkList); //返回链表的长度(即除去头结点以外的结点个数)
void GetElem(LinkList ,int ,int *);//获取指定位置的数据
int compare(int ,int );//两个结点数据域的比较方法
int LocateElem(LinkList ,int ,int (*f)(int ,int ));//根据上面的比较方法查找链表中第一个满足条件的数据,返回它在链表中的位置
void PriorElem(LinkList ,int ,int *);//返回指定值的元素的前驱元素
void NextElem(LinkList ,int ,int *);//返回指点值的元素的后继元素
//函数测试部分
//链表初始化,并且插入元素
LinkList l;
l=InitList();
if(ListInsert(l,1,9)){
printf("ok\n");
}
ListInsert(l,2,8);
ListInsert(l,1,10);
//测试求表长函数
printf("\n现在的表长是:%d\n",ListLength(l));
//测试遍历函数
ListTraverse(l,visit);
//测试元素删除函数
int t,*tp=&t;
if(ListDelete(l,2,tp)){
printf("\n成功删除了第%d个元素,被删除元素的值是%d\n",2,t);
}
printf("\n现在的表长是:%d\n",ListLength(l));
ListTraverse(l,visit);
//测试访问指定位置元素函数
int k,*e=&k;
GetElem(l,2,e);
printf("\n第二个元素是:%d\n",k);
//测试按值定位函数
int loc=LocateElem(l,10,compare);
if(loc!=0){
printf("\n定位10的位置:%d\n",loc);
}else{
printf("\n链表中没有这个元素\n");
}
//测试求前驱函数
int pri,*prip=&pri;
PriorElem(l,8,prip);
printf("找到8的前驱元素:%d\n",pri);
//测试求后继函数
int next,*nextp=&next;
NextElem(l,10,nextp);
printf("找到10的后继元素:%d\n",next);
//测试链表销毁函数
DestroyList(l);
l=NULL;//销毁空间的函数free不会把指针置空,这里手动置空
return 0;
}
//初始化函数
LinkList InitList(){//返回指向结构体类型的指针变量
LinkList t=(LinkList)malloc(sizeof(LNode));//申请头结点的空间
if(!t){
exit(1);
}
t->next=NULL;//头结点数据域为空,指针域置空
return t;//返回申请到的空间首地址
}
//插入元素函数
int ListInsert(LinkList l,int i,int e){
LinkList p=l;
int j=0;
while(p&&j<i-1){
p=p->next;
j++;
}
if(!p||j>i-1){
return 0;
}
LinkList s=(LinkList)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
return 1;
}
//额,后面的懒得写了,需要注意的后面各个函数中的循环终止条件,不太理解的可以画个例子在纸上试一下
//核心是p和p->next的关系:假如说p指向第i-1个元素,那么p的值就等于第i-2个结点的指针域的值。p->next等价于
//第i-1个结点的指针域的值,那么p->next指向第i的结点。理解这点很重要,因为链表无法随机访问,大多数操作都要从
//头指针出发挨个找的真确的位置。
void ListTraverse(LinkList l,void (*f)(int )){
if(!l){
printf("这是一个空表");
exit(1);
}
LinkList p=l;
for(p=p->next;p!=NULL;p=p->next){
f(p->data);
}
}
void visit(int a){
printf("%d\t",a);
}
int ListDelete(LinkList l,int i,int *e){
LinkList p=l;
int j=0;
while(p->next&&j<i-1){
p=p->next;
++j;
}
if(!(p->next)||j>i-1){
return 0;
}
LinkList q=p->next;
p->next=q->next;
*e=q->data;
free(q);
return 1;
}
void DestroyList(LinkList l){
LinkList p=l;
if(p->next!=NULL){
DestroyList(p->next);
free(p);
}else{
free(p);
}
}
int ListLength(LinkList l){
if(!l){
exit(1);
}
int j=0;
for(LinkList p=l;p->next!=NULL;p=p->next){
j++;
}
return j;
}
void GetElem(LinkList l,int i,int *e){
if(!l){
exit(1);
}
LinkList p=l;
for(int j=0;j<i;j++){
p=p->next;
}
*e=p->data;
}
int compare(int a,int b){
if(a==b){
return 1;
}else{
return 0;
}
}
int LocateElem(LinkList l,int e,int (*f)(int ,int )){
LinkList p=l;
int j=1;
for(p=p->next;p!=NULL;p=p->next){
if(f(e,p->data)==1){
return j;
}
j++;
}
return 0;
}
void PriorElem(LinkList l,int cur,int *pri){
int i=LocateElem(l,cur,compare);
if(i==1||i==0){
exit(1);
}
GetElem(l,i-1,pri);
}
void NextElem(LinkList l,int cur,int *next){
int i=LocateElem(l,cur,compare);
if(i==0||i==ListLength(l)){
exit(1);
}
GetElem(l,i+1,next);
}
运行结构:
ok
现在的表长是:3
10 9 8
成功删除了第2个元素,被删除元素的值是9
现在的表长是:2
10 8
第二个元素是:8
定位10的位置:1
找到8的前驱元素:10
找到10的后继元素:8
--------------------------------
Process exited after 2.351 seconds with return value 0
请按任意键继续. . .
码字不易点个赞再走啊~