数据结构—单链表

一、链表

(1)定义:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链。
(2)链表的基本知识

链表是一种动态的存储结构,所谓动态就是可以根据表中结点的个数来自动的分配合适的空间。也就是说,当插入结点时,系统自动申请一块空间,进行插入;当删除结点时,系统自动回收该结点所占用的空间,从而避免了空间的浪费。

链表在物理位置上并不相邻,但是在逻辑上是相邻的。

(2)链表的结构
  1. 带头、不带头
  2. 单向、双向
  3. 循环、非循环

将他们组合起来就有8种链表结构:带头单向循环链表、带头单向非循环链表、带头双向循环链表、带头双向非循环链表、不带头单向循环链表、不带头单向非循环链表、不带头双向循环链表、不带头双向非循环链表。

我们通常所说的单链表就是带头单向非循环链表

(3)链表的优缺点

二、单链表

单链表的结构

在这里插入图片描述
解析:

  1. 其中head表示的是头结点,一般来说头结点不用来存放信息,它可以看做单链表的入口地址,关于链表的所有的操作都是从头结点开始的,它指向的是链表中的第一个结点。

  2. 单链表中的每一个结点都由两部分组成。其中表示数据元素的部分叫做数据域,也就是A、B、C等具体的数据;表示直接后继的部分叫做指针域,它里面存放的是下一个结点的地址。

  3. 注意:最后一个结点的指针域一定要置为空。

三、单链表主要操作的实现

(1)插入操作
1. 头插:
将新结点插入到链表的第一个位置,换句话说,就是将新结点插入到head结点之后

如下图所示:
在这里插入图片描述
过程:

  1. 将新结点p连接到原先第一个结点之前
    p->next=head->next
  2. 将结点p连接到头结点之后
    head->next=p

注意:这个顺序不能弄反。

代码如下

void ListLink_InsertHead(LNode* head,DataType data)
{
	LNode* p;                                       
	p = (LNode*)malloc(sizeof(LNode));                
	p->data = data;
	p->next = head->next;         
	head->next = p;                         
}

2. 尾插: 将新结点插入到最后一个结点的后面
如下图所示:
在这里插入图片描述
过程:

  1. 找到最后一个结点(通过遍历)
  2. 将新结点连接到最后一个结点后面
  3. 新结点的next置为空

代码如下

void ListLink_InsertTail(LNode* head,DataType data)
{
	LNode* p;                     //代替head的作用
	LNode* q;                     //要插入的新节点
	q= (LNode*)malloc(sizeof(LNode));
	q->data = data;
	q->next = NULL;
	p = head;
	while (p->next != NULL)        //这个循环是为了找到最后一个结点
	{
		p = p->next;
	}
	//将新结点从最后一个结点后面插入
	p->next = q;
}

3. 随意插入: 在第i个位置处插入(包括第一个和最后一个位置)
如下图所示:
在这里插入图片描述

过程:

  1. 找到第i-1个结点
  2. 将第i个结点连接到新结点的后面
  3. 将新结点连接到第i-1个结点的后面

代码如下:

void ListLink_Insert(LNode* head, DataType data,DataType i)         //其中这个i是要插入的位置      
{
	LNode* p;                 
	p = head;
	LNode* q;                               //要插入的新节点
	q = (LNode*)malloc(sizeof(LNode));
	q->data = data;
	q->next = NULL;
	int j = 0;

	while (p->next != NULL && j < i - 1)
	{
		j++;
		p = p->next;                        //循环完毕后,p是第i-1个结点
	}

	q->next = p->next;                      //将第i个结点连接到新结点的后面
	p->next = q;                            //将新结点连接到第i-1个结点的后面
}

(2)删除操作
1. 头删: 删除链表的第一个结点
如下图所示:

在这里插入图片描述

过程:

  1. 先将第二个结点连接到头结点之后
  2. 再释放第一个结点

代码如下:

void ListLink_DeleteHead(LNode* head)
{
	LNode* p;                              //p是要删除的结点
	if (ListLink_IsEmpty(&L) == -1)         //删除之前首先要判空,判空函数会在下面写到
		return;
	p = head->next;
	head->next = p->next;
	free(p);
}

2. 尾删: 删除链表中最后一个元素
如下图所示:
在这里插入图片描述

过程:

  1. 找到倒数第二个结点
  2. 释放最后一个结点
  3. 将原倒数第二个结点的next置为空

代码如下:

void ListLink_DeleteTail(LNode* head)
{
	if (ListLink_IsEmpty(&L) == -1)         //删除之前首先要判空
		return;
	LNode* p,*q;
	p = head;
	while (p->next->next!= NULL)          //循环结束后,p是倒数第二个结点
	{
		p = p->next;              
	}
	q = p->next;                                   //q是最后一个结点
	free(q);
	p->next = NULL;
}

3. 随意删除: 删除第i个结点(包括头删和尾删)
如下图所示:
在这里插入图片描述

过程:

  1. 找到第i-1个结点
  2. 将第i+1个结点连接到第i-1个结点后面

代码如下:

void ListLink_delete(LNode* head,DataType i)
{

	LNode* p,*q;
	DataType j = 0;
	p = head;
	while (j < i - 1 && p->next != NULL)
	{
		j++;
		p = p->next;               //p是第i-1个元素
	}
	q = p->next;                   //q是第i个元素
	p->next = q->next;             //当q是最后一个结点时,q->next==NULL
	free(q);                  
}

四、完整的单链表的实现

上面所说的主要操作是单链表中比较难的部分,所以我将他们单独拿出来进行主要的讲解。但是一个单链表并不仅仅只是刚才所将的几个操作,只是其他操作较为简单,所以就不进行过多的叙述。只是在代码中进行适当的解释。

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
typedef int DataType;

