任意两个相邻节点p、q位置互换图示:
假设p1->next指向p,那么显然p1->next->next就指向q,
p1->next->next->next就指向q的后继节点,我们用p2保存
p1->next->next指针。即:p2=p1->next->next,则有:
[ ]---->[p]---------->[q]---->[ ](排序前)
p1->next p1->next->next p2->next
图15
假设p1->next指向p,那么显然p1->next->next就指向q,
p1->next->next->next就指向q的后继节点,我们用p2保存
p1->next->next指针。即:p2=p1->next->next,则有:
[ ]---->[p]---------->[q]---->[ ](排序前)
p1->next p1->next->next p2->next
图15
[ ]---->[q]---------->[p]---->[ ](排序后)
图16
1、排序后q节点指向p节点,在调整指向之前,我们要保存原p的指向节点地址,即:p2=p1->next->next;
2、顺着这一步一步往下推,排序后图16中p1->next->next要指的是p2->next,所以p1->next->next=p2->next;
3、在图15中p2->next原是q发出来的指向,排序后图16中q的指向要变为指向p的,而原来p1->next是指向p的,所以p2->next=p1->next;
4、在图15中p1->next原是指向p的,排序后图16中p1->next要指向q,原来p1->next->next(即p2)是指向q的,所以p1->next=p2;
5、至此,我们完成了相邻两节点的顺序交换。
6、下面的程序描述改进了一点就是记录了每次最后一次节点下沉的位置,这样我们不必每次都从头到尾的扫描,只需要扫描到记录点为止。 因为后面的都已经是排好序的了。
对链表进行冒泡排序的函数为:
- /*
- ==========================
- 功能:冒泡排序(由小到大)
- 返回:指向链表表头的指针
- ==========================
- */
- struct student *BubbleSort (struct student *head)
- {
- struct student *endpt; //控制循环比较
- struct student *p; //临时指针变量
- struct student *p1,*p2;
- p1 = (struct student *) malloc (LEN);
- p1->next = head; //注意理解:我们增加一个节点,放在第一个节点的前面,主要是为了便于比较。因为第一个节点没有前驱,我们不能交换地址
- head = p1; //让head指向p1节点,排序完成后,我们再把p1节点释放掉
- for (endpt = NULL; endpt != head; endpt = p) //结合第6点理解
- {
- for (p = p1 = head; p1->next->next != endpt; p1 = p1->next)
- {
- if (p1->next->num > p1->next->next->num) //如果前面的节点键值比后面节点的键值大,则交换
- {
- p2 = p1->next->next; //结合第1点理解
- p1->next->next = p2->next; //结合第2点理解
- p2->next = p1->next; //结合第3点理解
- p1->next = p2; //结合第4点理解
- p = p1->next->next; //结合第6点理解
- }
- }
- }
- p1 = head; //把p1的信息去掉
- head = head->next; //让head指向排序后的第一个节点
- free (p1); //释放p1
- p1 = NULL; //p1置为NULL,保证不产生“野指针”,即地址不确定的指针变量
- return head;
- }