单向链表

Posted #呆呆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单向链表相关的知识,希望对你有一定的参考价值。

《数据结构严蔚敏第二版》2.52,单向链表的链式表示与实现

(来了来了兄弟们,小手点一下右上角,来一波博主养成关注)
接第一篇博客,线性表包括顺序实现和链式实现,两者的底层存储区别体现在顺序表是开辟连续地址空间存储,而链表则不用连续地址空间存储,哪儿有空位往哪儿插入数据,对存储空间要求不高。 ----这是我的大白话(以下是书上正儿八经的解释)
链表和顺序表空间性能的比较:
存储空间:
由于顺序表的存储空间必须预先分配,容易造成存储空间浪费或者空间溢出(简单来说就是自己所需要使用的空间比我们拥有的内存大内存不够使用所造成的内存溢出)
链表不需要预先分配空间,只要内存允许 ,链表元素个数就不受限制。
存储密度:
顺序表的存储方式可以知道,一个结点只存数据,所以存储密度是1,而链表的存储方式为数据加指针,链表的不同,则数据和指针也不同,比如单链表一个结点只有一个指针,而双向链表一个结点有两个指针,那么链表的存储密度是必然小于1的。
就选择使用线性表方法来说,如果表的长度变化不大,则选择使用顺序表,反之,则选择链表。

单向链表的操作实现:

首先创建链表第一步预设结点类型:

typedef struct List{
	int data;    //数据域
	struct list* next;    //指针域
}node;      //node为节点名,每一个节点都是List结构体

初始化:
1.头节点初始化:

llist *CreatHead()
{
	list *p=(list*)malloc(sizeof(list));//给头节点开辟地址空间,一个就OK勒 
	p->next=NULL;//头节点可以选择不用初始化数据域,只用把头结点的指针置空就Ok勒 
	return p;//返回头节点,这样我们就可以通过头结点的指针找到整个链表辣 
} 

2.我们初始化整个链表,就整五个节点吧,太多了看着迷糊:

头插法:

void CreatList(list* p)
{
	for(int i=0;i<5;i++)//就整五个 
	{
		list *n=(list*)malloc(sizeof(list));//开辟一个新鲜的存储空间给新的节点
		//节点创建好了,那么我们就考虑下一步初始化这个节点的数据域,整简单点,节点的数据置为节点的次序
		n->data=i;
		//接下来就是用新节点的指针域将新的节点和头节点链接起来
		n->next=p->next;
		p->next=n;
		//简简单单两句话链接起来
		}
 } 

3.拉进主函数,打印出来看看啥样:

#include<stdio.h>
#include<stdlib.h>
typedef struct list{
	int data;
	list* next;
}node; 
list *CreatHead()
{
	list *p=(list*)malloc(sizeof(list));//给头节点开辟地址空间,一个就OK勒 
	p->next=NULL;//头节点可以选择不用初始化数据域,只用把头结点的指针置空就Ok勒 
	return p;//返回头节点,这样我们就可以通过头结点的指针找到整个链表辣 
} 
void CreatList(list* p)
{
	for(int i=0;i<5;i++)//就整五个 
	{
		list *n=(list*)malloc(sizeof(list));//开辟一个新鲜的存储空间给新的节点
		//节点创建好了,那么我们就考虑下一步初始化这个节点的数据域,整简单点,节点的数据置为节点的次序
		n->data=i;
		//接下来就是用新节点的指针域将新的节点和头节点链接起来
		n->next=p->next;
		p->next=n;
		//简简单单两句话链接起来
		 
		 
	}
 } 
 PrintfList(list * p) 
 {
 	printf("链表数据如下:\\n");
 	while(p->next!=NULL)
 	{
	    p=p->next;
 		printf("%d ",p->data);
	 }
	 printf("\\n"); 
 }
 int main()
 {
 	list* p=CreatHead();//接收头节点
 	CreatList(p);//传入头节点初始化整条链表
 	PrintfList(p);//简简单单打印一下;
  } 

贴上运行结果:

兄弟们看到有什么异样没有?是的没错,本来我们是按0 1 2 3 4 的顺序把数据装进链表里的,为什么打印出来就是反的啊,这是为什么呀?
俺来分析一下代码块:
n->next=p->next;
p->next=n;
首先链表还未链接的时候只有一个头节点,头结点的指针置空了,第一个节点来了;那么我们n->next=p->next;的意思就是把新节点的指针指向头节点的指向即为NULL,p->next=n;头结点的指针再指向第一个节点
如图所示:


