封装 链表

<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="" />

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页