作为一个编程小白,在这里发表自己的第一篇博客,心里还是有点紧张。
写博客的目的主要还是在于让自己对所学的知识进行充分的掌握,企图在写分析的过程当中可以达到“柳暗花明又一村”的境界,当然如果可以帮助到别人的话自己心里也肯定有辣么一点点成就感。
因为刚接触到链表,所以今天就想和大家一起探讨一下链表。想弄清楚链表,对指针的概念一定要很清楚。我们都知道,链表是由一个又一个节点构成,每个节点又分为数据域和指针域(eg:p->data/p->next)。
举一个简单的例子来描述节点:
这是一个简单的线性表的节点(无序的),当我们把它整理成一个又一个的节点是它是这样的:
画的不是很好看,但应该很好理解吧。图一中数据域就相当于p->data,指针域相当于p->next。
假设p是指向线性表中的第i个数据元素(a(i))的指针,那么p->next就是指向第i+1个数据元素,即a(i+1)。也就是说,p->data=a(i),p->next->data=a(i+1)
简单搞清楚指针的用法后我们来完成一个动态单链表的创建(键盘输入)。如果还是没有搞懂的同学一定要多画几次图来理解,因为博主也是一个小白,可能讲的不是很清楚。
我们先来分析一下,第一步,我们先写头文件,然后定义结构体变量
#include
#include
typedef struct node{
int data;
node *next;
}node;
接下来就可以写创建单链表的函数了,我用的是尾插法
node *create(){//尾插法创建链表
node *p,*q,*head;//定义三个指针,其中p类似一个辅助指针吧,用来存放所输入的链表数据
int n;//表示想要输入的链表数据个数
int i = 0;//表示已经输入的链表数据个数
head = (node*)malloc(sizeof(node));//创建头节点
head->next = NULL;//可以理解为初始化链表,让它为空表
q = head;//q始终指向尾节点,开始时头节点和尾节点时同一个
printf("你想要输入多少个数据:");
scanf("%d",&n);
printf("请输入数字:");
for(i = 0;i < n;i++){
p = (node*)malloc(sizeof(node));//创建数据节点,让输入的数据先存放在p里面
scanf("%d",&p->data);// 输入链表数据
p->next = q->next;
q->next = p;
q = p;
}
return head;
}
创建了单链表,当然得让它输出了,接下来写一个输出函数
void print(node *head){//输出链表
node *p;
p = (node*)malloc(sizeof(node));//给p分配内存空间,使它可以存放数据
p = head->next;//让p指向开始节点
printf("输出单链表里的所有元素:");
while(p != NULL){//当p不是空表时执行下面语句
printf("%d ",p->data);
p = p->next;
}
}
计算单链表的长度
int ListLength(node *head){//计算单链表的长度
printf("\n");
node *p = head;//让p指向头节点
int i = 0;
while(p->next != NULL){//当p的后继节点不为空时执行下面语句
p = p->next;
i++;//执行一次循环加1,直到p的后继节点为空时跳出循环
}
printf("单链表的长度:%d",i);
return 0;
}
按照数据值查找(数据n所在的位置是第几个)
int LoacteElem(node *head){//按元素值查找
printf("\n");
node *p = head;//让p指向头节点
int j = 0,e;
printf("输入要查找的数据值:");
scanf("%d",&e);
while(p->next != NULL && p->data != e){
p = p->next;
j++;
}
printf("查找的数据的位置:%d",j);
return 0;
}
按照数据的位置查找(第n个位置的数据为)
int GetElem(node *head){//查找元素
printf("\n");
int i,e,m;//i的作用相当于指针遍历到第几个节点、e表示想要查找第几个位置的元素、m表示找到想要查找的数据的值)
node *p = head;//同样的套路
printf("请输入要查找数据的位置:");
scanf("%d",&e);
for(i=0;i<e;i++){
p = p->next;
}
m = p->data;
printf("位置为%d的数据是%d",e,m);
return 0;
}
接下来是插入函数
node *Insert(node *head){//插入元素
printf("\n");
node *p = head;
node *q;
int j = 0,i,e;//同样,j表示指针p遍历到链表数据的位置,i、e表示要在第i个位置插入数据e)
printf("输入要插入的元素的位置:");
scanf("%d",&i);
printf("输入要插入的元素的值:");
scanf("%d",&e);
for(j = 0;j < i-1;j++){
p = p->next;
}
q = (node*)malloc(sizeof(node));
q->data = e;//最后几步大家如果理解不了可以试着画图,多画几次,搞清楚数据域和指针域到底指的是什么就变得很简单了
q->next = p->next;
p->next = q;
return head;
}
然后是删除函数
node *Delete(node *head){//删除元素
printf("\n");
node *p = head;//老套路
node *q,*r;
int j = 0,i;//j表示指针p遍历到链表的第几个数据,i表示删除第i个位置的数据
printf("请输入删除数据的位置:");
scanf("%d",&i);
for(j = 0;j < i-1;j++){
p = p->next;
}
q = (node *)malloc(sizeof(node));
q = p;
r=q->next;
p->next=r->next;
return head;
}
然后是主函数
int main(){
node *head;
head = create();//调用创建链表函数(尾插法)
print(head);//调用输出函数,输出链表的所有数据
ListLength(head);//求链表的长度
LoacteElem(head);//按数据值查找(第n个数据的位置是第m个)
GetElem(head);//查找数据(第n个位置的数据是第m个)
Insert(head);//调用插入函数
print(head);//输出插入后的链表
Delete(head);//调用删除函数
print(head);//输出删除后的链表
printf("\n");
return 0;
}
最后,总结部分:以上函数按顺序添加至编译器(Dev-C++和vs2019)中可以正常运行,但是根据vs2019编译器的规则需将(”scanf“改为”scanf_s“),而且会出现警告(不是错误是警告)(eg:取消NULL指针对p的应用),该解决方法是在出现问题的函数中添加
if(p == NULL){
return 0;
}
该警告的出现是基于vs编译器的严谨,如果不添加该代码块那么万一p为空表的话运行将会出现问题(vs知道p是空表,它不知道应该运行出什么样的结果)但是我们知道我们运行的时候一定会输入数据,所以它不会为空。(ps:vs好严谨)
不足之处:不能实现删除重复项以及排序问题
自我评价:个人感觉代码没有讲的很清楚,因为插入和删除应该再仔细讲讲,但是由于自身也是一个小白,很多东西可以用嘴说出来,写却不知道怎么写,如果大家把开头的两幅图搞明白的话,写这些函数的时候多画图理解,根据自己的想法写代码,不要按照书上或者网上的代码按部就班,多练习几次就会很熟悉了。
个人心得:这次写博客让我自己对指针的理解确实是提升了一个档次(不是瞎说),写之前脑海里有画图的概念,却不知道怎么画。在写的过程中因为要将这些东西清楚的说明出来,那么自己一定要很熟悉。于是就多看书,查资料,在上传每一段代码前自己又在本子上过了一遍,确认无误后才复制上去。所以还是建议大家对于自己不会的东西一定要持之以恒,花时间搞清楚它,当自己真的从朦胧到熟悉,那种感觉真的很舒服。
最后,就希望路过的大佬可以提一点建议,也希望许多和我一样的小萌新可以共同努力,一起在编程这条路”走到黑“