程序人入行第一篇
第一章:单链表篇
前言
在开始之前我想问大家一个问题:谈到链表,你想到了什么?先思考三秒。 ……
嗯!我想,你心中已经有了一个答案。而我的答案是——指针。
我认为链表是普通人从普通过渡到一萌新的第一个阶段。这里说的普通人指的是那些会能够理解基础程序语法和逻辑的人,但是止步于指针的人,相信大多数初学者都是这种状态,害怕理解指针或者理解了指针但是写程序总是不用。
当然,上边这些只代表我的个人观点,也代表了我的历程。我依稀记得我第一次接触链表的时候,大受震撼,脑子混隆隆一下就炸开了——原来程序还能这样写,小新我跪了。
在我看来,链表,他的重要性不亚于炒菜时厨师放的油盐酱醋,想要跻身程序员一行,学会链表不可或缺。
废话到此为止,下面我们直接上程序。
温馨提示:关注并私信小新,免费得源码……
一、确定框架
作为一个优秀的成员,写任何一个程序之前首先要确定基本的框架,这将决定我们的程序走向。
温馨提示:如果看不懂下发的框架,建议小萌新们可以先了解下链表的原理。
二、展开框架
1.定义结点
代码如下(示例):
温馨提示:手动敲写代码快速学会链表
2.创建一个结点
写这个代码前,我们首先要确定两个地方
1:函数返回值
2:函数参数表
具体怎么操作还得看你自己的想法,本程序以传入地址,数据,返回空为例。
//创建一个结点
void createNode(List *node,int a)
{
List p = calloc(1,sizeof(mallocnode));//申请一个内存空间,大小为sizeof(mallocnode);
p->data = a;//传入数据
p->next=*node;//指向头结点
*node=p;//把结果传给头结点
return;
}
//主函数中的使用方式
<注意传入参数:node为指针,a为int类型变量>createNode(&node,a);
赶紧手动敲一敲吧!
3:打印结点
写之前干什么?老两步——思考返回值,思考参数表,给大家三秒钟思考一下
void printfNode(List node)
{
List p = node;
int i=0;
while(p)//当前结点的地址为空的时候,结束循环
{
printf("node->data[%d]=%d\n",i++,p->data);
p=p->next;//循环结束进入下一个结点
}
if(i==0)printf("空链表");
return;
}
//下面是主函数中的使用
createNode(&node,1);
createNode(&node,2);
createNode(&node,3);
createNode(&node,4);
createNode(&node,5);
printfNode(node);//打印之前创建的五个结点内容
4:删除
这里依然是老二步:确定返回值,确定参数表
在写之前,我们在大脑中回忆一下链表的存储结构
现在,我们大致有了一个思路,如果要删除结点2,只需要让指向结点2的箭头跳过2直接指向结点3即可。
那么这个箭头是什么?
没错,是指针p->next,只需要让结点1的p->next指向结点3的p,然后让结点2的p->next断开和3的链接就OK了。 因为链表是单向的,所以,在这个过程中,我们必须用在用一个指针记住结点1的地址 [原理:p,p->next是两个地址,但是图中有三个地址]。
理论结束,实验开始
//删除当前结点
void deleteNode(List *node,int a)
{
List p = *node;//当前指针位置
List q=NULL;//记录前一个结点的地址
if(*node==NULL)//判断是空链表直接退出,提高程序运行速度
{
printf("这是一个空链表");
return;
}
while(p)
{
if(p->data==a)//找到了
{
if(p==*node)//如果当前位置是头结点的话,q为空,单独处理
{
if(p->next!=NULL)//表示当前链表不止一个结点
{
(*node)=(*node)->next;//头结点指向第二个结点的地址
p->next=NULL;//把头结点的箭头置空
}
free(p);
p=NULL;
}
else
{
if(p->next==NULL)//判断是不是最后一个结点
{
q->next=NULL;
}
else
{
q->next=p->next;//第一个结点的箭头指向第三个结点的箭头
p->next=NULL;//打断当前结点的箭头
}
free(p);
p=NULL;
}
return ;//一个执行完毕需要退出,原因是已经操作了p.和q,接线来不能继续指向
}
q=p;//每一次,记住前一个位置的地址
p=p->next;
}
printf("没有找到");
return;
}
//主函数部分的使用
int main(int argc, char* argv[])
{
int a=5;
for(int i=5;i>=0;i--)
{
createNode(&node,i);
}
printfNode(node);
deleteNode(&node,2);
printfNode(node);
return 1;
}
5:排序
链表的排序一般采用插入法:这就需要再新建一个链表,不了解原理的同学可先去百度一下插入法排序的原理。
这里我就不多说了,直接上程序:
//排序
void rankNode(List *node)
{
List p = *node;
List New=NULL;//新链表
if(p==NULL)
{
printf("空链表哦");
return;
}
while(*node)//排序一定会遍历链表
{
p=*node;
(*node)=(*node)->next;
p->next=NULL;
rank(p,&New);//将当前结点扔进单独的函数进行排序
}
*node=New;
return;
}
void rank(List p,List *new)
{
List pNew= *new;
List pAnd = NULL;//记录当前位置
while(pNew)
{
if(pNew->data>p->data)//发现了第一个他大的值
{
if(pNew==*new)//第一个位置就发现了,用头插法
{
*new=p;//给头指针赋值传入内容
}
else
{
pAnd->next=p;
}
p->next=pNew;
return;
}
pAnd=pNew;
pNew=pNew->next;
}
if(*new==NULL)
{
*new=p;
}
else//没有在循环中找到,直接插在最后一个
{
pAnd->next=p;
}
return ;
}
//这里是用了两个函数来来完成任务,下面是主程序的使用
int main(int argc, char* argv[])
{
int a=5;
createNode(&node,2);
createNode(&node,1);
createNode(&node,6);
createNode(&node,4);
createNode(&node,7);
printfNode(node);
rankNode(&node);
printfNode(node);
return 1;
}
修改结点
相对来说,修改结点就比较简单了,大家可以不看教程自己试一试,整个过程还是老二步骤:
返回值,参数列表,其实这里还应该多加一个步骤,梳理实现功能。
void setNode(List *node,int a)
{
List p = *node;
int setNumber=0;
while(p)
{
if(p->data==a)
{
printf("修改当前%d值为:",p->data);
scanf("%d",&setNumber);
p->data=setNumber;
}
p=p->next;//这个移动到下一结点的命令相信大家已经都能看懂了吧
}
return;
}
//主函数的使用
int main(int argc, char* argv[])
{
int a=5;
createNode(&node,2);
createNode(&node,1);
createNode(&node,6);
createNode(&node,4);
createNode(&node,7);
printfNode(node);
setNode(&node,1);
printfNode(node);
return 1;
}
查询一个结点
学会了前面的,这个再简单不过了
//查询一个结点
void seek(List node,int a)
{
List p = node;
while(p)
{
if(p->data==a)
{
printf("数据为:%d,地址为%p",p->data,p);
}
p=p->next;
}
return ;
}
//主函数中的使用
int main(int argc, char* argv[])
{
int a=5;
createNode(&node,2);
createNode(&node,1);
createNode(&node,6);
createNode(&node,4);
createNode(&node,7);
printfNode(node);
seek(node,6);
return 1;
}