C++顺序列表数据结构(值得深究)小白专用,高手绕道

// demo of the struct.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"//把这行放在最开始。

#include<string.h>
#include<iostream>

#define MAXLEN 100//定义顺序列表的最大长度,这种宏定义的方式经常用到,就是一处修改全部使用。
using namespace std;

typedef struct//定义最基础的数据类型	`
{
  char key[10];//数据结构里面有一个用来存放char这种变量的的数组key,10是学号大概的字节数
  char name[20];//数据结构里面有一个用来存放char这种变量的的数组name,20是名字大概的字节数
  int age;//这里不需要数组是因为,年龄就一个,而且是数组不是字符串
}DATA;//定义结点类型,此后DATA 就像int 或bool一样的一种数据类型了。
typedef struct//定义顺序列表,将上面的DATA作为数据的一种类型用到这里。就像int一样。
{
  DATA listData[MAXLEN+1];//保存顺序表的结构数组,这与char key[10]没有本质区别,C++定义了一些基础的数据结构如int,char,double,bool之类,但允许我们去开发;
  int ListLen;//定义一个int变量,来表示顺序表已存结点的数量。
}SLType;//顺序列表的数据形式,就像int最开始定义一样。
void SLInit(SLType*SL);
int SLAdd(SLType*SL,DATA data);
int SLAll(SLType*SL);
DATA*SLFindByNum(SLType*SL,int i);
int SLFindByCout(SLType*SL,char*key);
int SLDelete(SLType*SL,int n);
int SLInsert(SLType*SL,int n,DATA data);
int _tmain(int argc, _TCHAR* argv[])  
{
	int i;
	int point;
	SLType SL;//实例化一个顺序表变量,与int i;没有本质区别
	DATA data;//实例化结点保存数据类型的变量
	DATA* pdata;//定义一个DATA类型的指针变量。
	char key[10];//留作保留关键字
	cout<<"顺序列表操作演示!"<<endl;
	SLInit(&SL);//初始化顺序表,SL是前面实例化的一个对象,&SL是把这个列表的首地址传过来 ,因为其中SLInit函数定义用的是指针形参。
	cout<<"初始化顺序表完成!\n";
	do
	{
		cout<<"输入添加的结点(学号 姓名 年龄):";//每次输入结束的标志是按了回车键。
		fflush(stdin);//清空输入缓冲区
		cin>>data.key>>data.name>>data.age;//之前有篇博客专门写过这种输入。
		if(data.age)//输入时候的退出机制,如果输入了0,那么就不进入,同样可以用data.name或者data.key代替
			{
				if(!SLAdd(&SL,data))//如果添加失败就退出这个循环机制,
									//注意这里又用了&SL,说明又是对地址进行操作,而SLAdd必然是用指针形参去定义的。
				break;
			}
		else//如果没输入0
				break;//一次输入结束,跳出死循环
	}while(1);//用do while(1)这样的形式作为输入循环,但一些输入结束后必然要跳出循环,往下执行。所以程序里面有了break
	cout<<"顺序列表中的结点顺序为:\n";
	SLAll(&SL);//按照地址把所有的结点数据打印出来。
	fflush(stdin);//清空输入缓冲区
	/
	//以下是三种不同的算法处理。
	do//这个算法是用来实现类似于限制输入密码次数,并在还没输入完次数的时候给以循环的功能。do while(1)循环的作用
	{
		for(int j=0;j<3;j++)
		{
			cout<<"\n取出结点的序号:\n";
			cin>>i;//很小的错误,这里的i与上面的for的不能用同一个,如果上面也有i则会出错。所以要用j。
			pdata=SLFindByNum(&SL,i);
			fflush(stdin);//清空输入缓冲区
			if(pdata)
			{
				printf("第%d个结点为:(%s,%s,%d)\n",i,pdata->key,pdata->name,pdata->age);
				break;//跳出if循环,输入正确的时候要直接跳出do while(1)循环,这个break跳出只是跳出是到75行的break。
			}
			int a=2-j;//标志位
			if(a)
			{
			cout<<"你还可以输入:"<<a<<"次!"<<endl;//告诉输入的人,它输入错误的次数,和还剩的次数。
			}
			else//当a=0的时候,也就是连续三次输入错误的时候,告诉输入的人,它的输入错误,没有输入的机会了。
			cout<<"你已经没有输入的机会了!!";
		}
		break;	//跳出do while(1)循环,此种有两次机会到这里,一次是上次的break跳出到这里,另一次是for循环,三次结束后自动跳出。
	}while(1);
	
	//原本上面的代码是如下的(书本的代码)
	//cout<<"\n要取出结点的序号为:";
	//	cin>>i;
	//	pdata=SLFindByNum(&SL,i);//从这条语句进去,但是如果返回的是NULL ,那么直接往cout<<"\n取出结点的关键字:\n";运行。所以上面循环这些是拦路的罢了,让它多走几次。
	//	if(pdata)
	//	{
	//		printf("第%d个结点为:(%s,%s,%d)\n",i,pdata->key,pdata->name,pdata->age);
	//	}//这个功能到这里就结束了,如果是输入了不正确的,那么就会直接跳到下一个语段,
	//
	//如果非要加上循环1次的do while(1),事实上下面的代码是跟上面的原来的代码一样
	//do
	//{
	//	cout<<"\n要取出结点的序号为:";
	//	cin>>i;
	//	pdata=SLFindByNum(&SL,i);//从这条语句进去,但是如果返回的是NULL ,那么直接往cout<<"\n取出结点的关键字:\n";运行。所以上面循环这些是拦路的罢了,让它多走几次。
	//	if(pdata)
	//	{
	//		printf("第%d个结点为:(%s,%s,%d)\n",i,pdata->key,pdata->name,pdata->age);
	//	}//这个功能到这里就结束了,如果是输入了不正确的,那么就会直接跳到下一个语段,
	//	break;//加循环一定要注意上面时候跳出循环。不能弄成死循环。
	//}while(1);
	/
	cout<<"\n取出结点的关键字:\n";
	cin>>key;
	i=SLFindByCout(&SL,key);//按关键字查找,返回结点序号,这里的i等同于前面输入的i
	pdata=SLFindByNum(&SL,i);
	if(pdata)
	{
		printf("第%d个结点为:(%s,%s,%d)\n",i,pdata->key,pdata->name,pdata->age);
	}
	fflush(stdin);//清空输入缓冲区
	cout<<"\n输入你想删除的结点位置:";
	cin>>point;
	i=SLDelete(&SL,point);//把想删掉的结点输入到删除的函数中。
	if(i)
	{
		cout<<"修改后顺序列表中的结点顺序为:\n";
		SLAll(&SL);//按照地址把所有的结点数据打印出来。
	}
	fflush(stdin);//清空输入缓冲区
	cout<<"\n输入你想插入的结点位置:";
	cin>>point;
	cout<<"输入你想再添加的结点(学号 姓名 年龄):";//每次输入结束的标志是按了回车键。
	fflush(stdin);//清空输入缓冲区
	cin>>data.key>>data.name>>data.age;//之前有篇博客专门写过这种输入。
	i=SLInsert(&SL,point,data);
	if(i)
	{
		cout<<"增加后顺序列表中的结点顺序为:\n";
		SLAll(&SL);//按照地址把所有的结点数据打印出来。
	}
	
	
	return 0;
	}

