数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。
数据结构的研究内容是构造复杂软件系统的基础,它的核心技术是分解与抽象。通过分解可以划分出数据的3个层次;再通过抽象,舍弃数据元素的具体内容,就得到逻辑结构。类似地,通过分解将处理要求划分成各种功能,再通过抽象舍弃实现细节,就得到运算的定义。上述两个方面的结合可以将问题变换为数据结构。这是一个从具体(即具体问题)到抽象(即数据结构)的过程。然后,通过增加对实现细节的考虑进一步得到存储结构和实现运算,从而完成设计任务。这是一个从抽象(即数据结构)到具体(即具体实现)的过程。
作为开发人员,我们必须对数据结构有充分的了解。在本文中,我将简要解释每个程序员必须知道的8种常用数据结构。
一、数据结构分类
数据结构表明一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成 。数据的逻辑结构是对数据之间关系的描述,如顺序关系,隶属关系等,有时就把逻辑结构简称为数据结构,数据的逻辑结构分为以下四种:
-
集合结构:集合结构的集合中任何两个数据元素之间都没有逻辑关系,组织形式松散。
-
线性结构:数据结构中线性结构指的是数据元素之间存在着“一对一”的线性关系的数据结构。
-
树状结构:树状结构是一个或多个节点的有限集合。
-
网络结构:网络结构是指通信系统的整体设计,它为网络硬件、软件、协议、存取控制和拓扑提供标准。
1、数组
2、栈
3、队列
4、链表
5、树
6、散列表
7、堆
8、图
二、数据结构详解
1、数组
数组(Array)是有序的元素序列。若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按有序的形式组织起来的一种形式。这些有序排列的同类数据元素的集合称为数组。
特点:
1.数组是相同数据类型的元素的集合,不允许出现混合类型。
2.数组中的各元素的存储是有先后顺序的,它们在内存中按照这个先后顺序连续存放在一起。
3.数组元素用整个数组的名字和它自己在数组中的顺序位置来表示。例如,a[0]表示名字为a的数组中的第一个元素,a[1]代表数组a的第二个元素,以此类推。
4.其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。
5.数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
6.可以使用索引直接访问数组中的任何元素。
数组操作的复杂性数组操作的时间和空间复杂性在下表中描述。
时间复杂性
空间复杂性
在数组中,最坏情况下的空间复杂度是O(n)。
2、栈
栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。
特点:
1.栈的最主要特点是后进先出。
2.栈是一种运算受限的线性表,限定仅在表尾进行插入和删除操作的线性表。
3.栈的所有插入和删除操作均在栈顶进行,而栈底不允许插入和删除。
4.向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素
5.从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
进栈与出栈:
1、进栈(PUSH)算法
①若TOP≥n时,则给出溢出信息,作出错处理(进栈前首先检查栈是否已满,满则溢出;不满则作②);
②置TOP=TOP+1(栈指针加1,指向进栈地址);
③S(TOP)=X,结束(X为新进栈的元素);
2、退栈(POP)算法
①若TOP≤0,则给出下溢信息,作出错处理(退栈前先检查是否已为空栈, 空则下溢;不空则作②);
②X=S(TOP),(退栈后的元素赋给X):
③TOP=TOP-1,结束(栈指针减1,指向栈顶)。
时间空间复杂度:
对于固定大小栈的出栈和入栈,时间复杂度为O(1)。但是对于支持动态扩容的顺序栈:出栈时间复杂度为O(1)。入栈:如果有空闲空间,入栈复杂度为O(1),空间不够时,时间复杂度为O(n)。
3、队列
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。
特点:
1.像栈一样,队列(queue)也是表。使用队列插入在一端进行删除则在另一端进行。比如说键盘进行字母或数字的输入,到显示器上如记事本上输出,其实这就是队列的应用。
2.队列是只允许在一端进行插入操作,而在另一端删除的线性表。
3.队列是一种先进先出的线性表,简称FIFO允许插入的一端是队尾,允许删除的一端是队头。
4.队列的基本操作是enqueue(人队),它是在表的末端(叫作队尾( rear))插入一个元素,和dequeue(出队),它是删除(并返回)在表的开头(叫作队头( front))的元素。
入队与出队:
队列的顺序存储结构——入队操作
Front和Rear一开始都指向队头的前面,此时队列为空,当进入元素是,Front不动,Rear跟随进入的元素进行++。即队尾进入元素。
队列的顺序存储结构——出队操作
从队头开始删除元素,每删除一个元素,后面的元素都向前移动,然后Front不动,Rear--。
4、链表
链表是一种顺序结构,由相互链接的线性顺序项目序列组成。因此,您必须顺序访问数据,并且无法进行随机访问。链接列表提供了动态集的简单灵活的表示形式。让我们考虑以下有关链表的术语。您可以通过参考下图来获得一个清晰的主意。
· 链表中的元素称为节点。
· 每个节点都包含一个密钥和一个指向其后继节点(称为next)的指针。
· 名为head的属性指向链接列表的第一个元素。
· 链表的最后一个元素称为尾。
以下是可用的各种类型的链表。
· 单链列表—只能沿正向遍历项目。
· 双链表-可以在前进和后退方向上遍历项目。节点由一个称为上一个的附加指针组成,指向上一个节点。
· 循环链接列表—链接列表,其中头的上一个指针指向尾部,尾号的下一个指针指向头。
链表操作
· 搜索:通过简单的线性搜索在给定的链表中找到键为k的第一个元素,并返回指向该元素的指针
· 插入:在链接列表中插入一个密钥。插入可以通过3种不同的方式完成;在列表的开头插入,在列表的末尾插入,然后在列表的中间插入。
· 删除:从给定的链表中删除元素x。您不能单步删除节点。删除可以通过3种不同方式完成;从列表的开头删除,从列表的末尾删除,然后从列表的中间删除。
链表的应用
· 用于编译器设计中的符号表管理。
· 用于在使用Alt Tab(使用循环链表实现)的程序之间进行切换。
5、树
树是一种层次结构,其中数据按层次进行组织并链接在一起。此结构与链接列表不同,而在链接列表中,项目以线性顺序链接。
在过去的几十年中,已经开发出各种类型的树木,以适合某些应用并满足某些限制。一些示例是二叉搜索树,B树,红黑树,展开树,AVL树和n元树。
二叉搜索树
顾名思义,二进制搜索树(BST)是一种二进制树,其中数据以分层结构进行组织。此数据结构按排序顺序存储值,我们将在本课程中详细研究这些值。
二叉搜索树中的每个节点都包含以下属性。
· key:存储在节点中的值。
· left:指向左孩子的指针。
· 右:指向正确孩子的指针。
· p:指向父节点的指针。
二叉搜索树具有独特的属性,可将其与其他树区分开。此属性称为binary-search-tree属性。
令x为二叉搜索树中的一个节点。
· 如果y是x左子树中的一个节点,则y.key≤x.key
· 如果y是x的右子树中的节点,则y.key≥x.key
树的应用
· 二叉树:用于实现表达式解析器和表达式求解器。
· 二进制搜索树:用于许多不断输入和输出数据的搜索应用程序中。
· 堆:由JVM(Java虚拟机)用来存储Java对象。
· Trap:用于无线网络。
6、散列表
散列表,也叫哈希表,是根据关键码和值 (key和value) 直接进行访问的数据结构,通过key和value来映射到集合中的一个位置,这样就可以很快找到集合中的对应元素。
记录的存储位置=f(key)
这里的对应关系 f 成为散列函数,又称为哈希 (hash函数),而散列表就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里,这种存储空间可以充分利用数组的查找优势来查找元素,所以查找的速度很快。
哈希表在应用中也是比较常见的,就如Java中有些集合类就是借鉴了哈希原理构造的,例如HashMap,HashTable等,利用hash表的优势,对于集合的查找元素时非常方便的,然而,因为哈希表是基于数组衍生的数据结构,在添加删除元素方面是比较慢的,所以很多时候需要用到一种数组链表来做,也就是拉链法。拉链法是数组结合链表的一种结构,较早前的hashMap底层的存储就是采用这种结构,直到jdk1.8之后才换成了数组加红黑树的结构,其示例图如下:
从图中可以看出,左边很明显是个数组,数组的每个成员包括一个指针,指向一个链表的头,当然这个链表可能为空,也可能元素很多。我们根据元素的一些特征把元素分配到不同的链表中去,也是根据这些特征,找到正确的链表,再从链表中找出这个元素。
哈希表的应用场景很多,当然也有很多问题要考虑,比如哈希冲突的问题,如果处理的不好会浪费大量的时间,导致应用崩溃。
7、堆
堆是二叉树的一种特殊情况,其中将父节点与其子节点的值进行比较,并对其进行相应排列。
让我们看看如何表示堆。堆可以使用树和数组表示。下图显示了我们如何使用二叉树和数组来表示二叉堆。
堆可以有2种类型。
· 最小堆-父项的密钥小于或等于子项的密钥。这称为min-heap属性。根将包含堆的最小值。
· 最大堆数-父项的密钥大于或等于子项的密钥。这称为max-heap属性。根将包含堆的最大值。
堆的应用
· 用于实现优先级队列,因为可以根据堆属性对优先级值进行排序。
· 可以在O(log n)时间内使用堆来实现队列功能。
· 用于查找给定数组中k个最小(或最大)的值。
· 用于堆排序算法。
8、图
一个图由一组有限的顶点或节点以及一组连接这些顶点的边组成。
图的顺序是图中的顶点数。图的大小是图中的边数。
如果两个节点通过同一边彼此连接,则称它们为相邻节点。
有向图
如果图形G的所有边缘都具有指示什么是起始顶点和什么是终止顶点的方向,则称该图形为有向图。
我们说(u,v)从顶点u入射或离开顶点u,然后入射到或进入顶点v。
自环:从顶点到自身的边。
无向图
如果图G的所有边缘均无方向,则称其为无向图。它可以在两个顶点之间以两种方式传播。
如果顶点未连接到图中的任何其他节点,则称该顶点为孤立的。
图的应用
1.用于表示社交媒体网络。每个用户都是一个顶点,并且在用户连接时会创建一条边。
2.用于表示搜索引擎的网页和链接。互联网上的网页通过超链接相互链接。每页是一个顶点,两页之间的超链接是一条边。用于Google中的页面排名。
3.用于表示GPS中的位置和路线。位置是顶点,连接位置的路线是边。用于计算两个位置之间的最短路径。
【公众号】:奇牛编程
【C语言】五小时快速入门C语言:【C语言】五小时快速入门C语言