单向链表的排序(使用冒泡排序交换节点)

文章内容:

第一点:首先我们谈一谈有关链表的储存问题,首先我希望大家记住一各准则:链表的头指针尽量不要用来存储数据,为什么?因为这样会使你处理数据时更加的方便,方便在哪里?如果头指针不放数据的话,那么你在处理数据的时候,就可以将所有数据都统一对待?有的同学可能会有疑惑,结合排序说一下吧?排序的话其实就是改变各链节中的指针的指向,因为你链表的本质就是通过指针将多个链节(结构体)连接在一起,而排序就是改变链节的先后顺序,而改变它的顺序就是要改变其前后连接的的指针的指向。好讲到这里我问再回到我们最开始的问题:为什么链表的有指针不要储存数据,因为如果头指针储存数据的话,只要处理其后的指向就行,可其他的数据都要处理前后的指向关系,这就要分类讨论了,而且要考虑多很多的因素,既然如此,我干脆头指针就不储存了,这样所有的链节的处理方式都一样了。

弄懂了上面这一点后我们再来重头戏:有关排序的内容

有关链表冒泡排序的理解

首先上面也提及到了排序的问题,冒泡排序本值就是相邻比较大数(小数)后移,比较也就是说有两个物体才有比较,所以这里我们引入两个链节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表示内循环执行的次数
	}
}

看到这里恭喜你已经掌握了该方法,如果还是看不懂建议多看几遍,因为本文应该是csdn中相对详细的文章了

上一篇:C# 中的PadRight和PadLeft


下一篇:动态规划的无后效性