☆ C/C++中使用单链表与结构体数组->排序(姓名+学号+分数)
对于单链表的排序,这篇文章介绍的是交换节点数据域的方法。
在写的时候仍然遇到了一些很奇怪的问题~(百思不得其解~)
(ㄒoㄒ)
*--->>>已解决~
对于那些相应的问题会附在这篇文章的最后方,
如果解决 ,后续会在下方附加解决方案。
****************************************************************************************************************************************
★首先附上单链表解决的源代码(复制即可运行~):
#include <iostream>
#include <string.h>
#include <malloc.h>
using namespace std;
typedef struct stu_info
{
char name[20]; //学生姓名
int sno; //学生学号
int score; //学生成绩
struct stu_info *link;
}STUDENT;
STUDENT * init_stu(int n) //初始化学生信息数组并进行插入学生数据
{
STUDENT * h; //定义头结点
STUDENT * S; //待排序的学生信息,指向头结点的下一个节点
STUDENT * p; //和h共同指向头结点
if (!(h = (STUDENT *)malloc(sizeof(STUDENT))))
{
cout << "Memory allocate error!" << endl;
exit(0);
}
h->name[0] = '0';
h->link = NULL;
p = h;
cout << "请输入学生的姓名、学号和分数 " << endl;
for (int i = 0; i < n; i++)
{
if (!(p->link = (STUDENT *)malloc(sizeof(STUDENT))))
{
cout << "Memory allocate error!" << endl;
exit(1);
}
S = p->link;
cin >> p->name;
cin >> S->sno;
cin >> S->score;
S->link = NULL;
p = S;
}
return h;
}
//void student_sort_by_sno(STUDENT * pStu)
void pFunc_2(STUDENT * pStu,int n)
{
STUDENT *p; //链表长度
int temp;
if (pStu->link == NULL)
return;
p = pStu->link; //p指向结点1,可以得到结点1的数据
for (int j = 1; j<n; j++) //冒泡排序
{
p = pStu->link; //每次冒泡过程完毕后,必须返回结点1
for (int i = n - 1; i >= j; i--) //一次冒泡过程
{
if ((p->sno) >(p->link->sno)) //从小到大排序
{
temp = p->sno;
p->sno = p->link->sno;
p->link->sno = temp;
}
p = p->link;
}
}
p = pStu->link; //要打印排序好的链表,必须从头开始打印,也是遍历的一个过程
//打印出排好序的链表(从小到大)
for (int m = 0; m<n; m++)
{
cout << p->sno << endl;
p = p->link;
}
cout << endl;
}
//void student_sort_by_name(STUDENT * pStu ,int n)
void pFunc_1(STUDENT * pStu,int n)
{
STUDENT *temp;
temp = (STUDENT*)malloc(sizeof(STUDENT));
STUDENT * p;
p = pStu;
for (int j = 1; j < n; j++)
{
p = pStu;
for (int i = n - 1; i >= j; i--)
{
if (strcmp(p->name, p->link->name)>0) //注意这里需要注明>0,不能直接写表达式
{
strcpy(temp->name, p->name);
strcpy(p->name, p->link->name);
strcpy(p->link->name, temp->name);
}
p = p->link;
}
}
for (int m = 0; m<n; m++)
{
cout << pStu->name << endl;
pStu = pStu->link;
}
cout << endl;
}
//void student_sort_by_score(STUDENT * pStu)
void pFunc_3(STUDENT * pStu ,int n)
{
STUDENT *p; //链表长度
int temp;
if (pStu->link == NULL)
return;
p = pStu->link; //p指向结点1,可以得到结点1的数据
for (int j = 1; j<n; j++) //冒泡排序
{
p = pStu->link; //每次冒泡过程完毕后,必须返回结点1
for (int i = n - 1; i >= j; i--) //一次冒泡过程
{
if ((p->score) >(p->link->score)) //从小到大排序
{
temp = p->score;
p->score = p->link->score;
p->link->score = temp;
}
p = p->link;
}
}
p = pStu->link; //要打印排序好的链表,必须从头开始打印,也是遍历的一个过程
//打印出排好序的链表(从小到大)
for (int m = 0; m<n; m++)
{
cout << p->score << endl;
p = p->link;
}
cout << endl;
}
int main()
{
int n; //n为输入的学生的个数
cout << "请输入需要输入的学生个数:" << endl;
cin >> n;
cout << endl << "初始化学生信息" << endl << endl;
STUDENT * pStu;
pStu = (STUDENT *)malloc(sizeof(STUDENT));
pStu = init_stu(n);
//Q1测试
//cout << endl << pStu->name << " ** " << pStu->sno << endl;
cout << endl;
cout << "1:按照姓名排序" << endl;
cout << "2:按照学号进行排序" << endl;
cout << "3:按照成绩进行排序" << endl;
cout << endl;
cout << "Please choose the function: " ;
int number = 0;
cin >> number;
switch(number)
{
case 1:pFunc_1(pStu,n);break;
case 2:pFunc_2(pStu,n);break;
case 3:pFunc_3(pStu,n);break;
default:cout << "Input Error! " << endl;
}
return 0;
}
★程序详细分析部分:
1:首先定义一个学生信息结构体,结构体包含三个部分(学生的姓名、学号与分数),然后定义一个结构体变量STUDENT
typedef struct stu_info
{
char name[20]; //学生姓名
int sno; //学生学号
int score; //学生成绩
struct stu_info *link;
}STUDENT;
2:初始化学生信息数组并进行插入学生数据:
· 这里声明头指针h指空,接着在函数结束时返回该空头指针(在主函数中将返回值赋予一个结构体指针的时候使用)
STUDENT * init_stu(int n) //初始化学生信息数组并进行插入学生数据
{
STUDENT * h; //定义头结点
STUDENT * S; //待排序的学生信息,指向头结点的下一个节点
STUDENT * p; //和h共同指向头结点
if (!(h = (STUDENT *)malloc(sizeof(STUDENT))))
{
cout << "Memory allocate error!" << endl;
exit(0);
}
h->name[0] = '0';
h->link = NULL;
p = h;
cout << "请输入学生的姓名、学号和分数 " << endl;
for (int i = 0; i < n; i++)
{
if (!(p->link = (STUDENT *)malloc(sizeof(STUDENT))))
{
cout << "Memory allocate error!" << endl;
exit(1);
}
S = p->link;
cin >> p->name;
cin >> S->sno;
cin >> S->score;
S->link = NULL;
p = S;
}
return h;
}
3:按照学号与分数进行排序
这两种排序的方法是一致的,通过冒泡法将最大的值移到尾部再次循环。
void pFunc_2(STUDENT * pStu, int n) //按照学号排序
{
STUDENT *p; //链表长度
int temp;
if (pStu->link == NULL)
return;
p = pStu->link; //p指向结点1,可以得到结点1的数据
for (int j = 1; j<n; j++) //冒泡排序
{
p = pStu->link; //每次冒泡过程完毕后,必须返回结点1
for (int i = n - 1; i >= j; i--) //一次冒泡过程
{
if ((p->sno) >(p->link->sno)) //从小到大排序
{
temp = p->sno;
p->sno = p->link->sno;
p->link->sno = temp;
}
p = p->link;
}
}
p = pStu->link; //要打印排序好的链表,必须从头开始打印,也是遍历的一个过程
//打印出排好序的链表(从小到大)
for (int m = 0; m<n; m++)
{
cout << p->sno << endl;
p = p->link;
}
cout << endl;
}
void pFunc_3(STUDENT * pStu, int n) //按照分数进行排序
{
STUDENT *p; //链表长度
int temp;
if (pStu->link == NULL)
return;
p = pStu->link; //p指向结点1,可以得到结点1的数据
for (int j = 1; j<n; j++) //冒泡排序
{
p = pStu->link; //每次冒泡过程完毕后,必须返回结点1
for (int i = n - 1; i >= j; i--) //一次冒泡过程
{
if ((p->score) >(p->link->score)) //从小到大排序
{
temp = p->score;
p->score = p->link->score;
p->link->score = temp;
}
p = p->link;
}
}
p = pStu->link; //要打印排序好的链表,必须从头开始打印,也是遍历的一个过程
//打印出排好序的链表(从小到大)
for (int m = 0; m<n; m++)
{
cout << p->score << endl;
p = p->link;
}
cout << endl;
}
4:这里的按照姓名排序需要多加注意
在使用中间变量进行交换的时候,一定要注意表达式的左右值(也就是确保赋值语句的正确性)---->>>
举个例子:p->name = p->link->name 这个语句是错误的,为什么呢?因为在表达式左侧的name是一个地址常量,不允许 修改,能修改的只可以是该地址所指向区域的值。
所以这里采用了字符串复制函数strcpy来进行数据域中的字符串的复制(交换),但是这里需要注意两点:
首先是在VS中使用该函数会报警告错误,可以在项目->xxx属性->C/C++->预处理器->预处理器定义->在后面添加: _CRT_SECURE_NO_WARNINGS即可;
其次是注意两个交换的变量类型需要一致,在本代码中指针域需要赋值给指针域。
void pFunc_1(STUDENT * pStu, int n) //按照姓名排序
{
STUDENT *temp;
temp = (STUDENT*)malloc(sizeof(STUDENT));
STUDENT * p;
p = pStu;
for (int j = 1; j < n; j++)
{
p = pStu;
for (int i = n - 1; i >= j; i--)
{
if (strcmp(p->name, p->link->name)>0) //注意这里需要注明>0,不能直接写表达式
{
strcpy(temp->name, p->name);
strcpy(p->name, p->link->name);
strcpy(p->link->name, temp->name);
}
p = p->link;
}
}
for (int m = 0; m<n; m++)
{
cout << pStu->name << endl;
pStu = pStu->link;
}
cout << endl;
}
这里还需要提到的一点就是在使用strcpy(字符串比较函数)的时候,一定要指明结果是>0还是<0,否则会出现错误;
5:最后附上主函数,有疑问的地方也就出现在了这里:
Q1:下面的代码中注释掉的一部分的输出中:
为什么头结点指向name的时候会直接指向头结点后面的那个节点的值呢?(第一个输出结果不是空,是它后面的name值,而第二个输出的就是第一个节点的地址…)
int main()
{
int n; //n为输入的学生的个数
cout << "请输入需要输入的学生个数:" << endl;
cin >> n;
cout << endl << "初始化学生信息" << endl << endl;
STUDENT * pStu;
pStu = (STUDENT *)malloc(sizeof(STUDENT));
pStu = init_stu(n);
//Q1测试
//cout << endl << pStu->name << " ** " << pStu->sno << endl;
cout << endl;
cout << "1:按照姓名排序" << endl;
cout << "2:按照学号进行排序" << endl;
cout << "3:按照成绩进行排序" << endl;
cout << endl;
cout << "Please choose the function: ";
int number = 0;
cin >> number;
switch (number)
{
case 1:pFunc_1(pStu, n); break;
case 2:pFunc_2(pStu, n); break;
case 3:pFunc_3(pStu, n); break;
default:cout << "Input Error! " << endl;
}
return 0;
}
****************************************************************************************************************************************
2018.09.30下午更新上述问题解决方案:
事后想了想从什么方向入手去解决这个问题,
既然头指针指向数据域中的姓名部分出错,而学号、分数部分没有出错,那么问题就应该出现在初始化对name的赋值上,
于是回到那一部分,分析后果然发现问题(未改动问题代码,方便对比发现问题):
在初始化语句中,p=h就直接让p指针和h指针一致了,这里应该是当时没注意,
以至于产生了后面的错误。
此处直接将p改为S即可,
到这里问题成功解决ヾ(◍°∇°◍)ノ゙
****************************************************************************************************************************************
2018.09.30下午更新【使用结构体数组进行排序】部分功能:
鉴于该代码是按照要求并经过适当加工,
代码量有点大,这里写在另一篇文章中;
传送门:点我即达(。・ω・。)
****************************************************************************************************************************************
☆仅仅记录日常编写代码 与 疑问(`・ω・´)
****************************************************************************************************************************************
最快的脚步不是跨越,而是继续,最慢的步伐不是小步,而是徘徊。
****************************************************************************************************************************************