void SLInit(SLType*SL)//初始化列表,用的是指针变量,是因为对列表的内存地址里的内容初始化
{
	SL->ListLen=0;//初始化为空表,因为ListLen在结构体设置中就是作为记录列表内部有多少个节点存在的。
					//这里并没有清空列表,每次调用这个函数作用是:把列表计数节点重新放置到最开始的位置上,将指针往前拨的意思
					//如此就算是原来的地址有数据也不担心,以后的数据就会将原来的数据给覆盖,也就是清空。
					//如此会更快,感觉不安全。
}
//添加一个结点
int SLAdd(SLType*SL,DATA data)//用int作为函数的类型往往是为了此函数返回标准位,让调用这个函数的函数可以做成判断
{
	if(SL->ListLen>=MAXLEN)//如果顺序表已经满了。指针读取变量值的方式SL->ListLen。
	{
	   cout<<"顺序表已满,不能再添加结点了!"<<endl;
	   return 0;//注意这里返回0,所以if(!SLAdd(&SL,data))才有了!去判断,不然也进不去,当然也是可以设置为return1,然后不要!的
	   
	}
	else
	{
		SL->listData[++SL->ListLen]=data;//这行代码很高水平,主要格式是SL->listData[i]=data也就是目的是要把data的数据写到顺序表中,
										//但因为这是要在现在的最后一个上面再加一个,那么就是说要先把指针往后移一个单位。
										//其实这代码是两个组合int n=SL->ListLen;SL->listData[n+1]=data;
										//说明其实SL->ListLen可以做取值用,++SL->ListLen又可以作为赋值使用。那么可以知道的是这里不是从0开始计算的。
	}
}
//显示全部结点
int SLAll(SLType*SL)//注意这里只需要一个被SLType实例化的对象指针
{
	int i;
	for(i=1;i<=SL->ListLen;i++)//i=1开始是因为人类计算不从0开始
	{
		printf("(%s,%s,%d)\n",SL->listData[i].key,SL->listData[i].name,SL->listData[i].age);
	}
	return 0;//其实这个函数可以用void 不用int 那么连return 0;都可以不用。
}
//按序号查找一个结点
//一下是一个错误实例的代码,这是一个重点
//int SLFindByNum(SLType*SL,int i)
//{
//	if(i<1||i>SL->ListLen+1)//判断这个序号是不是错误序号,不是i<1||i>SL->ListLen+1是因为记数是从1开始的。
//	{
//		cout<<"结点序号错误,不能返回结点!\n";
//		return NULL;
//	}
//	return (SL->listData[i]);//正确的情况下,返回的是listData[i]的值。
//}
//以下才是正确的代码格式
DATA*SLFindByNum(SLType*SL,int i)
{
	if(i<1||i>SL->ListLen)//判断这个序号是不是错误序号,不是i<1||i>SL->ListLen+1是因为记数是从1开始的。
	{
		cout<<"结点序号错误,不能返回结点!\n";
			return NULL;
	}
	return&(SL->listData[i]);//正确的情况下,返回的是listData[i]的值。
}//对比上面的代码,这里用的是DATA*这种格式的指针,让着一个函数SLFindByNum(SLType*SL,int i)都成了DATA指针,如此才需要用&返回一个地址里的值,
//为什么用int不行?原因是listData[i]是DATA类型的数组,里面有三个参数,与int的类型不一样,不能用int来作为其返回函数类型,所以要用DATA.


