第一次学数据结构的时候,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语言大概半年,编写代码的手段可能比较稚嫩,如果写的比较垃圾,欢迎前辈指正。
创作不易,求点赞。