文章内容:
第一点:首先我们谈一谈有关链表的储存问题,首先我希望大家记住一各准则:链表的头指针尽量不要用来存储数据,为什么?因为这样会使你处理数据时更加的方便,方便在哪里?如果头指针不放数据的话,那么你在处理数据的时候,就可以将所有数据都统一对待?有的同学可能会有疑惑,结合排序说一下吧?排序的话其实就是改变各链节中的指针的指向,因为你链表的本质就是通过指针将多个链节(结构体)连接在一起,而排序就是改变链节的先后顺序,而改变它的顺序就是要改变其前后连接的的指针的指向。好讲到这里我问再回到我们最开始的问题:为什么链表的有指针不要储存数据,因为如果头指针储存数据的话,只要处理其后的指向就行,可其他的数据都要处理前后的指向关系,这就要分类讨论了,而且要考虑多很多的因素,既然如此,我干脆头指针就不储存了,这样所有的链节的处理方式都一样了。
弄懂了上面这一点后我们再来重头戏:有关排序的内容
有关链表冒泡排序的理解
首先上面也提及到了排序的问题,冒泡排序本值就是相邻比较大数(小数)后移,比较也就是说有两个物体才有比较,所以这里我们引入两个链节curp(当前链节),nextp(后一项链节),其中nextp=curp->next;因为相邻麻,如果是在数组中就就是这俩个兄弟的事情了,可这里是链表啊,你想像一下一群小朋友手拉手的情景,所以curp这个小朋友前面有人拉着它啊,所以这里再引入以个变量prep(前一项链节),还有后面的小朋友,因为是单项链表所以,干脆就认为它是nextp->next就算了;
上面说明了:我们改变链表中某两个链节的顺序问题,其实涉及到了4个链节(改变一支手拉这手的队伍中,某两个小朋友的顺序,需要改变4个小朋友的手拉手问题)
理解了上面之后其实就好办多了,下面是本人的手绘图解,帮助大家更好的理解:
下面上代码:
在代码中会对链表的生成及冒泡排序进行详细的解说:
#include <stdio.h>
#include <stdlib.h>
struct student
{
int num;
int age;
struct student* next;
};
struct student *head = (struct student *)malloc(sizeof(struct student));
//因为链表头不储存数据,所以单独生成链表头
void main()
{
void mppx();//自定义一个冒泡排序函数
void addlb(struct student *pnew);//这个函数用于将生成的链节加到链表上去
void creatlb();//该函数用于生成链节,并输入链节的数据
void browse();//浏览个链节的内容
head->next = NULL;//刚开始时链表只有头链节,所以head->next = NULL;
creatlb();//函数调用生成链节,函数中会嵌套调用addlb
mppx();//调用冒泡排序对链表进行排序
browse();//输出链表
}
void addlb(struct student *pnew)
{
struct student* p2;
for (p2 = head;p2->next != NULL;p2 = p2->next)
{
//这个循环的目的是找出链表中的最后一个链节,方便在其后添加链节
}
p2->next = pnew;//添加传递过来的链节
pnew->next = NULL;//使最后的链节的指向为NULL
}
void creatlb()//生成链节的同时输入数据
{
int xh;
struct student* p;
printf("输入学生的学号(输入-1退出输入):");
scanf("%d", &xh);
while (xh != -1)
{
if ((p = (struct student*)malloc(sizeof(struct student))) == NULL)
{
free(p);
break;
}
else
{
p->num = xh;
printf("输入学生的年龄:");
scanf("%d", &p->age);
addlb(p);
}
printf("输入学生的学号(输入-1退出输入):");
scanf("%d", &xh);
}
}
void browse()
{
struct student* p;
for (p = head->next;p != NULL;p = p->next)//因为首链节head不储存数据所以从head->next开始,输出数据
{
printf("学生的学号:%d 学生的年龄:%d\n", p->num, p->age);
}
getchar();
getchar();
}
void mppx()//重头戏,认真看
{
struct student* prep; //prep前一项 curp当前项 nextp后一项 end控制循环
struct student* nextp;
struct student* end = NULL;//最开始一轮是所有的元素都要比较,所以end最开始为空
struct student* curp;
while (head->next != end)//冒泡排序都是双重循环,这里为外循环用于控制
{//循环的趟数
//初始化三个指针 ; 判断是否到达结束位置 ; 三个指针集体后移
for (prep = head, curp = prep->next, nextp = curp->next; nextp != end; prep = prep->next, curp = curp->next, nextp = nextp->next)
{
if (curp->num > nextp->num) //从小到大
{
prep->next = nextp;//交换指针指向
curp->next = nextp->next;
nextp->next = curp;
//此时next变前一项,cur变后一项 交换next cur
struct student* temp = curp; curp = nextp; nextp = temp;
//上面这一步我也想了很久,不是改变指针指向,就已经改变
//顺序,这步用来干嘛呀?很好,这时请看看for循环中的循环
//条件,并结合上图好好理解下,因为curp,nextp,都要不断后移,所以交换顺序后,
//应该使原本的curp变为nextp,nextp=curp;
}
}
//一轮循环结束 最后一项已经排好 end提前一项 (冒泡原理)
end = curp;//此时curp就是倒数第i项。i表示内循环执行的次数
}
}