#include <stdio.h>
struct Student
{
char szName[20];
unsigned long ulNumber;
unsigned int uAge;
};
struct Node
{
Student student;//其他结构体可作为结构体的成员变量
int iScore;//学生成绩
Node* pNext;//此结构体本身的指针可以作为自身的成员变量
//Node pNext;// 错误!结构体本身不可以作为自身的成员变量
Node* pPrevious;// 双向链表中的一个节点,pNext 指向此节点后一个节点
// pPrevious 指向此节点前一个节点
};
// 因为此函数定义在 main() 函数后面了,所以要前置声明后
// 在 main() 函数里才能使用
void str_copy(char* szTo, char* szFrom);
void print_node(Node* pNode);
void print_list(Node* pHead);
void swap_node(Node* pNode, Node* qNode);
int main()
{
Node node;// 声明一个节点,后面作为头节点
Node* pHead = &node;// 双向链表的头节点,一般不会保存有用数据,仅使用其中的
// pNext 指针指向链表的第一个保存有用数据的节点;
pHead->pPrevious = NULL; // 头节点没有前向节点,置为空指针
// 开始构造第一个有用节点
Node curNode;
char curStudentName[] = "xiaoming";//注意此处不能超过19个字符,在Student
//结构体定义的学生姓名为20个字符,要预留出一个
// 位置放字符串终止字符 '\0',此字符会自动添加到
// curStudentName[] 数组中
// 如函数中所示,复制数组时要一个元素一个元素的复制
str_copy(curNode.student.szName, curStudentName);
curNode.student.ulNumber = 10001;
curNode.student.uAge = 10;
curNode.iScore = 98;
curNode.pPrevious = pHead; // 第一个有用节点的前置节点时头节点
pHead->pNext = &curNode; // 同理,头节点的后置节点为第一个有用节点
// 下面构造第二个节点
Node curNode2;
char curStudentName2[] = "xiaohong";
str_copy(curNode2.student.szName, curStudentName2);
curNode2.student.ulNumber = 10002;
curNode2.student.uAge = 11;
curNode2.iScore = 96;
curNode2.pPrevious = pHead->pNext;// 第二个节点的前置节点为第一个节点
pHead->pNext->pNext = &curNode2;// 同理,第一个节点的后置节点为第二个节点
//下面用循环从第二个节点开始多构造几个节点
Node nodes[7];
Node* pCurNode = &curNode2;
for (int i = 0; i < 7; ++i)
{
char studentName[20] = "xiaogang";
studentName[8] = 'A' + i;
studentName[9] = '\0';//因为上一条语句覆盖了自动添加的字符串终止符
//所以此处要手动添加
str_copy(nodes[i].student.szName, studentName);
nodes[i].student.ulNumber = 10003 + i;
// 如果学号为偶数则年龄为 10 岁,否则为 11 岁
nodes[i].student.uAge = nodes[i].student.ulNumber % 2 == 0 ? 10 : 11;
nodes[i].iScore = 100 + 2 * i;
nodes[i].pPrevious = pCurNode;// 此节点的前置节点为上一步循环中构造的节点
pCurNode->pNext = &nodes[i];// 上一步循环中构造的节点的后置节点为此节点
pCurNode = &nodes[i];// 构造完毕后,用pCurNode记录当前节点用于下一步循环
}
pCurNode->pNext = NULL;// 特别注意!最后一个节点的后置节点为空指针
// 因为最后一个节点就没有后置节点了
print_list(pHead);
swap_node(pHead->pNext, pHead->pNext->pNext->pNext->pNext);
printf("------------------------\n");
print_list(pHead);
swap_node(pHead->pNext, pHead->pNext->pNext);
printf("------------------------\n");
print_list(pHead);
// 接下来用冒泡排序算法对链表里的学生进行按成绩降序排名
// 拿到第一个学生的节点
Node* pNode = pHead->pNext;
// 因为 pNode 指向的学生需要和他后面的学生进行比较
// 所以需要保证他后面还有学生
while (pNode->pNext != NULL)
{
// 记录 pNode 用于循环迭代,后面交换会改变 pNode 节点
// 所以提前记录到pPreNode
Node* pPreNode = pNode->pPrevious;
// 拿到 pNode 后面的节点
Node* qNode = pNode->pNext;
// 还没有比到最后一个学生则继续比较
while (qNode != NULL)
{
// 记录下 qNode 的前置节点
Node* qPreNode = qNode->pPrevious;
// 就算记录了 qNode 的前置节点,后面的交换操作
// 也有可能破坏这个前置节点,此标志用于标记破坏的
// 情形,进行特殊处理
bool pqFlag = false;
// 如果后面的学生成绩高,则把他向前交换
if (pNode->iScore < qNode->iScore)
{
if (qPreNode == pNode)
{
// qNode 的前置节点被破坏的情形就是
// qPreNode 与 pNode 相等时,此时交换pNode, qNode
// 就会改变 qPreNode 的值,而此时 qPreNode == pNode
// 说明 pNode 正好就是 qNode 的前置节点,因此...
pqFlag = true;
}
swap_node(pNode, qNode);
pNode = pPreNode->pNext;
}
if (pqFlag)
{
// ...接上,pNode 前置节点的后置的后置就是 qNode,
// 再后置就是 qNode 的下一个节点,进入后续循环进行比较
qNode = pPreNode->pNext->pNext->pNext;
}
else
{
qNode = qPreNode->pNext->pNext;
}
}
pNode = pPreNode->pNext->pNext;
}
printf("after being sorted:\n");
print_list(pHead);
return 0;
}
void str_copy(char* szTo, char* szFrom)
{
for (int i = 0;/* 此处未用到可为空,但后面的分号不可省略 */; ++i)
{
szTo[i] = szFrom[i];
if (szFrom[i] == '\0')
{
// 复制到最后的字符串终止字符了,跳出循环
break;//跳出从 break 往外数的第一个循环,此处即为 for 循环
}
}
}
void print_node(Node* pNode)
{
printf("Student Name: %s\n", pNode->student.szName);
printf("Student Number: %d\n", pNode->student.ulNumber);
printf("Student Age: %d\n", pNode->student.uAge);
printf("Student Score: %d\n", pNode->iScore);
}
// 打印整个链表的有用数据
void print_list(Node* pHead)
{
// 首先把第一个有用数据的指针复制给 pCurNode
Node* pCurNode = pHead->pNext;
//如果不是空指针,则说明未到链表结尾
//则打印这个节点
while (pCurNode != NULL)
{
print_node(pCurNode);
pCurNode = pCurNode->pNext;
}
}
// 在链表中交换两个节点的位置,实际上就是
// 分别交换这两个节点的前后置节点指针
void swap_node(Node* pNode, Node* qNode)
{
// 先记录两个节点的前后置指针
Node* pPreNode = pNode->pPrevious;
Node* pNextNode = pNode->pNext;
Node* qPreNode = qNode->pPrevious;
Node* qNextNode = qNode->pNext;
// 如果两个节点不相邻,说明两者之间有节点,
// bNodesExist 则为 true
bool bNodesExist = pNextNode != qNode;
if (bNodesExist)
{
pNode->pNext = qNextNode;
pNode->pPrevious = qPreNode;
qNode->pNext = pNextNode;
qNode->pPrevious = pPreNode;
}
// 如果两个节点相邻,则交换方式与不相邻不同
else
{
qNode->pNext = pNode;
qNode->pPrevious = pPreNode;
pNode->pNext = qNextNode;
pNode->pPrevious = qNode;
}
// 再考虑靠近头节点位置节点的前置指针,此时 pNode 和 qNode
// 已经互换位置,此时 qNode 靠近头节点
pPreNode->pNext = qNode;
// 同理
if (qNextNode != NULL)
{
qNextNode->pPrevious = pNode;
}
// 最后需要考虑两个交换节点之间的节点
// 若两个节点之间有节点
if (bNodesExist)
{
if (pNextNode != NULL)
{
pNextNode->pPrevious = qNode;
}
qPreNode->pNext = pNode;
}
// 若两个节点之间没有节点,则在前面交换两个节点内容
// 已经完成了交换操作
}
----------------运行结果----------------------
Student Name: xiaoming
Student Number: 10001
Student Age: 10
Student Score: 98
Student Name: xiaohong
Student Number: 10002
Student Age: 11
Student Score: 96
Student Name: xiaogangA
Student Number: 10003
Student Age: 11
Student Score: 100
Student Name: xiaogangB
Student Number: 10004
Student Age: 10
Student Score: 102
Student Name: xiaogangC
Student Number: 10005
Student Age: 11
Student Score: 104
Student Name: xiaogangD
Student Number: 10006
Student Age: 10
Student Score: 106
Student Name: xiaogangE
Student Number: 10007
Student Age: 11
Student Score: 108
Student Name: xiaogangF
Student Number: 10008
Student Age: 10
Student Score: 110
Student Name: xiaogangG
Student Number: 10009
Student Age: 11
Student Score: 112
------------------------
Student Name: xiaogangB
Student Number: 10004
Student Age: 10
Student Score: 102
Student Name: xiaohong
Student Number: 10002
Student Age: 11
Student Score: 96
Student Name: xiaogangA
Student Number: 10003
Student Age: 11
Student Score: 100
Student Name: xiaoming
Student Number: 10001
Student Age: 10
Student Score: 98
Student Name: xiaogangC
Student Number: 10005
Student Age: 11
Student Score: 104
Student Name: xiaogangD
Student Number: 10006
Student Age: 10
Student Score: 106
Student Name: xiaogangE
Student Number: 10007
Student Age: 11
Student Score: 108
Student Name: xiaogangF
Student Number: 10008
Student Age: 10
Student Score: 110
Student Name: xiaogangG
Student Number: 10009
Student Age: 11
Student Score: 112
------------------------
Student Name: xiaohong
Student Number: 10002
Student Age: 11
Student Score: 96
Student Name: xiaogangB
Student Number: 10004
Student Age: 10
Student Score: 102
Student Name: xiaogangA
Student Number: 10003
Student Age: 11
Student Score: 100
Student Name: xiaoming
Student Number: 10001
Student Age: 10
Student Score: 98
Student Name: xiaogangC
Student Number: 10005
Student Age: 11
Student Score: 104
Student Name: xiaogangD
Student Number: 10006
Student Age: 10
Student Score: 106
Student Name: xiaogangE
Student Number: 10007
Student Age: 11
Student Score: 108
Student Name: xiaogangF
Student Number: 10008
Student Age: 10
Student Score: 110
Student Name: xiaogangG
Student Number: 10009
Student Age: 11
Student Score: 112
after being sorted:
Student Name: xiaogangG
Student Number: 10009
Student Age: 11
Student Score: 112
Student Name: xiaogangF
Student Number: 10008
Student Age: 10
Student Score: 110
Student Name: xiaogangE
Student Number: 10007
Student Age: 11
Student Score: 108
Student Name: xiaogangD
Student Number: 10006
Student Age: 10
Student Score: 106
Student Name: xiaogangC
Student Number: 10005
Student Age: 11
Student Score: 104
Student Name: xiaogangB
Student Number: 10004
Student Age: 10
Student Score: 102
Student Name: xiaogangA
Student Number: 10003
Student Age: 11
Student Score: 100
Student Name: xiaoming
Student Number: 10001
Student Age: 10
Student Score: 98
Student Name: xiaohong
Student Number: 10002
Student Age: 11
Student Score: 96
双向链表排序
最新推荐文章于 2024-07-12 01:14:17 发布