赞
踩
第一次学数据结构的时候,c语言基础实在太差,在尝试用c语言来实现的时候一直碰壁,数据结构书里的代码是伪代码,使用的时候各种内存访问冲突,各种锟斤拷烫烫烫,各种变量级别不同。于是我又重新学了一遍c语言,并且又看了一本详细阐述c语言指针有关的书,终于明白了,以前为什么出那么多错。之前一直出错的本质就是没有理解指针。学完了以后,现在已经可以成功使用C语言实现数据结构中的算法了。分享一下我的学习成果,希望和我一样的人也能少走弯路。
我想在函数内完成我想要的操作,之前一直用的是一级指针,可是每次创建完的链表都会内存访问冲突。后来经过深入的学习,我发现了这个错误的本质和c语言中经典的用swap函数交换两个变量的值到主函数中没有交换的错误是一样的。然后我就采用了二级指针,解决了这个问题。
代码中LinkList *Head等价于LNode **Head,
LinkList head等价于LNode *head。为了便于区分我将二级指针中的头节点的首字母进行了大写。
下面是头文件“链表.h”,主要放了链表结构的声明以及相关函数的声明。
- //蔚明
- //链表.h
-
- /*我想在函数内完成我想要的操作,之前一直用的
- 是一级指针,可是每次创建完的链表都会内存访问
- 冲突。后来经过深入的学习,我发现了这个错误的
- 本质和c语言中经典的用swap函数交换两个变量的值
- 到主函数中没有交换的错误是一样的。然后我就采用
- 了二级指针,解决了这个问题,代码中多次用到二级指
- 针。
- 代码中LinkList *Head等价于LNode **Head,
- LinkList head等价于LNode *head。
- 为了便于区分我将二级指针中的头节点的首字母进行了大写。*/
-
- #ifndef 链表_H
- #define 链表_H
-
- #pragma warning(disable:6031)//在visual studio中使用字符串函数,输入函数时
- #pragma warning(disable:4996)//会报错(C4996,C6031),使用这两行代码以后可以正常使用。
- //也可以"字符串函数名_s()"的方式使用函数,但需要更多参数。
-
- #include "stdio.h"
- #include "stdlib.h"
-
- #define OK 0
- #define FAIL -1
- #define ENTER printf("\n");
-
- typedef int Elemtype;//可将"int"更换为任意类型变量。
-
- typedef struct LNode//定义一个链表的结构类型。
- {
- Elemtype data; //结点的数据域。
- struct LNode* next; //结点的指针域。
- }LNode, * LinkList;
-
- int Init_List(LNode** L);//使用这个函数来初始化一个节点。运行成功返回OK。
-
- int Assign_List(LNode** L);//为结点数据域赋值,成功返回OK,此函数可按需改变。
-
- void Get_List(LNode* p, Elemtype* e);//取结点p的数据域。此函数可按需改变
-
- void Print_List(Elemtype e);//打印数据域内容。
-
- int Creat_List(LinkList* Head);//使用这个函数来创造一个链表,运行成功返回OK.
-
- void Read_List(LinkList head);//遍历链表
-
- LNode* Search_List(LinkList head, Elemtype e);//在链表中查找第一个含有元素e的节点,并返回这个结点的地址。
-
- int Insert_List(LinkList* Head, Elemtype e);//在第一个含有元素e的结点后面插入一个新的结点,成功返回OK。
-
- int Delete_List(LinkList* Head, Elemtype e);//删除第一个含有元素e的节点。
-
- void Destroy_List(LinkList* Head);//销毁整个链表。
-
- #endif
在编写这个代码时,注释中有此函数可按需更改。意思是更改这个函数。可以对不同的数据域进行操作。以便于快速的修改整个程序代码。创造不同的链表的目的。
下面是源文件“链表.c”,主要放了链表相关函数的定义。
- //蔚明
- //链表.c
-
- /*在编写这个代码时,注释中有此函数可按需更改。
- 意思是更改这个函数可以对不同的数据域进行操作。
- 以便于快速的修改整个程序代码。创造不同的链表的目的。*/
-
- #include "链表.h"
-
- int Init_List(LNode** L)//使用这个函数来初始化一个节点。运行成功返回OK。
- {
- if ((*L = (LNode*)malloc(sizeof(LNode))) == NULL) //为结点分配内存
- {
- puts("内存分配失败!!!");
- exit(1);
- }
- else return OK;
- }
-
- int Assign_List(LNode** L)//为结点数据域赋值,成功返回OK,此函数可按需改变
- {
- Elemtype data;
- puts("请为结点赋值");
- scanf("%d", &data);
- (*L)->data = data;
- return OK;
- }
-
- void Get_List(LNode* p, Elemtype* e)//取结点p的数据域。
- {
- *e = p->data;
- }
-
- void Print_List(Elemtype e)//打印数据域内容。此函数可按需改变
- {
- printf("%d ", e);
- }
-
- int Creat_List(LinkList* Head) //使用这个函数来创造一个链表,运行成功返回OK.
- {
- puts("正在创建一个链表。");
-
- //创建一个只有头结点的空链表。
- if (Init_List(Head) != OK) exit(1);
- (*Head)->next = NULL;
-
- LNode* r = *Head; //尾指针初始化并指向头结点。
-
- char over_symbol = '#'; //结束创建的标志。
-
- while (1==1)
- {
- //生成新的结点p。
- LNode* p;
- if (Init_List(&p) != OK) exit(1); //初始化结点p。
- Assign_List(&p); //为新结点数据域赋值。
- p->next = NULL;
-
- r->next = p; //把新结点插到尾结点之后。
- r = p; //尾指针指向新结点。
-
- //如果没有getchar()吃掉回车,会直接跳过下面的scanf()函数。
- puts("结束请输入:over,否则请按<Enter>");
- getchar();//吃掉回车。
- scanf("ove%c", &over_symbol); //当输入"over"时跳出循环。
- getchar();//吃掉回车。
-
- if (over_symbol == 'r') break;//判断是否满足结束条件。
- }
-
- puts("创建成功");
- return OK;
- }
-
- void Read_List(LinkList head)//遍历链表
- {
- LNode* read = head->next; //定义一个读取数据域的指针。
- Elemtype e; //定义一个保存数据域的变量
-
- //寻链向下进行操作。
- while (read != NULL)
- {
- Get_List(read, &e); //把结点数据域内容读到变量e
- Print_List(e); //打印数据域内容
- read = read->next;
- }
-
- ENTER;
- }
-
- LNode* Search_List(LinkList head, Elemtype e) //在链表中查找第一个含有元素e的节点,并返回这个结点的地址。没找到返回NULL。
- {
- LNode* read = head->next; //定义一个读取数据域的指针。
-
- while (read != NULL) //在到达尾结点之前一直循环。
- {
- if (read->data == e) return read; //如果找到就返回,否则寻链向下。
- else read = read->next;
- }
-
- return NULL; //到了为结点还是没找到,返回NULL。
- }
-
- int Insert_List(LinkList* Head, Elemtype e) //在第一个含有元素e的结点后面插入一个新的结点,成功返回OK.
- {
- puts("\n正在插入一个结点");
-
- LNode* read = (*Head)->next; //定义一个读取数据域的指针。
- LNode* pre_read = *Head; //定义一个指向读取数据域的指针的前驱的指针。
- LNode* temp = NULL;
-
- //查找需要操作的结点。
- while (read != NULL) //在到达尾结点之前一直循环。
- {
- if (read->data == e) break; //如果找到就返回,否则寻链向下。
- else
- {
- read = read->next;
- pre_read = pre_read->next;
- }
- }
- if (read == NULL) return FAIL; //到了为结点还是没找到,返回。
- temp = read; //使用temp临时保存要 操作的结点
-
- LNode* p = NULL; //生成新的结点p。
- if (Init_List(&p) != OK) exit(1); //初始化结点p。
- Assign_List(&p); //为新结点数据域赋值。
- p->next = temp; //将新结点与原结点后继相连。
-
- pre_read->next = p;//把原结点的前驱的后继与新结点相连。
-
- puts("插入成功");
- return OK;
- }
-
- int Delete_List(LinkList* Head, Elemtype e) //删除第一个含有元素e的节点,成功返回OK。
- {
- puts("\n正在删除一个结点");
-
- LNode* read = (*Head)->next; //定义一个读取数据域的指针。
- LNode* pre_read = *Head; //定义一个指向读取数据域的指针的前驱的指针。
- LNode* temp = NULL;
-
- //查找需要操作的结点。
- while (read != NULL) //在到达尾结点之前一直循环。
- {
- if (read->data == e) break; //如果找到就返回,否则寻链向下。
- else
- {
- read = read->next;
- pre_read = pre_read->next;
- }
- }
- if (read == NULL) return FAIL; //到了为结点还是没找到,返回。
- temp = read->next; //使用temp临时保存要 操作的结点的后继。
-
- free(read); //释放结点的空间。
-
- pre_read->next = temp; //把此结点前驱和后继相连。
-
- puts("删除成功");
- return OK;
- }
-
- void Destroy_List(LinkList* Head) //销毁整个链表。
- {
- puts("\n正在销毁一个链表");
-
- LNode* p = *Head; //定义一个访问链表结点的指针。
- LNode* temp = p->next; //使用temp临时保存要操作的结点的后继。
-
- //寻链向下,依次释放内存。
- do {
- free(p); //释放结点的空间。
- p = temp; //结点指向它的后继。
- temp = p->next;
- }
- while (temp != NULL);
-
- puts("销毁成功");
- return;
- }
下面是源文件“main.c”,主函数在这个文件中,主要是测试链表有关函数的功能是否正常。
- //蔚明
- //main.c
-
- #include"链表.h"
-
- void main()
- {
- LinkList head = NULL;
- Elemtype e = 0;
- if (Creat_List(&head) != OK)//创建一个头结点为head的链表。
- {
- puts("创建链表失败!!!");
- exit(1);
- }
- Read_List(head);//读取链表的数据域并打印。
-
- puts("请输入那个结点前需要插入");
- scanf("%d", &e);
- if(Insert_List(&head, e) != OK)//在链表中指定的结点前插入元素e
- {
- puts("插入失败!!!");
- exit(1);
- }
- puts("插入后的链表");
- Read_List(head);//读取链表的数据域并打印。
-
- puts("请输入那个结点需要删除");
- scanf("%d", &e);
- if (Delete_List(&head, e) != OK)
- {
- puts("删除失败!!!");
- exit(1);
- }
- puts("删除后的链表");
- Read_List(head);//读取链表的数据域并打印。
-
- Destroy_List(&head);//释放为这个链表申请的内存。
-
- return;
- }
运行结果如下:
我把Elemtype修改成了一个有名字,年龄的结构体变量,然后略微修改了对变量赋值的函数以及查找该变量的条件判断语句,部分修改如下
- //Elemtype由int变为如下
- typedef struct person
- {
- char name[10];
- int age;
- }Elemtype;
-
-
- //变量的赋值和呈现进行修改
- int Assign_List(LNode** L)//为结点数据域赋值,成功返回OK,此函数可按需改变
- {
- Elemtype data={"",0};
- puts("请输入名字");
- scanf("%s", data.name);
- getchar();
- puts("请输入年龄");
- scanf("%d", &data.age);
- (*L)->data = data;
- return OK;
- }
-
- void Print_List(Elemtype e)//打印数据域内容。此函数可按需改变
- {
- printf("姓名:%s,年龄:%d\n", e.name,e.age);
- }
-
- //略微修改插入和删除函数中的判断语句
-
- if (read->data == e) break; //改为if (strcmp(read->data.name, e.name)==0) return read;
-
- //略微修改主函数中的赋值语句
-
- scanf("%d",&e);//改为 scanf("%s", e.name);e.age = 0;
得到的运行结果如下
我学C语言大概半年,编写代码的手段可能比较稚嫩,如果写的比较垃圾,欢迎前辈指正。
创作不易,求点赞。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。