链式存储【链表】的定义及使用(附带实现代码)

链式存储【链表】

1.链表的定义

逻辑上连续,物理上不连续

彼此通过指针相连

每个结点只有一个前驱结点,每个结点只有一个后继结点

首结点只有一个前驱,尾结点只有一个后继

typedef struct LNode {
	int data;	//数据域
	pLNode pNext;	//指针域,pNext是struct Node*类型
}LNode,*pLNode;
(1)链表的特点
  • 在内存中可以存在任何地方,不要求连续。
  • 每一个数据都保存了下一个数据的内存地址,通过这个地址找到下一个数据。 第一个人知道第二个人的座位号,第二个人知道第三个人的座位号……
  • 增加数据和删除数据很容易。 再来个人可以随便坐,比如来了个人要做到第三个位置,那他只需要把自己的位置告诉第二个人,然后问第二个人拿到原来第三个人的位置就行了。其他人都不用动。
  • 查找数据时效率低,因为不具有随机访问性,所以访问某个位置的数据都要从第一个数据开始访问,然后根据第一个数据保存的下一个数据的地址找到第二个数据,以此类推。 要找到第三个人,必须从第一个人开始问起。
  • 不指定大小,扩展方便。链表大小不用定义,数据随意增删。
(2)专业术语:

首结点:

第一个有效结点

尾结点:

最后一个有效结点

头结点:

头结点的数据类型和首结点类型一样

第一个有效结点之前的那个结点
头结点并不存放有效数据
加头结点的目的主要是为了方便对链表的操作

头指针:

指向头结点的指针变量

尾指针:

指向尾结点的指针变量

(3)如果希望通过一个函数来对链表进行处理,我们至少需要接收链表的哪些参数

如果希望通过一个函数来对链表进行处理,我们至少需要接收链表的哪些参数

只需要一个参数:头指针

以为我们通过头指针可以推算出链表的其他所有参数

2.分类

(1)单链表
(2)双链表

每一个结点有两个指针域

(3)循环链表

能透过任何一个结点找到其他所有结点

(4)非循环链表

3.算法

(1)主函数和函数声明
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>

typedef struct LNode {
	int data;	//数据域
	struct LNode* pNext;	//指针域,pNext是struct Node*类型
}LNode, * pLNode;

pLNode create_list();//创建链表
void show_list(pLNode pHead);//遍历链表
bool is_empty(pLNode pHead);//判断是否为空
int length_list(pLNode pHead);//求链表长度
bool insert_list(pLNode pHead, int pos, int val);//插入
bool delete_list(pLNode pHead, int pos, int* val);//删除
void sort_list(pLNode pHead);//排序

