我写的这些都是我自己学习的时候所掌握的知识点,以及自己时常写代码留下的注释,最后加上了一些个人发现同学的一些问题,索性发来出来
#include <stdio.h>//标准输入输出头文件
#include <stdlib.h>//用于使用malloc函数的头文件
//这是typedef标识符用法,这个node我们不用,只做讲解,
/*typedef struct node{
//数据域
int m_id;
int m_money;
//指针域
struct Node *next;
}*linklist,node;
这样写相当于给node起了个小名,这个小名天生就是指针型标识符,
比如linklist p就相当于node *p;因此我们可以起个小名叫node,
这样就不用每次写struct node a;来定义了。直接node a;就行 */
//结构体和类名的首字母大写,这是规范,c语言是区分大小写的
//结点结构体
typedef struct Node{
//数据域
int m_id;
int m_money;
//指针域
struct Node *next;
}Node,*linklist;
//第一个函数,先来初始化链表
void Init_linklist(linklist test){
/*先来讲一下新建结点,(Node*)的意义有两层,
一是告诉malloc,按Node结构体的样子分配内存,
二是把malloc返回的地址强制转换成Node*的类型
sizeof是测出Node的长度,如果你知道string并打算在结点里使用
那么最好学会c++的new,要不然在添加结点的时候,程序时常崩溃
不报错的那种崩溃*/
test = (Node *)malloc(sizeof(Node));
//给新建结点的指针域归为null;一定要初始化,要不然会成野指针的
test->next=NULL;
}
//添加结点
void Add_node(linklist test){
//一个结点的指针,依据它来添加元素
linklist temp;
//初始化一个p,让他和test相同,避免操作test
linklist p=test;
//分配内存给temp
temp=(linklist)malloc(sizeof(Node));
//接下来可以先输入,也可以吧temp安放在末端位置,我用switch来分开两种情况
//其实没有区别
int sel=0;//select的简写
printf("请输入选择(1-先输入 2-后输入):");
scanf("%d",&sel);
switch(sel)
{
case 1: {
//这个是先输入
printf("输入数据\n");
printf("请输入id:\n");
scanf("%d",&temp->m_id);
printf("请输入money:\n");
scanf("%d",&temp->m_money);
printf("输入完成\n");
system("pause");//按任意键继续的函数
while(p->next)//p的next为空指针时结束循环
{
p=p->next;//使p移向链表末端
}
printf("移动指针完成\n");
system("pause");//按任意键继续的函数
//连接新结点
p->next=temp;
//把新结点的next置空;防止野指针出现
temp->next=NULL;
break;//防止你们跑了发现问题,我来个跳出
}
case 2: {
//这个是先判断
while(p->next)//p的next为空指针时结束循环
{
p=p->next;//使p移向链表末端
}
printf("移动指针完成\n");
system("pause");//按任意键继续的函数
//输入数据
printf("输入数据\n");
printf("请输入id:\n");
scanf("%d",&temp->m_id);
printf("请输入money:\n");
scanf("%d",&temp->m_money);
printf("输入完成\n");
system("pause");//按任意键继续的函数
//连接新结点
p->next=temp;
//把新结点的next置空;防止野指针出现
temp->next=NULL;
break;//防止你们跑了发现问题,我来个跳出
}
}
printf("添加成功\n");
}
//用const是为了防止误修改,const的英语就是恒定的(adj);常数(n)
void Show_linklist(const linklist test)
{
//还是一样,不操控test,另来一个指针来移动
linklist p=test->next;
//这是打印的表头格式
printf("id\tmoney\n");
//用循环,直到末结点
while(p)
{
//打印数据
printf("%d\t",p->m_id);
printf("%d\n",p->m_money);
//每次循环完了,指针后移
p=p->next;
}
}
//删除函数
void Delete_node(linklist test)
//根据结构体的特点,可以设置多种删除
//比如按名字查找删除,按电话,按序号,按id,这里只有一个id
{ /*这里是双指针法,毕竟删除一个结点,需要把这个结点的
前后结点连接起来,保证链表依旧连贯,用一个指针指向删除结点的
前驱结点,把这个前驱结点的next指向另个一个指针
(这个指针指向删除结点) 的next,这样两个结点就连接起来了*/
linklist temp=NULL;
linklist p=test;
//为什么要来个Node t保存要查询的结点id?
/*因为实际运用可以有多种查询方式,而这些查询都会依据
Node的元素来查询*/
Node t;
//立个flag做标记
int flag=0;
//下面就开始查询了,查到了就删除,查不到就依据flag判断
printf("请输入删除人id:\n");
scanf("%d",&t.m_id);
//依旧遍历一下链表
while(p->next)
{
//因为上面我是将temp先置空,所以这里先让他指向第一个结点
temp=p->next;
//下面就是遍历判断了
if(temp->m_id==t.m_id)
{
//这不陌生把
printf("删除人id:");
printf("%d\t",temp->m_id);
printf("删除人的财产:");
printf("%d\n",temp->m_money);
printf("已删除此人!\n");
//查到了就修改flag
flag=1;
//更新结点连接
p->next=temp->next;
//释放被删除的结点
free(temp);
break;
}
//没有退出就移动指针
p=p->next;
}
//这里就是告诉你链表有没有这个结点,没有也会给个提示
if(flag==0)
{
printf("查无此人!\n");
}
}
//查找函数
void Query_node(linklist test)
{
//这里就是和上面一样的用法
linklist p=test;
Node temp;
int flag=0;
//查找开始
printf("请输入查找id:\n");
scanf("%d",&temp.m_id);
//select的简写
int sel;
//因为实际需要,一般都是查出全部的结点,但有时候只用查一个来减少时间
//毕竟查全部就相当于遍历全部结点,而查一个结点,可能查到半路就停了
printf("请输入查找第一个人还是查找全部人(1-单人 2-全部人):\n");
//我用Switch是为了方便后续添加功能
scanf("%d",&sel);
switch (sel)
{
//这两个的区别基本上只有是否用了break
case 1:while(p->next)
{
if(p->m_id==temp.m_id)
{
printf("id\tmoney\n");
printf("%d\t%d\n",p->m_id,p->m_money);
flag=1;
//查到一个就退出
break;
}
p=p->next;
}
case 2: {
while(p->next)
{ printf("id\tmoney\n");
if(p->m_id==temp.m_id)
{
printf("%d\t%d\n",p->m_id,p->m_money);
//写flag=1也可以,我是为了和上面来个区分
flag++;
}
p=p->next;
}
}
}
if(flag==0)
{
printf("查无此人!\n");
}
}
//清空函数
void Clear_linklist(linklist test)
{
printf("开始清空!\n");
//一样的双指针法,每个结点都要释放
linklist p1,p2;
p1=test->next;
while(p1->next)
{
p2=p1->next;
free(p1);
p1=p2;
}
//最后一定一定要给头结点的next置空,要不然会成为野指针的
//最后一定一定要给头结点的next置空,要不然会成为野指针的
//最后一定一定要给头结点的next置空,要不然会成为野指针的
test->next=NULL;
printf("已清空!\n");
}
//修改函数
//modify就是英文修改
void Modify_node(linklist test)
{
//这里就没有什么好说的了,都是上面讲过的
linklist p=test;
Node temp;
int flag=0;
printf("请输入修改人的id:\n");
scanf("%d",&temp.m_id);
while(p->next)
{
p=p->next;
if(p->m_id==temp.m_id)
{
printf("请输入被修改人的新信息:\n");
printf("请输入id:\n");
scanf("%d",&p->m_id);
printf("请输入money:\n");
scanf("%d",&p->m_money);
printf("修改完成!\n");
flag=1;
break;
}
}
if(flag==0)
{
printf("查无此人!\n");
}
}
//主函数,因为我的函数都在main前,所以不用声明
int main(){
//声明一个链表
linklist head;
//初始化
Init_linklist(head);
//告诉我成功了
printf("成功\n");
//添加函数
Add_node(head);
//打印函数
Show_linklist(head);
//删除函数
Delete_node(head);
//显示删除后的链表
Show_linklist(head);
//查找函数
Query_node(head);
//修改函数
Modify_node(head);
Show_linklist(head);
//清空链表
Clear_linklist(head);
Show_linklist(head);
system("pause");//按任意键继续的函数
return 0;
}
/*一些关于程序设计的命名规范,请务必了解学会,不要到了
学完了还只会用a,b,c命名,即使英语和我一样都很差,但也要
会用翻译软件啊
1,函数命名,首字母大写,前面加上功能,比如Add这样,可以用下划线"_",也可以不用
2,变量命名,需要大量使用的变量,比如
1.结构体和类成员 m_id
2.普通变量 i_sum,f_sum
3.全局变量 g_hight
4.常量 c_pai
5.静态变量 s_pai
3,宏定义的东西一般是全部大写 such as:#define MAX 1000
4,文件命名,单个文件的时候你写1.cpp这当然没问题,但是当你写多个文件的时候
还这样写怕不是要搞死人,一般大型程序里,各种定义声明是分开的,每个源文件名字都体现了功能*/
没有费心思去搞一个目录啥的,就是简单检查了一遍是否可以跑