//按照关键字查找
int SLFindByCout(SLType*SL,char*key)
{
	int i;
	for(i=1;i<=SL->ListLen;i++)//从头开始找起
	{
		if(strcmp(SL->listData[i].key,key)==0)//判断是否本地数据库(前面)是不是有与外部数据匹配的函数。
		{
			return i;
		}
	}
	return 0;//说明了为什么不是从0开始的记录的原因。以后像这样的数据结构处理,基本上都是从1开始记录。
}
//插入结点
int SLInsert(SLType*SL,int n,DATA data)//在n这个点上插入DATA数据,DATA这里的格式与int类型.
										//用int作为SLInsert(SLType*SL,int n,DATA data)的类型,要注意这里可能是用于返回标准位。
{
	int i;
	if(SL->ListLen>=MAXLEN)//顺序表结点数量已经到了最大数量,不能再继续插入
	{
		cout<<"顺序表已满,不能再插入了!\n";
		return 0;
	}
	if(n<1||n>SL->ListLen-1)//插入结点位置不正确,ListLen-1是因为还可以在最后一个位置上插入。
	{
		cout<<"插入元素序号错误,不能再插入了!\n";
		return 0;
	}
	for(i=SL->ListLen;i>=n;i--)//往前移的循环
	{
		SL->listData[i+1]=SL->listData[i];//将前面的数据传给后面的,可以直接覆盖掉。
	}
	上面的工作是为了空出位置
	SL->listData[n]=data;
	SL->ListLen++;//让这个数组长度加1。
	return 1;
}
//删除结点
int SLDelete(SLType*SL,int n)
{
	int i;
	
	if(n<1||n>SL->ListLen+1)//插入结点位置不正确,ListLen-1是因为还可以在最后一个位置上插入。
	{
		cout<<"删除元素序号错误,不能删除了!\n";
		return 0;
	}
	for(i=n;i<SL->ListLen;i++)//往前移的循环
	{
		SL->listData[i]=SL->listData[i+1];//将前面的数据传给后面的,可以直接覆盖掉。
	}
	上面的工作是为了空出位置
	SL->ListLen--;//让这个数组长度减1。
	return 1;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值