int main() {
	int val, pos;
	pLNode pHead = NULL;	//等价于struct Node*pHead= NUll
	//初始化
	pHead = create_list();
	show_list(pHead);

	//链表长度
	printf("链表长度为%d\n", length_list(pHead));//链表长度

	//排序
	sort_list(pHead);
	show_list(pHead);

	//插入
	printf("请输入想要插入到第几个结点之后:");
	scanf_s("%d", &pos);
	printf("请输入想要插入的值:");
	scanf_s("%d", &val);
	if (insert_list(pHead, pos, val))
	{
		printf("插入成功\n");
	}
	show_list(pHead);

	//删除
	printf("请输入想要删除的结点位置:");
	scanf_s("%d", &pos);
	if (delete_list(pHead, pos, &val))
	{
		printf("删除成功\n");
		printf("删除的值为:%d\n", val);
	}
	show_list(pHead);

	return 0;
}
(2)分配地址初始化
pLNode create_list() //创建链表
{
	int len;	//用来存放有效结点的个数
	int val;	//用来临时存放用户输入的结点的值
	//分配了一个不存放有效数据的头结点
	pLNode pHead = (pLNode)malloc(sizeof(LNode));
	if (pHead == NULL)
	{
		printf("分配失败\n");
		exit(-1);
	}
	pLNode pTail = pHead;	//pTail永远指向尾结点
	pTail->pNext = NULL;
	printf("请输入您要生成的链表结点的个数:len=");
	scanf_s("%d", &len);
	for (int i = 0; i < len; i++)
	{
		printf("请输入第%d个数", i + 1);
		scanf_s("%d", &val);
		//分配一个新的结点
		pLNode pNew = (pLNode)malloc(sizeof(LNode));
		if (pNew == NULL)
		{
			printf("分配失败\n");
			exit(-1);
		}

		pNew->data = val;
		pTail->pNext = pNew;
		pNew->pNext = NULL;	//清空尾结点的指针域
		pTail = pNew;	//更新尾结点
	}
	return pHead;
}
(3)遍历链表
void show_list(pLNode pHead)	//遍历链表
{
	if (is_empty(pHead) == false)
	{
		pLNode p = pHead->pNext;
		printf("链表数据为:", p->data);
		while (p != NULL)
		{
			printf("%d  ", p->data);
			p = p->pNext;
		}
		printf("\n");
	}
	else
	{
		printf("链表为空\n");
	}
}
(4)求链表长度
int length_list(pLNode pHead)//求链表长度
{
	int i = 0;
	if (is_empty(pHead) == false)
	{
		pLNode p = pHead->pNext;
		while (p != NULL)
		{
			p = p->pNext;
			i++;
		}
	}
	return i;
}
(5)插入结点
bool insert_list(pLNode pHead, int pos, int val)//插入,pos从1开始
{
	int i = 1;
	pLNode p = pHead;
	while (p != NULL && i <= pos)	//通过循环,寻找到pos的位置
	{
		p = p->pNext;
		i++;
	}
	if (i > pos || p == NULL)
	{
		printf("位置不合法,插入失败\n");
		return false;
	}
	pLNode pNew = (pLNode)malloc(sizeof(LNode));
	pLNode z = p->pNext;	//临时结点
	p->pNext = pNew;
	pNew->data = val;
	pNew->pNext = z;
	return true;
}
(6)删除结点
bool delete_list(pLNode pHead, int pos, int* val)	//删除
{
	int i = 1;
	pLNode p = pHead;
	while (p != NULL && i < pos)	//通过循环,寻找到pos前面的位置
	{
		p = p->pNext;
		i++;
	}
	if (i > pos || p == NULL)
	{
		printf("位置不合法,插入失败\n");
		return false;
	}
	pLNode z = p->pNext;	//临时结点
	*val = z->data;
	p->pNext = z->pNext;
	free(z);
	z = NULL;	//避免野指针
	return true;
}
(7)排序
void sort_list(pLNode pHead)	//排序
{
	int i, j, t;
	int len = length_list(pHead);
	pLNode p;
	pLNode q;
	if (is_empty(pHead) == false)
	{
		for (i = 0, p = pHead->pNext; i < len - 1; i++, p = p->pNext)
		{
			for (j = i + 1, q = p->pNext; j < len; j++, q = q->pNext)
			{
				if (p->data < q->data)	//类似于数组中的:a[i] < a[j]
				{
					t = p->data;	//类似于数组中的:t = a[i]
					p->data = q->data;	//类似于数组中的:a[i] = a[j]
					q->data = t;	//类似于数组中的:a[j] = t
				}
			}
		}
	}
	else
	{
		printf("链表为空\n");
	}
}

4.优缺点

(1)链表的优点
  • 插入删除速度快

在插入和删除操作时,只需要修改被删节点上一节点的链接地址,不需要移动元素,从而改进了在顺序存储结构中的插入和删除操作需要移动大量元素的缺点。

  • 内存利用率高,不会浪费内存
  • 大小没有固定,拓展很灵活。
(2)链表的缺点
  • 不能随机查找,必须从第一个开始遍历,查找效率低
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
存储结构的链表元素删除算法,可以分为以下几个步骤: 1. 首先找到要删除的节点,遍历整个链表,直到找到目标节点。 2. 找到目标节点后,将其前一个节点的指针指向目标节点的后一个节点,即跳过了目标节点,将其删除。 3. 释放目标节点的内存空间,防止内存泄漏。 下面是采用单链表实现链表元素删除算法的C语言代码实现: ```c #include <stdio.h> #include <stdlib.h> // 定义链表节点结构体 typedef struct Node { int data; struct Node *next; } Node; // 创建链表 Node* createList() { Node *head = (Node*)malloc(sizeof(Node)); // 创建头节点 head->next = NULL; return head; } // 在链表尾部插入节点 void insertAtTail(Node *head, int val) { Node *p = head; while (p->next != NULL) { p = p->next; // 找到链表尾部 } Node *newNode = (Node*)malloc(sizeof(Node)); // 创建新节点 newNode->data = val; newNode->next = NULL; p->next = newNode; // 在尾部插入新节点 } // 删除链表中指定元素 void deleteNode(Node *head, int val) { Node *p = head->next; Node *pre = head; // 指向p的前一个节点 while (p != NULL && p->data != val) { pre = p; p = p->next; } if (p != NULL) { // 找到目标节点 pre->next = p->next; // 跳过目标节点 free(p); // 释放目标节点内存空间 } } // 打印链表 void printList(Node *head) { Node *p = head->next; while (p != NULL) { printf("%d ", p->data); p = p->next; } printf("\n"); } int main() { Node *head = createList(); insertAtTail(head, 1); insertAtTail(head, 2); insertAtTail(head, 3); insertAtTail(head, 4); printf("Before delete: "); printList(head); deleteNode(head, 3); printf("After delete: "); printList(head); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

PCGuo999

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值