C语言之链表

之前用C语言写过数据结构的链式队列   链接http://t.csdn.cn/sgDme感兴趣的朋友可以看一下。

今天再写一个数据结构里面最基础的链表(后附完整代码),链表作为数据结构的基本,掌握其写法是非常重要的。

链表的数据结构如图所示

 节点是由数据域和指针域两个部分组成,其中数据域里面的数据类型可以是任何形式的数据,包括结构体数据类型,在本例中为了方便说明就以整形为例。指针域里面存放指针。

一般在写数据结构之前,都要对节点进行定义,节点定义如下

//定义节点
typedef struct Node
{
	int data;    //节点的数据域
	struct Node* next;  //节点的指针域
}NODE;

一般单向链表可以分为有表头和没有表头两种,本例描述有表头的单向链表。表头的定义如下

//创建链表头
NODE* createHead()
{//这次以有表头的链表为例
	NODE* mylist = (NODE*)malloc(sizeof(NODE));
	mylist->data = 0;    //表头里面的这个参数用来记录链表里面的系点数
	mylist->next = NULL; 
	return mylist;
}

定义节点后就要设计函数创建节点,创建节点函数如下

//创建节点
NODE* createNode(int data)
{//所谓创建节点就是初始化节点里面的参数
	NODE* newnode = (NODE*)malloc(sizeof(NODE));
	newnode->data = data;   
	newnode->next = NULL;  //初始化时,指针域指向空
	return newnode;
}

现在我们有了链表头和创建节点函数,就可以给链表中加入节点元素了,加入节点的方法一般用两种,即头插法尾插法,头插法函数如下

//头插法
void insertNodeByHead(NODE* mylist, int data)
{
	NODE* newnode = createNode(data);
	if (mylist->data == 0)  
	{
		mylist->next = newnode;   //当链表为空,直接将新节点放在表头的后面
	}
	else    //当链表不为空
	{
		NODE* tmp = mylist->next;    //创建临时指针指向链表当前的第一个元素    
		newnode->next = tmp;      //将新节点的指针域指向临时指针所指向的地址,即原先链表的第一个节点
		mylist->next = newnode; //链表头的指针域在指向新节点的地址,这样新节点在空间上就处于链表的第一位了
	}
	mylist->data++;    //插入新节点后,计数加一
}

尾插法函数如下

//尾插法
void insertNodeByTail(NODE* mylist, int data)
{
	NODE* newnode = createNode(data);   //插入前,先创建新节点
	if (mylist->data == 0)
	{
		mylist->next = newnode;  //如果链表为空,则直接将新节点插入表头后面
	}
	else
	{
		NODE* tmp = mylist->next;    //创建一个临时指针用来找到链表尾部
		while (tmp->next)  //当临时节点后面的节点不是空时
		{
			tmp = tmp->next;   //临时指针指向下一个节点
		}
		tmp->next = newnode;    //最终跳出循环的临时指针后面所指为空,此时临时指针就是链表尾部
	}
	mylist->data++;   //插入节点后,计数值加一
}

有加入节点的方法,就应该有删除节点的方法,本例的链表默认每个元素的数据域数值大小不同,删除节点函数如下(其实删除节点的方法有很多种,比如定义删除链表的第几个元素),本例删除节点的依据是数据域的值为我们要删除的对象

//删除节点
void deleteNode(NODE* mylist,int data)
{
	NODE* pre = mylist;        //创建新指针指向链表的表头地址
	NODE* cur = mylist->next;   //创建新指针指向链表的第一个节点地址
	while (cur->data != data && cur->next!=NULL)    //当cur的数据域不等于要删除的节点数据时,
		                                            //且cur的下一个节点不为空
	{
		pre = cur;    //pre指向cur
		cur = cur->next;   //cur指向下一个节点位置
	}
	//跳出这个循环后,cur所指向的位置要么为空,要么是要删除的节点(本例设定链表元素数值都不同)
	if (cur == NULL)   //当cur所指向的位置为空时
	{
		printf("该链表没有这个节点\n");    //打印提示
	}
	else    //当cur指向了要删除的节点
	{
		pre->next = cur->next;   //由之前的while循环,pre一直指向cur的前一个节点。
		                         //此时让pre指向cur的后一个节点(这里需要空间想象)
		free(cur);   //free掉cur,即删除链表中这个节点
		mylist->data--;   //删除一个节点,计数需要减一
	}
}

这样我们对于链表的一些基础操作都有了,在将链表的每个元素从第一个当最后一个打印出来

//打印链表
void print(NODE* mylist)
{
	NODE* tmp = mylist->next;   //创建临时指针指向第一个节点地址
	while(tmp)   //当临时指针不为空
	{
		printf("%d ", tmp->data);   //打印节点的数据域
		tmp = tmp->next;    //临时指针指向下一个节点
	}
	printf("\n");   //打印完所有节点的数据域之后,换行
}

测试代码

int main()
{//测试
	//创建链表头
	NODE* mylist = createHead();
	//头插法
	insertNodeByHead(mylist, 1);
	insertNodeByHead(mylist, 2);
	insertNodeByHead(mylist, 3);
	insertNodeByHead(mylist, 4);
	insertNodeByHead(mylist, 5);
	//打印所有节点
	print(mylist);

	//尾插法
	insertNodeByTail(mylist, 10);
	insertNodeByTail(mylist, 9);
	insertNodeByTail(mylist, 8);
	insertNodeByTail(mylist, 7);
	//打印所有节点
	print(mylist);

	//删除数据域为3的节点
	deleteNode(mylist, 3);
	//打印所有节点
	print(mylist);

	return 0;
}

    

测试结果

完整代码

#include<stdio.h>
#include<malloc.h>
//定义节点
typedef struct Node
{
	int data;    //节点的数据域
	struct Node* next;  //节点的指针域
}NODE;

//创建节点
NODE* createNode(int data)
{//所谓创建节点就是初始化节点里面的参数
	NODE* newnode = (NODE*)malloc(sizeof(NODE));
	newnode->data = data;   
	newnode->next = NULL;  //初始化时,指针域指向空
	return newnode;
}

//创建链表头
NODE* createHead()
{//这次以有表头的链表为例
	NODE* mylist = (NODE*)malloc(sizeof(NODE));
	mylist->data = 0;    //表头里面的这个参数用来记录链表里面的系点数
	mylist->next = NULL; 
	return mylist;
}

//尾插法
void insertNodeByTail(NODE* mylist, int data)
{
	NODE* newnode = createNode(data);   //插入前,先创建新节点
	if (mylist->data == 0)
	{
		mylist->next = newnode;  //如果链表为空,则直接将新节点插入表头后面
	}
	else
	{
		NODE* tmp = mylist->next;    //创建一个临时指针用来找到链表尾部
		while (tmp->next)  //当临时节点后面的节点不是空时
		{
			tmp = tmp->next;   //临时指针指向下一个节点
		}
		tmp->next = newnode;    //最终跳出循环的临时指针后面所指为空,此时临时指针就是链表尾部
	}
	mylist->data++;   //插入节点后,计数值加一
}

//头插法
void insertNodeByHead(NODE* mylist, int data)
{
	NODE* newnode = createNode(data);
	if (mylist->data == 0)  
	{
		mylist->next = newnode;   //当链表为空,直接将新节点放在表头的后面
	}
	else    //当链表不为空
	{
		NODE* tmp = mylist->next;    //创建临时指针指向链表当前的第一个元素    
		newnode->next = tmp;      //将新节点的指针域指向临时指针所指向的地址,即原先链表的第一个节点
		mylist->next = newnode; //链表头的指针域在指向新节点的地址,这样新节点在空间上就处于链表的第一位了
	}
	mylist->data++;    //插入新节点后,计数加一
}

//删除节点
void deleteNode(NODE* mylist,int data)
{
	NODE* pre = mylist;        //创建新指针指向链表的表头地址
	NODE* cur = mylist->next;   //创建新指针指向链表的第一个节点地址
	while (cur->data != data && cur->next!=NULL)    //当cur的数据域不等于要删除的节点数据时,
		                                            //且cur的下一个节点不为空
	{
		pre = cur;    //pre指向cur
		cur = cur->next;   //cur指向下一个节点位置
	}
	//跳出这个循环后,cur所指向的位置要么为空,要么是要删除的节点(本例设定链表元素数值都不同)
	if (cur == NULL)   //当cur所指向的位置为空时
	{
		printf("该链表没有这个节点\n");    //打印提示
	}
	else    //当cur指向了要删除的节点
	{
		pre->next = cur->next;   //由之前的while循环,pre一直指向cur的前一个节点。
		                         //此时让pre指向cur的后一个节点(这里需要空间想象)
		free(cur);   //free掉cur,即删除链表中这个节点
		mylist->data--;   //删除一个节点,计数需要减一
	}
}

//打印链表
void print(NODE* mylist)
{
	NODE* tmp = mylist->next;   //创建临时指针指向第一个节点地址
	while(tmp)   //当临时指针不为空
	{
		printf("%d ", tmp->data);   //打印节点的数据域
		tmp = tmp->next;    //临时指针指向下一个节点
	}
	printf("\n");   //打印完所有节点的数据域之后,换行
}
int main()
{//测试
	//创建链表头
	NODE* mylist = createHead();
	//头插法
	insertNodeByHead(mylist, 1);
	insertNodeByHead(mylist, 2);
	insertNodeByHead(mylist, 3);
	insertNodeByHead(mylist, 4);
	insertNodeByHead(mylist, 5);
	//打印所有节点
	print(mylist);

	//尾插法
	insertNodeByTail(mylist, 10);
	insertNodeByTail(mylist, 9);
	insertNodeByTail(mylist, 8);
	insertNodeByTail(mylist, 7);
	//打印所有节点
	print(mylist);

	//删除数据域为3的节点
	deleteNode(mylist, 3);
	//打印所有节点
	print(mylist);

	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值