//链表结构的数据格式存储
#include "stdafx.h"//把这行放在最开始。
#include<string.h>
#include<iostream>
using namespace std;
typedef struct
{
char key[10];
char name[20];
int age;
}Data;
typedef struct Node//这里与上面的不同是多了Node,注意如果没有这个Node,下面的struct Node*nextNode;就会报错说找不到这个Node。
{
Data nodeData;
struct Node*nextNode;//与上面的Node是配合用的。
}CLType;//这里的数据结构类型是CLType与上面的Data一样,而不该是Node!
CLType* CLAddEnd(CLType*head,Data nodeData);
void CLAllNode(CLType*head);
CLType*CLAddFirst(CLType*head,Data nodeData);
CLType*CLDeleteNode(CLType*head,char*key);
int CLLength(CLType*head);
CLType*CLInsertNode(CLType*head,char *findkey,Data nodeData);
CLType*CLFindNode(CLType*head,char*key);
int main()
{
CLType*node,*head=NULL;//将头结点初始化为NULL,事实上这里是指向空内存。
Data nodeData;
char key[10];
char findkey[10];
int len=0;
printf("链表测试。先输入链表中的数据,格式为:关键字 姓名 年龄\n");
do
{
fflush(stdin);
cin>>nodeData.key;
if(strcmp(nodeData.key,"0")==0)
{
break;//输入了0则跳出do while(1)循环
}
else
{
cin>>nodeData.name>>nodeData.age;
head=CLAddEnd(head,nodeData);//可以用指针作为左值,可见CLAddEnd(head,nodeData)必然是一个指针类型函数
}
}while(1);
CLAllNode(head);//链表都是从头结点开始一个个去寻找的。
printf("\n演示插入结点,输入插入位置的关键字:");
cin>>findkey;
printf("\n输入插入点的数据(关键字 姓名 年龄):");
cin>>nodeData.key>>nodeData.name>>nodeData.age;
head=CLInsertNode(head,findkey,nodeData);
CLAllNode(head);
printf("\n演示删除结点,输入要删除的关键字:");
fflush(stdin);
cin>>key;
head=CLDeleteNode(head,key);//注意不是CLType*head
CLAllNode(head);
printf("\n演示插入头结点,输入插入点的数据(关键字 姓名 年龄):");
fflush(stdin);
cin>>nodeData.key>>nodeData.name>>nodeData.age;
head=CLAddFirst(head,nodeData);
CLAllNode(head);
printf("\n演示计算结点长度:");
len=CLLength(head);
cout<<len;
return 0;
}
CLType* CLAddEnd(CLType*head,Data nodeData)//返回一个CTLype类型的指针
{
CLType*node,*htemp;//一个中间存储的指针node,一个暂时性头指针htmep;
if(!(node=(CLType*)malloc(sizeof(CLType))))//sizeof(CLType)计算CLType这种数据结构需要多少内存,malloc(sizeof(CLType)),从内存池里面给划出一块内存
//(CTLype*)初始化这块内存池,使之为CLType类型。node=(CTLype*)malloc(sizeof(CLType))把这一块内存池的地址赋给node;
//!(node=(CTLype*)malloc(sizeof(CLType)))如果node等于0的时候进入if而当没有分配到内存的时候,就node就为0.
//或者这里直接用if(!(malloc(sizeof(CLType))))就可以了。
{
printf("申请内存失败!\n");
return NULL;//这个NULL 是返回给CTLype*CLAddEnd(CTLype*head,Data nodeData)的,可以理解为这个函数一直在等一个返回值,如果现在返回了,有值了就跳出。
}
else//其实这里是等效于if((node=(CTLype*)malloc(sizeof(CLType))))就是说能到这里说明malloc分配到了一块内存,并且这块内存叫node;
{
node->nodeData=nodeData;//函数第二个参数传递到这里
node->nextNode=NULL;//因为这里是在最后添加的结点,所以这应该是最后一个结点,下一结点的地址理应是指向NULL
if(head==NULL)//说明这个head来的时候,指向的是NULL 也就是它是头指针
{
head=node;//现在开始head有跟班了,它指向的地址是node不再是NULL,而node指向的是NULL;
return head;//一直在等待返回一个地址值
}
//重要的代码,演示如何查找响应的信息。
htemp=head;//让htemp来取代head,其实这里是等同于b=3;a=b;作用当head是头结点但不尾结点,也就是这不是空链表的时候。那么就该找到最后一个结点,标志就是nextNode=NULL
while(htemp->nextNode!=NULL)//查找尾结点,当nextNode指向的不是NULL的时候,就不是尾结点,为了要继续查找,所以让遍历一般
{
htemp=htemp->nextNode;//把htemp指向的下一节点的地址赋给它本身,也就是约等于++htemp一样,指针往后移一个单位。
}
htemp->nextNode=node;//node是现在存放着尾结点的新添加结点,要把node所代表的地址传给原来链表的尾结点并取代掉所指向的地址NULL,
//切注意这里不能用htemp=node,如此会是把原来的最后一个结点丢失,变成把node的地址传给原来的倒数第二个结点的nextNode
return head;//还是要返回头结点,链表的特性就是要知道任何一个结点的位置都要从头结点开始,如此就需要返回头结点。
}
}
void CLAllNode(CLType*head)
{
CLType*htemp;
htemp=head;//这里只是给以了头结点
//while(htemp=htemp->nextNode)//这样会导致头结点不能被打印出来,因为一进来就是把第二个结点的地址发给htemp了,而头结点已经被过掉了.
//{
// printf("结点(%s,%s,%d)\n",htemp->nodeData.key,htemp->nodeData.name,htemp->nodeData.age);
//}
while(htemp)
{
printf("结点(%s,%s,%d)\n",htemp->nodeData.key,htemp->nodeData.name,htemp->nodeData.age);
htemp=htemp->nextNode;//把下结点的地址给htemp,但尾结点的地址不是NULL,所以能把尾结点打印出来,但打印出来后就结束了。
}
}
//插入结点
CLType*CLInsertNode(CLType*head,char *findkey,Data nodeData)//插入结点,但要先分配内存,和查询是不是有这个结点,有这个结点时候再移动。
{
CLType*node,*nodetemp;
if(!(node=(CLType*)malloc(sizeof(CLType))))
{
printf("申请内存失败!\n");
return 0;//这个情况下,整个函数到此结束 ,不再往下进行。事实上我觉得这里应该是要返回head
}
node->nodeData=nodeData;//把新添加的数据放到新增加的内存的数据域里面(能运行到这里说明已经分配好内存了),
//对于node这个内存而言,有两个域一个是放数据,一个放下一节点的地址,现在已经放了数据,但地址域还是空的
nodetemp=CLFindNode(head,findkey);//上面步骤已经完成了申请内存和把数据存入新增加的内存中。接下来是要把这个内存放到链表中。
if(nodetemp)//说明不为NULL 的情况下,那么就是找到了该节点,接下来就是要把node的地址放到nodetemp的nextNode中。注意node的地址域还是空的
{
node->nextNode=nodetemp->nextNode;//因为node是新申请的内存,其中node->nextNode还是空的,可以用来存放地址,
//而且它现在插入来就是要取代tempnode的位置,原来由nodetemp->nextNode存放的地址现在该是转给它了。
nodetemp->nextNode=node;//经过上一步,nodetemp->nextNode已经空了,而且它现在要做node前面的结点,
//那么nodetemp->nextNode就该保存的是node的地址,注意千万不要把node与node->nextNode搞混淆
//node代表的是当前结点的地址,而node->nextNode代表的是存放在node里的下一节点的地址
}
else//如果说没能找到这个结点
{
cout<<"未能找到该结点";
free(node);//因为没有找到能插入的结点,数据无法插入,所以由上面申请到的结点该被释放掉。
}
return head;//返回头指针,
}
//查找结点
CLType*CLFindNode(CLType*head,char*key)//从头开始查找,查找无法是找到了,那么返回该结点的地址,要么没有找到返回一个NULL 或者是0
{
CLType*htemp;//一般内部都是需要再定义一个临时指针,用来作为循环用
htemp=head;//可以理解为初始化int a=3;
while(htemp)
{
if(strcmp(htemp->nodeData.key,key)==0)//注意这里后面的key不用“key”是因为这里都是形参不是直接找“abs”之类
{
return htemp;
}
//能到这里说明strcmp(htemp->nodeData.key,key)已经搜过了,但不对。
htemp=htemp->nextNode;//常用的循环方法,可以把htemp->nextNode去覆盖掉htemp里面的nextNode地址值,这样就能保证一直往下搜索
}
return NULL;//如果不能从if里面返回,那么到这里就说明没找到,要返回NULL
}
//删除结点
CLType*CLDeleteNode(CLType*head,char*key)//要删除结点,首先得确定有没有这个结点,有这个结点的情况下,
//把它删除后,那么它保存的nextNode要给到他前面的结点,如果可以直接覆盖那么就不需要再申请指针。但是要循环就得暂时指针
{
CLType*htemp;
CLType*node;
htemp=head;
node=head;
while(htemp)
{
if(strcmp(htemp->nodeData.key,key)==0)//注意这里后面的key不用“key”是因为这里都是形参不是直接找“abs”之类
{
node->nextNode=htemp->nextNode;//找到了这个结点,就是htemp,但是不能直接删除,要先把保留在它那里的地址拿出来。存放中中间量中
//真的从这里看出来,node=head只是初始化,而且可以被覆盖。而且如果第一次就进来只能说明
free(htemp);
return head;
}
node=htemp;//不断更新htemp前结点,这里是用node来存放要删除结点前面的一个结点的。
htemp=htemp->nextNode;
}
return head;
}
//计算链表长度
int CLLength(CLType*head)//拿一个来计算,然后遍历一遍就好了
{
CLType*htmep;
int i=0;
htmep=head;
while(htmep)
{
i++;
htmep=htmep->nextNode;
}
return i;
}
//插入头结点
CLType*CLAddFirst(CLType*head,Data nodeData)//插入头结点,那么就需要先分配一块内存,然后再把头结点的内存给到它就好了。
{
CLType*node;
if(!(node=(CLType*)malloc(sizeof(CLType))))
{
printf("申请内存失败!\n");
return NULL;//这个情况下,整个函数到此结束 ,不再往下进行。事实上我觉得这里应该是要返回head
}
node->nodeData=nodeData;
node->nextNode=head;
return node;
}
数据结构~~单向链表
最新推荐文章于 2024-06-11 10:00:00 发布