大学实验3指导:利用单链表实现A-B
实验目的:深入理解单链表的建立及操作
实验内容:
1.建立单链表A与B
2.实现主要的函数,查找、插入、删除等
3.实现操作A-B
步骤1:包含必要的函数库,对结构体LNode中的抽象数据类型ElemType进行具体定义
1 #include <stdio.h> 2 3 #include <stdlib.h> 4 5 typedef int ElemType;
步骤2:定义结构体LNode
1 typedef struct LNode 2 3 { 4 5 ElemType data; 6 7 struct LNode *next; 8 9 }LNode, *LinkList;
F提示:LNode用于声明单链表中的一个数据结点,LinkList用于声明指向单链表中的数据结点的指针
步骤3:定义基本的函数InitList_L()、ListInsert_L()、GetElem_L ()、ListSize_L(),用于建立单链表A和B
步骤3.1:实现函数InitList_L()。(该函数用于对单链表进行初始化,即创建头结点)
F提示1:参数L为指向“单链表头指针”的指针,即
LinkList *L;
等价于
LNode **L;
F提示2:由于需要在函数中修改L所指向的头指针的内容(即调用InitList_L()之前,头指针为空,调用InitList_L()之后,头指针指向新创建的头结点),因此需要在L前加“*”号。
F提示3:由于*L表示头指针的内容,因此在修改头结点的next域时,可以执行:
(*L)->next=NULL
由于在a为指向结构体的指针时,运算a->b等价于(*a).b,因此该语句等价于:
(*(*L)).next=NULL;
1 void InitList_L(LinkList *L) 2 3 { 4 5 *L=(LinkList)malloc(sizeof(LNode)); 6 7 if (*L==NULL) 8 9 exit(-1); 10 11 (*L)->next=NULL; 12 13 //等价于(*(*L)).next=NULL; 14 15 }
步骤3.2:实现函数ListInsert_L ()。(在指定位置上插入新元素)
1 int ListInsert_L(LinkList L,int i,ElemType e) 2 3 { 4 LNode *p,*s; 5 int j; 6 p=L; 7 j=0; 8 while(p&&j<i-1) 9 { 10 p=p->next;++j; 11 } 12 if(!p||j>i-1) 13 return 0; 14 s=(LinkList)malloc(sizeof(LNode)); 15 s->data=e; 16 s->next=p->next; 17 p->next=s; 18 return 1; 19 }
步骤3.3:GetElem_L()。(返回顺序表中指定位置上的元素)
1 int GetElem_L(LinkList L, int i, ElemType *e) 2 { 3 LinkList p; 4 int j=1; 5 p=L->next; 6 while(p&&j<i) 7 { 8 p=p->next; 9 ++j; 10 } 11 if(!p||j>i) 4 12 return 0; 13 *e=p->data; 14 return 1; 15 }
步骤3.4:ListSize_L()。(返回单链表的长度)
1 int ListSize_L(LinkList L) 2 { 3 LNode *p; 4 int count=0; 5 p=L; 6 while(p->next!=NULL) 7 { 8 p=p->next; 9 count++; 10 } 11 return count; 12 }
步骤4:建立线性表A与B,并输出其中的元素
F提示:采用静态方式创建A=(3,5,8,11),B=(2,6,8,9,11,15,20)。
1 int main() 2 { 3 int i,flag,e; 4 //定义两个头指针 5 LinkList A,B; 6 //测试函数GetElemPointer_L() 7 LNode *p; 8 /*初始化单链表A,将头指针A的地址(即指向A的指针)传入*/ 9 InitList_L(&A); 10 /*初始化单链表B,将头指针B的地址(即指向B的指针)传入*/ 11 InitList_L(&B); 12 /*为单链表A填充数据*/ 13 ListInsert_L(A,1,3); 14 … 15 ListInsert_L(A,4,11); 16 /*为单链表B填充数据*/ 17 ListInsert_L(B,1,2); 18 … 19 ListInsert_L(B,7,20); 20 /*输出单链表A*/ 21 printf("单链表A中的元素为:\n"); 22 for(i=1;i<=ListSize_L(A);i++) 23 { 24 flag=GetElem_L(A,i,&e); 25 if(flag) 26 printf("%4d",e); 27 } 28 printf("\n"); 29 /*输出单链表B*/ 30 printf("单链表B中的元素为:\n"); 31 for(i=1;i<=ListSize_L(B);i++) 32 { 33 flag=GetElem_L(B,i,&e); 34 if(flag) 35 printf("%4d",e); 36 } 37 printf("\n"); 38 }
步骤4的输出结果
步骤5:实现函数ListDelete_L()。(用于删除指定位置上的元素)
F提示:该函数的声明为:
1 int ListDelete_L(LinkList *L,int i, ElemType *e)
步骤6:测试函数ListDelete_L()
1 flag=ListDelete_L(&B,7,&e); 2 if(flag) 3 printf("被删除的元素为:%4d",e); 4 printf("\n"); 5 printf("单链表B中的剩余元素为:\n"); 6 for(i=1;i<=ListSize_L(B);i++) 7 { 8 flag=GetElem_L(B,i,&e); 9 if(flag) 10 printf("%4d",e); 11 } 12 printf("\n");
步骤6的输出结果
步骤7:实现函数LocateElem_L ()
LocateElem_L():返回给定元素在单链表中的位置(序号)。注意:头结点的序号为0。
F提示:首先,令p指向单链表的表头结点,即L->next。若单链表为空,即L->next==NULL,则返回0。否则,对单链表进行遍历,并返回匹配结点的位置。最后,若最终没有找到,则返回0。
该函数声明为:
1 int LocateElem_L(LinkList L, ElemType e);
步骤8:测试函数LocateElem_L()
1 flag=LocateElem_L(B,15); 2 printf("元素15在单链表B中的位置为:%4d\n",flag);
步骤8的输出结果
步骤9:实现函数GetElemPointer_L()。(返回指向单链表中第i个元素的指针)
F提示:首先,若单链表为空,即L->next==NULL,则返回空指针。接下来,若参数i非法,则返回空指针。然后,对单链表进行遍历,并返回匹配结点的指针。最后,若最终没有找到,则返回空指针。
该函数声明为:
1 LNode *GetElemPointer_L(LinkList L,int i);
步骤10:测试函数GetElemPointer_L()
1 p=GetElemPointer_L(A,3); 2 printf("单链表A中的第3个元素为:%4d\n",p->data);
步骤10的输出结果
步骤11:实现函数DelElem_L()(实现A-B)。
F提示:利用循环遍历顺序表B。在每轮循环中,先利用函数GetElemPointer_L()取得指向B中的当前结点的指针(假设该结点指针保存在p中),再利用函数LocateElem_L()检查中A是否存在数据域等于p->data的结点,若存在则返回匹配结点的位置pos。最后,利用函数ListDelete_L()删除所匹配的结点(即A中的第pos个结点)。
函数DelElem_L()的声明如下:
1 void DelElem_L(LinkList A,LinkList B);
步骤12:测试函数DelElem_L()的功能
1 DelElem_L(A,B);//执行A-B 2 printf("单链表A中的剩余元素为:\n"); 3 for(i=1;i<=ListSize_L(A);i++) 4 { 5 flag=GetElem_L(A,i,&e); 6 if(flag) 7 printf("%4d",e); 8 } 9 printf("\n");
步骤12的输出结果
思考题
1.将有关单链表的所有基本函数组织成单独的文件“LinkList.h”,然后利用include命令调用该文件。
1 /*删除指定位置上的元素*/ 2 int ListDelete_L(LinkList *L,int i, ElemType *e) { 3 LNode *p,*q; 4 p=*L; 5 int j=0; 6 while(p->next&&j<i-1) { 7 p=p->next; 8 ++j; 9 } 10 if(!(p->next)||j>i-1) return 0; 11 q=p->next; 12 p->next=q->next; 13 *e=q->data; 14 free(q); 15 return 1; 16 } 17 18 /*返回给定元素在单链表中的位置*/ 19 int LocateElem_L(LinkList L, ElemType e) { 20 LNode *p; 21 int i; 22 if(L->next==NULL) 23 return 0; 24 p=L->next; 25 i=1; 26 while(p) { 27 28 if(p->data==e) 29 return i; 30 else { 31 p=p->next; 32 i++; 33 } 34 if(!p) return 0; 35 } 36 } 37 38 /*返回指向单链表中第i个元素的指针*/ 39 /*若找到第i个结点,则返回指向该结点的指针;否则,返回空指针*/ 40 LNode *GetElemPointer_L(LinkList L,int i) { 41 LNode *p; 42 int j; 43 //若单链表为空 44 if(L->next==NULL) 45 return NULL; 46 //若参数非法 47 if(i<1) 48 return NULL; 49 p=L; 50 j=0; 51 while(p->next!=NULL&&j<i) { 52 p=p->next; 53 j++; 54 } 55 if(j==i) 56 return p; 57 else 58 return NULL; 59 } 60 61 void DelElem_L(LinkList A,LinkList B) { 62 int i,pos,flag; 63 ElemType e; 64 LNode *p; 65 for(i=1; i<=ListSize_L(B); i++) { 66 p=GetElemPointer_L(B,i);//p指向单链表B中存在第i个结点 67 if(p) { //若单链表B中存在第i个结点 68 pos=LocateElem_L(A,p->data);//若A中存在相同的结点,则用返回其在A中的位置 69 if(pos>0)//若存在,则在A中删除该元素 70 ListDelete_L(&A,pos,&e); 71 /* 72 {flag=ListDelete_L(&A,pos,&e); 73 if(flag) 74 printf("被删除的元素为:%4d\n",e); 75 } 76 */ 77 } 78 } 79 }