IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 线性表——链表 -> 正文阅读

[数据结构与算法]线性表——链表

链表

顺序表和链表都是线性表的一种。但链表与顺序表不同的是,他的物理上与逻辑上的机构并不一致。
顺序表的话,逻辑相邻,物理上也是相邻的。所以对于一整块连续的物理地址,当我们进行插入和删除操作的时候就会需要大量的移动元素。
而链表不需要使用地址连续的存储单元,即不需要满足物理与逻辑一致,通过"链"建立数据之间的逻辑关系,当进行插入或删除操作的时候,只需要修改指针即可。

单链表

线性表的链式存储为单链表,通过任意一组存储单元来存储数据。链表通过许多节点构成,节点除了放自身的数据还有存放下一个节点的地址来保证链表的逻辑关系。而链表一般只记录头节点。结构如下:

typedef struct node{
	int data;
	node *next;
}node;

typedef struct chainlist{
	node *head;
	int size;
}clist;

可以看到虽然链表解决了顺序表需要大量连续空间的问题,使其更灵活,但是也可以清楚的看到,每一个节点除了放置数据,还要多存放一个地址,那么这样的话一个数据元素所占据的空间就更大。

因为单链表的数据存放不再是连续的存储单元,所以其存储结构是非随机存取,不可以直接找到表中的某个特定节点,只能在每次寻找时从头遍历。

虚拟头节点

通常用头指针来标识一个单链表,当头指针为NULL时,则为空表。另外,为了简化操作,通常也会在链表第一个节点前加一个虚拟头节点。虚拟头不存放数据,或其存放数据是无意义的,但是他只想链表的第一个节点。

引入头节点的好处:

  1. 因为头指针指向第一个节点,而其余节点由上一个节点指向,所以在操作上有了虚拟头节点使得整个链表变得一致
  2. 无论链表是否为空,其头指针都指向虚拟头而非空指针,因此也空表和非空表的处理上也得到了统一

单链表实现

#include<stdio.h>
#include<stdlib.h>

typedef struct node{
	int data;
	node *next;
}node; 

typedef struct clist{
	node *head;
	int size;
}clist;
 

这是刚刚写好的代码先定义好链表和节点。以及头文件引用。


node *newNode(int val){
	node *n = (node *)malloc(sizeof(node));
	n->data = val;
	n->next = NULL;
	return n;
} 

写一个创建节点的方法,因为在我们插入数据的时候,根据数据要求申请地址,所以需要更方便的存储。


clist *initChainList(){
	clist *l = (clist *)malloc(sizeof(clist));
	l->head = newNode(-1);
	l->size = 0;
	return l;	 
}

void deleteChainList(clist *l){
	node *temp;
	temp = l->head;
	while(temp){
		node *t2 = temp->next;
		free(temp);
		temp = t2;
	}
	free(l);
}

然后就是创建初始化链表的方法以及删除链表的方法,同样为了避免内存泄漏在删除链表的同时要先释放每个节点的空间,因为在链表中只记录头指针,所以我们需要先进行遍历释放节点。最后再释放链表。



int insertElement(clist *l, int loc, int val){
	
	if(loc > l->size){
		return 0;
	}
	node *n = newNode(val);
	node *t = l->head;
	while(loc--){
		t = t->next;
	}
	n->next = t->next;
	t->next = n;
	l->size++;
	return 1;
} 

int deleteElement(clist *l, int loc){
	if(loc > l->size || loc <= 0){
		return 0;
	}
	node *t1 = l->head;
	while(--loc){
		t1 = t1->next;
	}
	node *t2 = t1->next;
	t1->next = t1->next->next;
	free(t2);
	l->size--;
	return 1;	
}

同顺序表一样我们需要两个操作链表的方法插入和删除,可以看到这里的插入删除操作要简单,只需要改变一下指针就好,但是在寻找时我们只能通过遍历的方式,这里就比顺序表麻烦。在这里同样也可以看到,我在插入的时候并不是选择头插法或尾插法,而是按照顺序表那样指定位置插入,可以做一个很好的比对。注意做完操作后对size也要进行相应的加减操作。


void showChainList(clist *l){
	node *t = l->head;
	while(t && t->next){
		node *t2 = t->next;
		printf("%d ", t2->data);
		t = t2;
	}
	printf("\n");

同样在最后的时候加上一个输出函数,方便我们检验实现效果。


int main(){
	
	clist *p = initChainList();

	
	int a,b,c;
	a = 0;
	while(a!=4){
		printf("输入您的操作指令:");
		scanf("%d %d",&a ,&b);
		if(a == 1){
			//InsertData
			scanf("%d", &c);
			insertElement(p, b, c);
			showChainList(p);
		}
		if(a == 2){
			deleteElement(p, b);
			//DeleteData
			showChainList(p);
		}
		if(a == 3){
			deleteChainList(p);
		}
	}
}

主程序代码和顺序表的差不多。看一下效果

当然我们也可以实现头插法或尾插法,或按序号和值查找节点等等一般线性表的操作。
以上就是单链表的全部内容。

双链表

在单链表中节点只可以指向后面的节点,那么在双链表里节点除了指向下一个节点,也可以指向前一个节点,我么把节点的前后节点分别称为前驱和后继。双链表的引入对于尾插和删除链表的最后一个元素的效率大大提高。双链表的定义如下:

//节点定义相同
struct chainlist{
	node *prior, *next;
	int data;
}

同单链表定义相同只是加了一个前驱指针。所以对操作来讲变化不大,只需要在上面代码的基础上加上前驱变化即可,这里不做演示。

循环链表

循环单链表

循环链表顾名思义就是链表会构成一个环,也就是说在链表最后一个节点的指向不再是NULL而是指向头节点,当首尾相连之后实际上就没有了头尾的概念,但是处于链表的特点我们需要经常遍历,所以我们的size的作用就显现出来了。但是在进行插入操作的时候因为环是没有首尾的所以插在任何地方的效果都是等价的。

循环双链表

同理就是在循环单链表的基础上在每个节点上加上指向前驱的指针

静态链表

静态链表借助数组来描述线性表的链式结构,与之前节点地址指针不同的是,指向下一个节点的是数组相对应的下标。同样也是需要先分配一块连续的内存空间

struct chainlist{
	int data;
	int next;
}

静态链表以next = -1 为结束标志,静态链表的插入删除与动态链表相同,只需要修改指针而不需要移动元素。但是使用起来并不方便,只适用不支持指针的高级语言,但又有该方面需求的场景。

顺序表和链表

顺序表链表
存取方式顺序存取,随机存取链表更为复杂只能遍历
逻辑结构与物理结构逻辑相邻即物理相邻逻辑相邻,但物理不一定相邻
按值查找无序时为O(n),有序时可采用折半查找为O( l o g 2 n log_2n log2?n)O(n)
按序号查找支持随机访问为O(1)O(n)
插入、删除操作需要大量的数据元素移动只需要调整指针即可
分配空间静态分配需要考虑大小,动态分配也需要数据移动,而且需要大量连续的空间只在需要时申请分配,,有空间即可分配。更加灵活高效

因为链表的每个节点都有指针域,所以链表数据密度要小于顺序表

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-07-04 23:13:18  更:2022-07-04 23:16:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:28:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码