C语言-----循环双向链表(增加文件读写链表功能)
2015-11-25 17:39
543 查看
双向链表其实是单链表的改进。
当我们对单链表进行操作时,有时你要对某个结点的直接前驱进行操作时,又必须从表头开始查找。这是由单链表结点的结构所限制的。因为单链表每个结点只有一个存储直接后继结点地址的链域,那么能不能定义一个既有存储直接后继结点地址的链域,又有存储直接前驱结点地址的链域的这样一个双链域结点结构呢?这就是双向链表。
在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域;一个存储直接前驱结点地址,一般称之为左链域。在c语言中双向链表结点类型可以定义为:
typedef struct node
{
int data; /*数据域*/
struct node *llink,*rlink; /*链域,*llink是左链域指针,*rlink是右链域指针*/
}JD;
当然,也可以把一个双向链表构建成一个双向循环链表。
双向链表与单向链表一样,也有三种基本运算:查找、插入和删除。
1、查找
假若我们要在一个带表头的双向循环链表中查找数据域为一特定值的某个结点时,我们同样从表头结点往后依次比较各结点数据域的值,若正是该特定值,则返回指向结点的指针,否则继续往后查,直到表尾。
2、插入
对于双向循环链表,我们现在可以随意地在某已知结点p前或者p后插入一个新的结点。
假若s,p,q是连续三个结点的指针,若我们要在p前插入一个新结点r,则只需把s的右链域指针指向r,r的左链域指针指向s,r的右链域指针指向p,p的左链域指针指向r即可。
在p,q之间插入原理也一样。
3、删除
删除某个结点,其实就是插入某个结点的逆操作。还是对于双向循环链表,要在连续的三个结点s,p,q中删除p结点,只需把s的右链域指针指向q,q的左链域指针指向s,并收回p结点就完成了。
4、保存和读写
在实际应用中,如通讯录系统,需要保存的联系人的信息,那么就需要将链表的数据保存在文件中,如果需要对通讯录内容进行更改或者操作就需要先从文件中读取之前保存的链表的数据,然后进行修改。(使用fread,fwrite,stat即可实现读写的功能)
下面使用具体的例子演示双向循环链表的实现,并将链表的数据保存在文件中。
以下是测试过程:
当我们对单链表进行操作时,有时你要对某个结点的直接前驱进行操作时,又必须从表头开始查找。这是由单链表结点的结构所限制的。因为单链表每个结点只有一个存储直接后继结点地址的链域,那么能不能定义一个既有存储直接后继结点地址的链域,又有存储直接前驱结点地址的链域的这样一个双链域结点结构呢?这就是双向链表。
在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域;一个存储直接前驱结点地址,一般称之为左链域。在c语言中双向链表结点类型可以定义为:
typedef struct node
{
int data; /*数据域*/
struct node *llink,*rlink; /*链域,*llink是左链域指针,*rlink是右链域指针*/
}JD;
当然,也可以把一个双向链表构建成一个双向循环链表。
双向链表与单向链表一样,也有三种基本运算:查找、插入和删除。
1、查找
假若我们要在一个带表头的双向循环链表中查找数据域为一特定值的某个结点时,我们同样从表头结点往后依次比较各结点数据域的值,若正是该特定值,则返回指向结点的指针,否则继续往后查,直到表尾。
2、插入
对于双向循环链表,我们现在可以随意地在某已知结点p前或者p后插入一个新的结点。
假若s,p,q是连续三个结点的指针,若我们要在p前插入一个新结点r,则只需把s的右链域指针指向r,r的左链域指针指向s,r的右链域指针指向p,p的左链域指针指向r即可。
在p,q之间插入原理也一样。
3、删除
删除某个结点,其实就是插入某个结点的逆操作。还是对于双向循环链表,要在连续的三个结点s,p,q中删除p结点,只需把s的右链域指针指向q,q的左链域指针指向s,并收回p结点就完成了。
4、保存和读写
在实际应用中,如通讯录系统,需要保存的联系人的信息,那么就需要将链表的数据保存在文件中,如果需要对通讯录内容进行更改或者操作就需要先从文件中读取之前保存的链表的数据,然后进行修改。(使用fread,fwrite,stat即可实现读写的功能)
下面使用具体的例子演示双向循环链表的实现,并将链表的数据保存在文件中。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include <sys/stat.h> typedef struct node{ char name[32]; struct node *l; //左节点 struct node *r; //右节点 }no; unsigned int len=sizeof(no); no* CreateList(int n){ no *head,*cur,*prev; if((head=(no *)malloc(len))==NULL){ printf("Alloc Failed \n"); return NULL; } head->name[0]='H'; head->l=NULL; head->r=NULL; prev=head; for(n;n>=1;n--){ if((cur=(no *)malloc(len))==NULL){ printf("Alloc Failed \n"); return NULL; } prev->r=cur; printf("pls input the name:"); scanf("%s",cur->name); cur->l=prev; cur->r=NULL; prev=cur; } prev->r=head; head->l=cur; return head; } void PrintList(no *head){ no *cur; cur=head->r; while(cur!=head){ printf("[%s]",cur->name); cur=cur->r; } printf("\n"); } no *Search(no* head){ no *cur; char *name; cur=head->r; printf("pls input search name:"); scanf("%s",name); while(cur!=head){ if(strcmp(name,cur->name)==0) return cur; else{ cur=cur->r; } } printf("Search : Not Found\n"); return NULL; } void* InsertNode(no *cur){//在cur节点之后插入 no *insert; char *str; if((insert=(no *)malloc(len))==NULL){ printf("Alloc Failed \n"); return NULL; } printf("pls input insert name:"); scanf("%s",insert->name); insert->r=cur->r; insert->l=cur; cur->r=insert; cur->r->l=insert; } void DeleteNode(no *cur){//删除cur节点 (cur->r)->l=cur->l; (cur->l)->r=cur->r; free(cur); cur=NULL; } FILE* File_CreateKeepOpen(char *filename,char* mode){ FILE *fp; fp=fopen(filename,mode); if(!fp){ return NULL; } return fp; } int File_Close(FILE *fp){ if(!fclose(fp)) return 1; else return 0; } int File_SaveNodeData(FILE *fp,no *head){ no *cur; int size; cur=head->r; while(cur!=head){ size=fwrite(cur,len,1,fp); cur=cur->r; size+=size; } return size; } no* File_ReadNodeData(FILE *fp){//其实就是创建双向循环链表的过程 no *head,*cur,*prev; struct stat s; int size,n; stat("list.data",&s); printf("s.st_size[%d]\n",s.st_size); printf("node num [%d]\n",s.st_size/len); if((head=(no *)malloc(len))==NULL){ printf("Alloc Failed \n"); return NULL; } head->name[0]='H'; head->l=NULL; head->r=NULL; prev=head; for(n=1;n<=s.st_size/len;n++){ if((cur=(no *)malloc(len))==NULL){ printf("Alloc Failed \n"); return NULL; } prev->r=cur; size=fread(cur,1,len,fp); cur->l=prev; cur->r=NULL; prev=cur; } prev->r=head; head->l=cur; return head; } int main(){ no *head,*cur,*ret; no *File_head; FILE *fp; int n; do{//使用do{}while结构实现scanf循环读取数据 printf("pls input create list num(integer) : "); scanf("%d",&n); }while(getchar()!='\n');//getchar执行之后消除缓冲区 if((head=CreateList(n))!=NULL) PrintList(head); else printf("CreateList Failed\n"); cur=Search(head); if((ret=InsertNode(cur))!=NULL) PrintList(head); else printf("InsertNode Failed\n"); cur=Search(head); DeleteNode(cur); PrintList(head); /*以只写的方式创建并打开文件,将以head为首节点的链表数据保存在文件list.data中*/ fp=File_CreateKeepOpen("list.data","w"); if(!fp){ printf("File_CreateKeepOpen Failed\n"); return -1; } /*将以head为首节点的链表数据保存在文件list.data中*/ size=File_SaveNodeData(fp,head); /*关闭文件list.data*/ if(!File_Close(fp)){ printf("File_Close Failed\n"); return -1; } /*以只读的方式打开文件list.data*/ fp=File_CreateKeepOpen("list.data","r"); if(!fp){ printf("File_CreateKeepOpen Failed\n"); return -1; } /*从文件list.data中读取链表数据,并返回新的链表头节点File_head*/ File_head=File_ReadNodeData(fp); if(File_head!=NULL){ printf("File_ReadNodeData Result:"); PrintList(File_head); } /*关闭文件list.data*/ if(!File_Close(fp)){ printf("File_Close Failed\n"); return -1; } /*根据新的链表头File_head,插入新的数据并打印*/ cur=Search(File_head); if((ret=InsertNode(cur))!=NULL) PrintList(File_head); else printf("InsertNode Failed\n"); return 0; }
以下是测试过程:
相关文章推荐
- C++primer plus第六版课后编程练习答案4.8
- C++primer plus第六版课后编程练习答案4.7
- C++虚函数表解析
- C++基础之快速排序实现(面试常考)
- C语言的负数扩充
- 【C++ Primer 学习笔记】: 容器和算法之【泛型算法】
- 【C++ Primer 学习笔记】: 容器和算法之【关联容器】
- Net调用非托管代码(P/Invoke与C++InterOP) [转]
- C语言中关于float、double、long double精度及数值范围理解
- C++构造函数的默认参数
- heap
- Find the k-th smallest element
- 二维数组运算符重载
- JNA调用C语言动态链接库学习实践总结(指针模拟)
- 关于C语言中的未初始化变量的值
- 玩坏你的C盲舍友哦
- 在C++/CLI的本地类中包含托管类成员变量的方法
- c++时间类型详解 time_t
- C语言--顺序表
- C++中标准输入流cin与Ctrl+Z使用时的问题