<strong><span style="font-family:Microsoft YaHei;">linknode.h</span></strong>
<strong><span style="font-family:Microsoft YaHei;">#include <stdio.h>
#include <stdlib.h>
typedef struct student
{
int num;
float score;
struct student *pNext;//用于存储下一个节点的地址
}ST;
//函数声明
void add(ST **phead, int inum, float iscore);//函数声明 传入头结点的地址 然后插入数据
//显示所有数据
void showall(ST *head);//传递头结点的指针 用于有可能要改变头结点的指向
//实现逆转 不改变数据 改变指针指向
ST *rev(ST *head);
//统计链表节点个数
int getnum(ST *head);
//查找数据
ST *search(ST*head, int num);
//查找oldnum 修改为newnum
void change(ST*head, int oldnum, int newnum);
//删除链表所有的节点 用完后释放链表
void *freeall(ST *head);
//返回头结点 传入头结点 要删除节点的编号
ST *delete(ST*head, int num);//头指针
//根据节点 在节点前面插入
ST *HeadInsert(ST*head, int num, int inum, float iscore);
//根据节点 在节点尾部插入
ST*BackInsert(ST*head, int num, int inum, float iscore);
//链表排序
void sort(ST*head, char ch);//传入头结点 当ch=='>'时,实现从大到小排序 当ch=='<'时,实现从小到大排序</span></strong>
---------------------------------------------------------------------------------------------------------------------------------------------------------
linknode.cpp
<strong><span style="font-family:Microsoft YaHei;">//函数定义 及实现
#include "linknode.h"
void add(ST **phead, int inum, float iscore)//函数声明 传入头结点的地址 然后插入数据
{
if (*phead==NULL)//判断链表是否为空
{
ST *newnode = (ST *)malloc(sizeof(ST));//分配内存
if (newnode==NULL)
{
printf("内存分配失败\n");
return;
}
newnode->num = inum;//节点初始化
newnode->score = iscore;
newnode->pNext = NULL;
*phead = newnode;//让头指针指向这个节点
}
else//建立链表:先建立一个空链表 然后在一个一个的将元素插在队尾
{//尾部插入方式
//链表不为空
ST*p = *phead;//指向头结点
//while (p!=NULL)//p=NULL循环终止 表示到了链表的尾部
//{
// p = p->pNext;//循环向前
//}
//让p存储最后一个节点地址
while (p->pNext != NULL)//循环到最后一个节点的地址
{
p = p->pNext;//循环向前
}
//p->pNext==NULL//跳出循环
//创建节点
ST*newnode = (ST*)malloc(sizeof(ST));//
if (newnode == NULL)
{
printf("内存分配失败\n");
return;
}
newnode->num = inum;
newnode->score = iscore;
newnode->pNext = NULL;
p->pNext = newnode;//链接上
}
}
void showall(ST *head)//传递头结点的指针 显示所有数据 用于有可能要改变头结点的指向
{//遍历所有节点
while (head!=NULL)//判断指针指向是否为空
{
printf("num=%d score=%f ",head->num,head->score);
printf("%p %p\n",head,head->pNext);//打印两个节点的地址
head = head->pNext;//指针不断向前循环
}
}
//实现逆转
ST *rev(ST *head)
{
ST*p1, *p2, *p3;
p1 = p2 = p3 = NULL;
if (head==NULL||head->pNext==NULL)//如果头结点为空 有一个节点 或者链表为空
{
return head;//返回头结点
}
p1 = head;
p2 = head->pNext;
while (p2!=NULL)//从第二个到最后一个节点进行循环
{
p3 = p2->pNext;//布局三个节点
p2->pNext = p1;//指向前面一个节点
p1 = p2;//指针向前移动 从第二个到最后一个节点全部指向前面的节点
p2 = p3;
}
head->pNext = NULL;//代表链表的结束 设置第一个节点指向为空
head = p1;//指向最后一个节点
return head;//副本机制 改变的head并不会生效 需要返回值赋值生效
}
//获取节点个数
int getnum(ST *head)
{
//遍历所有的节点
int i = 0;//节点计数器
while (head!=NULL)
{
i++;//计数器自增
head = head->pNext;//指针不断向前循环
}
return i;//统计个数
}
//查找数据
ST *search(ST*head, int num)//根据编号查找节点
{
while (head!=NULL)//判定指针指向是否为空 循环遍历一个链表
{
if (num==head->num)
{
return head;//返回当前节点的指针地址 表示找到
}
head = head->pNext;//指针不断向前移动 循环
}
return NULL;//如果没有找到 返回为空
}
//查找oldnum 修改为newnum
void change(ST*head, int oldnum, int newnum)
{
ST*psearch = search(head, oldnum);//创建指针psearch 调用查找节点函数search
if (psearch==NULL)
{
printf("没有找到\n");
}
else
{
psearch->num = newnum;
printf("修改成功\n");
}
}
//链表的清空 用完后就要释放链表
void *freeall(ST *head)// error C2040: “freeall”:“void *(ST *)”与“void (ST *)”的间接寻址级别不同
{//此处的freeall添加* 不要忘记头文件中的声明也要添加*
ST *p1, *p2;
p1 = p2 = NULL;
p1 = head;
while (p1->pNext!=NULL)//循环遍历所有节点
{
p2 = p1->pNext;//pw2为p1的下一个节点
p1->pNext = p2->pNext;//p1存储了p2的下一个节点的地址
free(p2);//释放
//显示删除的中间状态
printf("\n---删除的过程---\n");
showall(head);
}
free(head);//释放最后一个节点
return NULL;
}
//返回头结点 传入头结点 要删除节点的编号 定位要删除的节点所在位置
ST *delete(ST*head, int num)
{
ST*p1, *p2;
p1 = p2 = NULL;//定义两个空节点
p1 = head;//从头结点开始循环
while (p1!=NULL)//循环所有的节点
{
if (num == p1->num )//判断是否等价于参数中的num
{
break;
}
else
{
p2 = p1;//记录当前节点
p1 = p1->pNext;//继续完后循环
}
}
if (p1==head)//头结点的情况
{
head = p1->pNext;//跳过头结点
free(p1);//释放第一个节点
}
else//如果不是head的情况
{
p2->pNext = p1->pNext;//跳过p1
free(p1);
}
return head;//函数有副本机制 所以返回头结点的值
}
//根据节点 在节点前面插入
ST *HeadInsert(ST*head, int num, int inum, float iscore)
{
ST*p1, *p2;
p1 = p2 = NULL;//定义两个空节点
p1 = head;//从头结点开始
while (p1!=NULL)//一直循环到尾部最后一个节点
{
if (p1->num==num)
{
break;
}
else//找到节点当前位置
{
p2 = p1;//记录当前节点
p1 = p1->pNext;//循环到下一个节点
}
}
if (p1==head)//如果是头结点 就在头结点的前部插入节点
{
ST*newnode = (ST*)malloc(sizeof(ST));//分配内存
newnode->num = inum;//初始化节点
newnode->score = iscore;
newnode->pNext = head;//指向第一个节点的头结点
head = newnode;//newnode成为第一个节点
}
else
{
ST*newnode = (ST*)malloc(sizeof(ST));//分配内存
//初始化节点的两个数据
newnode->num = inum;
newnode->score = iscore;
newnode->pNext = p1;//新节点指向p1
p2->pNext = newnode;//指向新节点
}
return head;
}
//根据节点 在节点尾部插入 不需要记录前面的节点 要记住后面的节点
ST*BackInsert(ST*head, int num, int inum, float iscore)
{
ST*p1, *p2;
p1 = p2 = NULL;//对节点置空
p1 = head;//从头结点开始
while (p1!=NULL)//一直循环到最后一个节点
{
if (p1->num==num)//判断相等
{
break;
}
else
{
p1 = p1->pNext;//循环到下一个节点
}
}
if (NULL==p1->pNext)//最后一个节点
{
ST*newnode = (ST*)malloc(sizeof(ST));//分配内存
newnode->num = inum;//初始化节点
newnode->score = iscore;
newnode->pNext = NULL;//指针域此时为空 代表最后一个节点
p1->pNext = newnode;//指向新开辟的节点
}
else
{
p2 = p1->pNext;//记录下一个节点的位置
ST*newnode = (ST*)malloc(sizeof(ST));//分配内存
newnode->num = inum;//初始化节点
newnode->score = iscore;
newnode->pNext = p2;//连接下一个节点
p1->pNext = newnode;//p1指向新的节点
}
return head;
}
//链表排序 不能随便访问另外一个节点,只能访问与自己相近的一个节点 选择排序不适用于链表,需要使用冒泡排序法
void sort(ST*head, char ch)//传入头结点 当ch=='>'时,实现从大到小排序 当ch=='<'时,实现从小到大排序
{
//链表的冒泡排序
if ('>'==ch)//从大到小排序
{
for (ST*p1=head; p1!=NULL; p1=p1->pNext)//循环终止条件 首结点为空
{
for (ST*p2 = head; p2 != NULL; p2 = p2->pNext)
{
if (p1->num<p2->num)
{
ST temp;//交换p1、p2节点
temp.num = p1->num;//实现编号num的交换
p1->num = p2->num;
p2->num = temp.num;
//此结构体中还有成绩
temp.score = p1->score;//实现成绩score的交换
p1->score = p2->score;
p2->score = temp.score;
}
}
}
}
else if ('<'==ch)//从小到大排序
{
for (ST*p1=head;p1!=NULL;p1=p1->pNext)//外层循环 只能遍历所有情况 数组可以规避一些无意义的,但是链表必须全部遍历一遍
{
for (ST*p2 = head; p2 != NULL;p2=p2->pNext)//内层循环
{
if (p1->num>p2->num)
{
ST temp;
temp.num = p1->num;
p1->num = p2->num;
p2->num = temp.num;
//交换score
temp.score = p1->score;
p1->score = p2->score;
p2->score = temp.score;
}
}
}
}
}</span></strong>
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
测试代码段 主程序
main.c
<strong><span style="font-family:Microsoft YaHei;">#include "linknode.h"
void main5()
{
struct student *head = NULL;//创建头结点的指针
//添加5个节点
add(&head, 1, 70);
add(&head, 2, 67);
add(&head, 3, 99);
add(&head, 4, 87);
add(&head, 5, 67);
//显示所有节点
//printf("%d %f\n",head->num,head->score);//打印数据
//printf("%d %f\n",head->pNext->num,head->pNext->score);
//printf("%d %f\n",head->pNext->pNext->num,head->pNext->pNext->score);
//逆转链表
head = rev(head);
//显示所有节点
showall(head);
//测试获取有多少个节点
printf("num=%d\n",getnum(head));
//链表查找测试
ST*psearch = search(head, 3);
printf("%d %f\n",psearch->num,psearch->score);//打印返回链表指针指向的数据
change(head, 3, 30);//修改数据
showall(head);//显示所有的节点
//显示所有数据 传递头结点的指针
showall(head);
getchar();
}
<img src="https://img-blog.csdn.net/20150729085428211?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
void main4()
{
struct student *head = NULL;
add(&head, 1, 70);
add(&head, 2, 67);
add(&head, 3, 99);
add(&head, 4, 87);
add(&head, 5, 67);
showall(head);//显示所有的节点
printf("删除之后:");
//freeall(head);
//由于最后把head也给释放掉了 所以在此处要把head设为NULL
//head = NULL;//方法一 ok 删除所有节点之后 头结点务必为空
//为了代码的稳健型 使用方法二
//方法二 让freeall函数返回void*指针 最后return NULL 即可
head=freeall(head);
showall(head);//显示所有的节点
getchar();
}
<img src="https://img-blog.csdn.net/20150729085337085?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />
//测试链表的排序
void main3()
{
struct student *head = NULL;
add(&head, 10, 70);
add(&head, 2, 67);
add(&head, 30, 99);
add(&head, 4, 87);
add(&head, 50, 67);
showall(head);//显示所有的节点
printf("\n从小到大,排序之后:\n");
sort(head,'>');
showall(head);//显示所有的节点
printf("\n从大到小,排序之后:\n");
sort(head, '<');
showall(head);
printf("该链表一共有:%d个节点\n",getnum(head));
getchar();
}
</span></strong>
<strong><span style="font-family:Microsoft YaHei;"><img src="https://img-blog.csdn.net/20150729085528724?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" /></span></strong>
<strong><span style="font-family:Microsoft YaHei;">
//测试前插 节点
void main2()
{
struct student *head = NULL;
add(&head, 10, 70);
add(&head, 2, 67);
add(&head, 30, 99);
add(&head, 4, 87);
add(&head, 50, 67);
showall(head);//显示所有的节点
printf("\n插入之后的链表节点\n");
head = HeadInsert(head, 2, 20, 90.0);//在节点2的前面插入20
showall(head);
printf("\n插入之后的链表节点\n");
head = HeadInsert(head, 50, 520, 590.0);//在节点2的前面插入20
showall(head);
printf("\n插入之后的链表节点\n");
head = HeadInsert(head, 4, 40, 444.0);//在节点2的前面插入20
showall(head);
getchar();
}
</span></strong>
<strong><span style="font-family:Microsoft YaHei;"><img src="https://img-blog.csdn.net/20150729085546274?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" /></span></strong>
<strong><span style="font-family:Microsoft YaHei;">
//测试后 插节点
void main()
{
struct student *head = NULL;
add(&head, 10, 70);
add(&head, 2, 67);
add(&head, 30, 99);
add(&head, 4, 87);
add(&head, 50, 67);
showall(head);//显示所有的节点
printf("\n插入之后的链表节点\n");
head = BackInsert(head, 2, 20, 90.0);//在节点2的前面插入20
showall(head);
printf("\n插入之后的链表节点\n");
head = BackInsert(head, 50, 520, 590.0);//在节点2的前面插入20
showall(head);
printf("\n插入之后的链表节点\n");
head = BackInsert(head, 4, 40, 444.0);//在节点2的前面插入20
showall(head);
//测试删除指定节点
printf("\n删除节点后:\n");
//head = delete(head, 33);//如果删除一个不存在的节点 程序就会崩溃
head = delete(head, 4);//删除节点2
head = delete(head, 10);//删除节点2
head = delete(head, 50);//删除节点2
showall(head);
getchar();
}</span></strong>
<img src="https://img-blog.csdn.net/20150729085602092?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />