数据结构单链表

最近在复习数据结构,先将一些学习的心得和笔记分享一下

单链表:

首先来看一下单链表的存储结构:

单链表的可以理解为是由两个部分组成,就是数据域和指针域,数据域主要存储的是链表的数据,指针域则是指向下一个结点的地址,当然了,头指针没有数据域,它的指针就指向首元结点或头结点的地址,尾指针的指针域就指向的是NULL。可能有的小伙伴不理解头结点和首元结点的区别,我在下边解释一下:

  OK ,回到我们的主题,从上边的图我们能得到哪些信息呢,首先,头指针和首元结点的区别,其次最主要的就是单链表的存储方式,我们可以看到每一个结点都有一个指针域,指针域指向的就是下一个结点的地址,当然了,有的小伙伴要问了,那么单链表里边的头结点呢,我就不上图了,在这里跟大家解释一下,单链表里边是可以没有头结点的,所谓的头结点就是头指针指向的结点,这个结点的数据域就是DATA,没有具体的数值,指针域指向的就是下一个结点的地址,那么这个结点理所当然就成为了首元结点。

上边说了那么多可能有的小伙伴就有疑问了,你说了那么多,倒是上一个代码啊,别着急,代码这就来。

一般来说,我们在学习数据结构的时候无非就是那么几个部分,增、删、改、查。我们一步一步来。

首先我们要创建一个单链表,具体代码如下:

//创建一个结构体,这个结构体是用来实现链表中的一个结点的结构体
typedef struct listnode{
    int data;//这里是我上边说的数据域
    struct listnode *next;//这里就是指针域
}node;

ok,结构体创建好了我们是不是就是要创建这么一个链表了,这里呢我们通过一个循环来进行数据的载入,废话不多说了,直接上代码

node *create()
{
	//准备阶段
	int i = 0;
	node *head , *p , *q;
	int x = 0;
	head = (node *)malloc(sizeof(node));
	//变量分别是:链表中的数据个数、头结点等;
	
	//以下开始进入循环创建每一个结点的数据
	while(1)
	{
		cout<<"please input data"<<endl;
        cin>>x;
		if(x == 0)                //判断是不是输入的0,是的话就结束了
		{
			break;
		}
		p = (node *)malloc(sizeof(node));    //给我们的节点开辟一段空间
		p->data = x;                         //把输入的数据给data域 
		if(++i == 1)                         /*++i == 1 说明什么,说明是第一次插入的结点,那        
                                               就是首元节点了,既然是首元节点那就是在头结点的         
                                                 后边,所以使用head->next = p;*/
		{
			head->next = p;
		}
		else{
			q->next = p;                     /*这里就是说已经在首元节点后边了,但是还有数据, 
                                                 那就把q->next = p,有人要问了,你的q不还是     
                                                 null么,为什么能指向呢,请大家考虑一下,上边 
                                                 执行了一次if语句了,执行else的话已经是第二次 
                                                 循环了,此时的q已经不是空了,而是指向了首元结 
                                                 点了。*/
		}
		q = p;                                    //q此时指向了最后一个结点
	}
	q->next = NULL;                               //尾结点的指针域就是NULL
	return head;
}

通过上边的代码啊,我们就可以创建一个单链表,并且可以给这个链表里边写入我们的数据等等,根据以往的惯例,有的小伙伴就要说了,我到底怎样知道我建立了怎样一个链表,所以接下来就是求单链表的长度问题,具体解释看看代码吧

int length(node *a)        /*这个函数是用来求链表的长度的,既然是求链表的长度,那肯定得有一个链        
                              表吧,所以这里我用了传参的方式把上边建立的链表传进来了*/
{
	node *p;             
	p = a->next;        /*我们这里创建了一个指针,至于为什么指针指向了a->next呢,这里做一下解     
                         释,我们在传参的时候传的是链表的头指针,所以指针a也就是头指针的地址,那        
                         么a->next也就是首元结点的地址了*/
	int i = 0; 
	while(p != NULL)        
	{
		i++;
		p = p->next;        /*从这里就要开始遍历了,遍历结束的条件就是尾节点,这段应该都能看懂,        
                             我就不做多的解释了*/
	}
	return i;
}

到这里为止的话我们已经实现了单链表的创建以及链表长度的测量,肯定有小伙伴就要说了,写了这么多的带码,你倒是写个代码把链表内的所有数据打印出来啊,下面就是打印出链表内的所有数据。

void print(node *a)            //这里打印一下链表内的数据
{
	node *p;                    
	p = a->next;                //这里的前两行和上段带码一样,我就不做多的解释了
	while(p)
	{
		cout<<p->data<<" , ";
		p = p->next;            //  这个代码就是把链表内的数据全部打印,不难好好看看就行
	}
}

那么既然实现了查看所有的数据,那么能不能实现查看某一个结点的数据呢,当然是可以的,下边我用代码进行解释,既然实现的是某一个结点的数据查看,那么我们肯定要知道是哪一个结点,以及是哪一个链表对吧,所以说我们在传参的时候就需要有两个参数,一个链表,一个具体的整数,啊啊啊。。。一边写带码一边做讲解可太难了。

node *search_node(node *p , int pos)        //构造一个函数,用来查看来链表的某一个结点的数据
{
	if(pos < 0)                             //我们的链表的数据就没有比零小的,所以加一个判断
	{
		cout<<"参数有误"<<endl;
		return NULL;
	}
	if(pos == 0)                            //如果是0的话,那就是头节点么
	{
		cout<<"返回头节点"<<endl;
		return p;
	}
	if(p == NULL)                            /*当然了,既然要判断数值的是否正确,肯定也要判断     
                                                链表是否正确,如果是空的话,那就没必要查看了, 
                                                 啥都没有*/
	{
		cout<<"链表有误,这特么就是一个空的"<<endl;
		return NULL;
	}
	while(pos--)                        //程序执行到这一步的话就要开始遍历了
	{
		p = p->next;                    //指针后移一位
		if(p == NULL)
		{
			cout<<"表中没有这个节点"<<endl;    //没找到的话就直接返回一个NULL
			return NULL;
		}
	}
	return p;                    //找到的话就把这个节点返回
}

很多小伙伴在学习单链表的时候都会遇到一个很头疼的问题,那就是头插法和尾插法这俩货,现在我把我对他们俩的理解进行阐述,首先基本的思想就如下边的图示。

 什么是头插法呢,所谓头插法就是说在链表的头结点位置插入新的结点,新的结点也有数据域和指针域,我们将先将其指针域指向头结点的位置,此时的链表中就有两个指针指向了头结点,一个是头指针,另一个就是新的结点的指针,随后将头指针指向新的结点的地址,此时就完成了头插法的操作,理论听的昏昏欲睡,我们上代码吧

//首先是头插法
node *inserthead(node *p)        //参数传的就是我们链表的头指针
{
	node *b  = p;                
	node *a = (node *)malloc(sizeof(node));    //定义一个指针,并为其分配node大小的空间
	a->data = 4;                               /*为了方便起见我就把数据域直接给定了,好奇心强的    
                                                 小伙伴可以自己输入*/
	a->next = b->next;                         /*核心算法来了啊,这一步就是让新的节点的指针域指    
                                                 向头节点的地址*/
	b->next = a;                               //此时把新节点的地址给头指针,那么头插法就结束了
	return b;
}

头插法结束了,我们开始我们的尾插法吧,尾插法的思想其实和头插法区别不是很大,无非就是一个在头位置,一个在尾位置,那么再插入之前我们就要做一个准备工作,就是把指针先指向尾结点的位置,然后进行操作,操作的步骤就是两步,第一步就是让尾指针指向我们新的结点的地址,第二步就是把新的结点的指针域之null,这样就结束了尾插法的操作,是不是又昏昏欲睡了,我们接着上代码

//下来是尾插法
node *insertwei(node *p)
{
	node *b = p;
	node *a = (node *)malloc(sizeof(node));
	a->data = 4;                    //在这之前都和头插法一样,想了解的看看上边的代码
	while(b->next != NULL)
	{
		b = b->next;
	}                        //这个循环的作用就是让我们的指针指向尾结点的位置
	a->next = NULL;            //让新的结点的指针域指向NULL
	b->next = a;            //尾结点的指针域指向新的结点的地址,这样就完成了尾插法
	return p;
}

今天就写到这儿吧,在这里我把主函数贴出来

//进入主函数
int main()
{
	node *a = create();//创建单链表
	node *c = a;
	int i = length(a);
    cout<<"长度是:"<<i<<endl;    //链表求长度
	print(a);
	cout<<endl;

	node *b = search_node(c,5);    //查看链表第五个结点的数据
	cout<<b->data<<endl;

	a = inserthead(a);            //传说中的头插法
	print(a);
	printf("\r\n");

	a = insertwei(a);            //传说中的尾插法
	print(a);

	return 0;
}

完整的代码:上边我用的是C++的输入输出流,为了照顾没学过C++的小伙伴,我把C的代码贴上

//编程实现一个单链表的创建
#include<stdio.h>
#include<malloc.h>
typedef struct node
{
	int data;			//数据域
	node *next;			//指针域
}node;

node *create()
{
	//准备阶段
	int i = 0;
	node *head , *p , *q;
	int x = 0;
	head = (node *)malloc(sizeof(node));
	//变量分别是:链表中的数据个数、头结点等;
	
	//以下开始进入循环创建每一个结点的数据
	while(1)
	{
		printf("please input the data:");
		scanf("%d",&x);
		if(x == 0)
		{
			break;
		}
		p = (node *)malloc(sizeof(node));
		p->data = x;
		if(++i == 1)
		{
			head->next = p;
		}
		else{
			q->next = p;
		}
		q = p;
	}
	q->next = NULL;
	return head;
}	

/*单链表的求长度*/
int length(node *a)
{
	node *p;
	p = a->next;
	int i = 0; 
	while(p != NULL)
	{
		i++;
		p = p->next;
	}
	return i;
}
//单链表的结点查找
node *search_node(node *p , int pos)
{
	if(pos < 0)
	{
		printf("参数有误");
		return NULL;
	}
	if(pos == 0)
	{
		printf("返回头结点");
		return p;
	}
	if(p == NULL)
	{
		printf("链表有误,这特么就是一个空的");
		return NULL;
	}
	while(pos--)
	{
		p = p->next;
		if(p == NULL)
		{
			printf("表中没有这个结点");
			return NULL;
		}
	}
	return p;
}
void print(node *a)
{
	node *p;
	p = a->next;
	while(p)
	{
		printf("%d,",p->data);
		p = p->next;
	}
}
/*从此开始就是插入操作*/
//首先是头插法
node *inserthead(node *p)
{
	node *b  = p;
	node *a = (node *)malloc(sizeof(node));
	a->data = 4;
	a->next = b->next;
	b->next = a;
	return b;
}
//下来是尾插法
node *insertwei(node *p)
{
	node *b = p;
	node *a = (node *)malloc(sizeof(node));
	a->data = 4;
	while(b->next != NULL)
	{
		b = b->next;
	}
	a->next = NULL;
	b->next = a;
	return p;
}
//进入主函数
int main()
{
	node *a = create();//创建单链表
	node *c = a;
	int i = length(a);
	printf("长度是:%d\r\n",i);
	print(a);
	printf("\r\n");
	node *b = search_node(c,5);
	printf("%d\r\n",b->data);
	
	a = inserthead(a);
	print(a);
	printf("\r\n");
	a = insertwei(a);
	print(a);
	return 0;
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子一个小李

如果觉得有帮助的话,还请打赏呀

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值