struct Node                      //这个结构体定义的链表中各个节点的类型
{
	DataType data;             //数据域
	struct Node* next;         //指针域
}L;								//结构体变量


typedef struct Node LNode;       

//链表的初始化其实就是将链表置为空
void ListLink_Init(LNode* head)
{
	head->next == NULL;
}

//判断链表是否为空
DataType ListLink_IsEmpty(LNode* head)
{
	if (head->next == NULL)         //如果链表中是空的就返回-1,否则返回1
		return -1;
	else
		return 1;
}

//头插:只从头部插入,也就是插入到第一个位置
void ListLink_InsertHead(LNode* head,DataType data)
{
	LNode* p;                                       
	p = (LNode*)malloc(sizeof(LNode));                
	p->data = data;
	p->next = head->next;         
	head->next = p;                         
}
//尾插
void ListLink_InsertTail(LNode* head,DataType data)
{
	LNode* p;                   //代替head的作用
	LNode* q;                   //要插入的新节点
	q= (LNode*)malloc(sizeof(LNode));
	q->data = data;
	q->next = NULL;
	p = head;
	while (p->next != NULL)      //这个循环是为了找到最后一个结点
	{
		p = p->next;
	}
	//将新结点从最后一个结点后面插入
	p->next = q;
}

//插入:从第i个位置上插入结点(任意位置插入)

void ListLink_Insert(LNode* head, DataType data,DataType i)         //其中这个i是要插入的位置      
{
	LNode* p;                   
	p = head;
	LNode* q;                   //要插入的新节点
	q = (LNode*)malloc(sizeof(LNode));
	q->data = data;
	q->next = NULL;
	DataType j = 0;

	while (p->next != NULL && j < i - 1)
	{
		j++;
		p = p->next;                          //循环完毕后,p是第i-1个结点
	}

	q->next = p->next;
	p->next = q;
}

//头删:删除第一个结点
void ListLink_DeleteHead(LNode* head)
{
	LNode* p;
	if (ListLink_IsEmpty(&L) == -1)         //删除之前首先要判空
		return;
	p = head->next;
	head->next = p->next;
	free(p);
}
//尾删
void ListLink_DeleteTail(LNode* head)
{
	if (ListLink_IsEmpty(&L) == -1)         //删除之前首先要判空
		return;
	LNode* p,*q;
	p = head;
	while (p->next->next!= NULL)          //循环结束后,p是倒数第二个结点
	{
		p = p->next;              
	}
	q = p->next;                          //q是最后一个结点
	free(q);
	p->next = NULL;
}

//删除:删除第i个位置的结点(任意位置删除)

void ListLink_delete(LNode* head,DataType i)
{

	LNode* p,*q;
	DataType j = 0;
	p = head;
	while (j < i - 1 && p->next != NULL)
	{
		j++;
		p = p->next;
	}
	q = p->next;                 //q是第i个元素
	p->next = q->next;
	free(q);                   //当q是最后一个结点时,q->next==NULL
}
//根据位置查找:查找第i个结点,并且返回第i个结点
LNode* ListLink_FindLocate(LNode* head, DataType i)
{

	LNode* p;
	p = head;
	DataType j = 0;
	while (j < i && p->next != NULL)
	{
		j++;
		p = p->next;
	}
	return p;
}

//根据数字查找,返回与指定数字相同的结点
LNode* ListLink_FindFigure(LNode* head, DataType data)
{
	LNode* p;
	p = head->next;
	while (p != NULL)
	{
		if (p->data == data)
		{
			return p;
		}
		p = p->next;
	}
}

//销毁链表(包括头结点在内的所有结点)
//从头结点开始依次释放
void ListLink_Destory(LNode* head)
{
	LNode* p;
	while (head->next != NULL)
	{
		p = head;
		head = head->next;
		free(p);
	}
}

//求链表的长度
DataType ListLink_GetLength(LNode* head)
{
	LNode* p;
	DataType count = 0;
	p = head->next;
	while (p != NULL)
	{
		count++;
		p = p->next;
	}
	return count;
}
//输出链表中的每一个结点
void ListLink_Display(LNode* head)
{
	LNode* p;
	p = head->next;
	while (p != NULL)
	{
		printf("%d  ", p->data);
		p = p->next;
	}
}

//在main函数中给出一些具体的调用,这样大家知道如何使用这些函数
int main()
{
	ListLink_Init(&L);
	ListLink_InsertHead(&L, 3);
	ListLink_InsertHead(&L,5);
	ListLink_InsertHead(&L,7);
	ListLink_InsertHead(&L, 1);
	ListLink_InsertHead(&L, 39);

	ListLink_InsertTail(&L, 32);

	ListLink_Display(&L);

	system("pause");
	return 0;
}

五、小技巧

  1. 对链表进行遍历时,如果p指向的是头结点,那么while循环结束的标志是
    p->next!=NULL,并且当遍历结束时,此时的p指向的是最后一个结点
p=head;
while(p->next!=NULL)
{
	p=p->next;
}
  1. 对链表进行遍历时,如果p指向的是第一个结点,那么while循环结束的标志是
    p!=NULL,并且当遍历结束时,此时的p指向的是最后一个结点的后面一个不知道的空间
p=head->next;
while(p!=NULL)
{
	p=p->next;
}
  1. 在链表中只要要找第i-1个结点,代码其实是固定的
//第一种:当p=head时
int j=0;
p=head;
while(p->next!=NULL && j<i-1)
{
	j++;
	p=p->next;                   //循环完毕后p是第i-1个结点
}

//第二种:p=head->next
int j =0;
p=head->next;
while(p!=NULL && j<i-1)
{
	p=p->next;
	j++;
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值