然后新的节点再接入头节点与上一个节点之间,这种插入新节点的方法称为头插法,酱紫搞的话,新进来的元素就挨个排后面去了,头插输入输出实现的功能是先进后出,相反的,尾插输入输出实现的功能就是先进先出:

尾插法:

void CreatList(list* p)
{
	list *end=p;//整一个尾节点 ,先把尾节点置为头节点 
	for(int i=0;i<5;i++)//就整五个 
	{
		list *n=(list*)malloc(sizeof(list));//开辟一个新鲜的存储空间给新的节点
		//节点创建好了,那么我们就考虑下一步初始化这个节点的数据域,整简单点,节点的数据置为节点的次序
		n->data=i;
		n->next=end->next; 
		end->next=n;
		end=n;//尾节点跟着新节点位置跑 头节点保持不变就链接起来喽 
	}
 } 

拉进主函数我们看看咋样:

#include<stdio.h>
#include<stdlib.h>
typedef struct list{
	int data;
	list* next;
}node; 
list *CreatHead()
{
	list *p=(list*)malloc(sizeof(list));//给头节点开辟地址空间,一个就OK勒 
	p->next=NULL;//头节点可以选择不用初始化数据域,只用把头结点的指针置空就Ok勒 
	return p;//返回头节点,这样我们就可以通过头结点的指针找到整个链表辣 
} 
void CreatList(list* p)
{
	list *end=p;//整一个尾节点 ,先把尾节点置为头节点 
	for(int i=0;i<5;i++)//就整五个 
	{
		list *n=(list*)malloc(sizeof(list));//开辟一个新鲜的存储空间给新的节点
		//节点创建好了,那么我们就考虑下一步初始化这个节点的数据域,整简单点,节点的数据置为节点的次序
		n->data=i;
		n->next=end->next; 
		end->next=n;
		end=n;//尾节点跟着新节点位置跑 头节点保持不变就链接起来喽 
	}
 } 
 PrintfList(list * p) 
 {
 	printf("链表数据如下:\\n");
 	while(p->next!=NULL)
 	{
	    p=p->next;
 		printf("%d ",p->data);
	 }
	 printf("\\n"); 
 }
 int main()
 {
 	list* p=CreatHead();
 	CreatList(p);

 	PrintfList(p);
  } 

打印结果:

这样式儿的打印出来就是先进先出喽。
接下来看看单链表的增删查(改太简单了,只要查到了节点有手就行,这里就不讲改了)

单链表增加

这个功能实现,咱们第一步得找到要插入节点的位置,插入节点,OK完活!

 void InsertList(list *p,int add, int element)
{
	int i=0;
	while(p->next!=NULL)
	{
		p=p->next;
		i++;
		if(i==add)//找到位置喽
		{
			list *newnode=(list*)malloc(sizeof(list));
			newnode->data=element;
			newnode->next=p->next;
			p->next=newnode; 
		}
	}
}

试试在在第二位数据与第三位数据之间插入元素3:

删除节点

两个操作:1.找到要删除的节点,更改这个节点的前驱后继链接
2.释放掉该节点所占的地址空间(为一名合格的程序员,要对存储空间负责,对不再利用的存储空间要及时释放。QAQ)
简简单单咱们删除第一个节点试试:

void DeleteNode(list *p,1)
{
	list *pre=p;
	list *temp=p; 
	int i=0;
	while(p->next!=NULL)
	{
	   pre=p;
	   p=p->next;  
	   i++;
	   if(i==add)
	   {
	   	pre->next=p->next;
	   	p->next=NULL;
	   	temp=p; 
		} 
	   	}
	   	free(temp);
}

贴上结果:

查找:

肥肠简单,遍历链表+比对信息即可:

void querylist(list *p,int q)//q为要删除的数据信息
{
	int i=0;
	while(p->next!=NULL)
	{
		p=p->next;
		i++;
		if(p->data==q)
		{
			printf("查找数据位于第%d位\\n",i);
			break;
		 } 
	 } 
}

贴上运行结果:

怎么样,是不是真的肥肠简单哇,不会的童鞋,多康康就会了哟,好了,今天就更这些了,明天更新循环和双向链表的内容,谢谢大家(0.0).

以上是关于单向链表的主要内容,如果未能解决你的问题,请参考以下文章

链表的java实现(单向双向链表,单向链表的反转)

链表的java实现(单向双向链表,单向链表的反转)

数据结构单向链表及其Java代码实现

单向链表 代码实现

C提高 7   单向链表,传统链表,通用链表,linus天才哲学代码

6L-单向链表实现