1.链表
链表中的数据是呈线性排列的。在链表中数据的添加和删除都较为方便,访问会比较耗时。
链表中每一个节点都分别存储了数据和指针,指针指向的是下个数据的内存地址。
在链表中,数据都是分散存储在内存中的,无需存储在连续空间内。
因为链表是分散存储的,如果需要访问数据,只能从1个数据开始,顺着指针的指向一一往下啊访问。所以 访问的时间复杂度是O(n) 。
如果想要添加(删除)数据,只需要改变添加位置的前后指针就可以,所以 删除的时间复杂度为O(1) 。
以上我们的链表最后的一个数据的指针其实是没有任何指向的空指针,如果我们把这个指针指向第一个数据的内存地址,那就形成了一个单向循环链表,如果我们的每一个节点都记录上一个数据的内存地址和下一个数据的内存地址,那么就形成了双向链表。
2.数组
数组也是呈线性排列的一种数据结构。数组中访问特别简单,而添加数据和删除数据比较耗功夫。
数组的数据是按照顺序存储在内存的连续空间内。
由于数据是存储在连续的内存空间中的,所以每个数据的内存地址都是可以通过数组下标算出的,我们也可以借此直接访问目标数据(这叫做“随机访问”) ,访问的时间复杂度是O(1)。
如果我们需要删除(添加)目标数据,就要麻烦的多。
比如添加数据,首先在数据的末尾确保需要增加的存储空间。为了给新的数据腾出位置,要把已有的数据给一个个移开。最后在空出的位置写入数据。反之,如果想要删除数据,首先要删除目标数据,然后把已有数据一个个往空位移。删除(添加)的时间复杂度是 O(n)。
访问 | 添加 | 删除 | |
---|---|---|---|
链表 | O(n) | O(1) | O(1) |
数组 | O(1) | O(n) | O(n) |
3.栈
栈也是一种呈线性排列的数据结构,不过在这种结构中我们只能访问最新添加的数据。
栈的数据也是按照顺序存储在内存的连续空间内。,这里更正:栈的数据不一定按照顺序存储在内存的连续空间内,栈分为顺序栈和链式栈,在内存中分别是存放在连续空间内和分散的空间内。
栈是一种后进先出的结构,简称“LI-FO”(Last In Frist Out),在栈中,添加或删除数据只能栈顶一端进行,访问也只能访问到顶端的数据,如果想对中间的数据访问或者操作,必须通过出栈的操作才行。
适用于深度优先收索。
4. 队列
队列与栈相似,也是线性排列的数据结构,但是队列中添加或删除数据是可以从两端进行的。
建立顺序队列结构必须为其静态分配或动态申请一片连续的存储空间,并设置两个指针进行管理。一个是队头指针front,它指向队头元素;另一个是队尾指针rear,它指向下一个入队元素的存储位置。
队列是一种先进先出的结构,简称“FIFO”(Frist In Frist Out) 。只能从队列的尾部添加数据,只能从头部删除或访问数据。
每次在队尾插入一个元素是,rear增1;每次在队头删除一个元素时,front增1。随着插入和删除操作的进行,队列元素的个数不断变化,队列所占的存储空间也在为队列结构所分配的连续空间中移动。当front=rear时,队列中没有任何元素,称为空队列。当rear增加到指向分配的连续空间之外时,队列无法再插入新元素,但这时往往还有大量可用空间未被占用,这些空间是已经出队的队列元素曾经占用过得存储单元。
顺序队列中的溢出现象:
(1) "下溢"现象:当队列为空时,做出队运算产生的溢出现象。“下溢”是正常现象,常用作程序控制转移的条件。
(2)"真上溢"现象:当队列满时,做进栈运算产生空间溢出的现象。“真上溢”是一种出错状态,应设法避免。
(3)"假上溢"现象:由于入队和出队操作中,头尾指针只增加不减小,致使被删元素的空间永远无法重新利用。当队列中实际的元素个数远远小于向量空间的规模时,也可能由于尾指针已超越向量空间的上界而不能做入队操作。该现象称为"假上溢"现象。
循环队列:
对队列的使用方法稍加改进:无论插入或删除,一旦rear指针增1或front指针增1 时超出了所分配的队列空间,就让它指向这片连续空间的起始位置。自己真从MaxSize-1增1变到0,可用取余运算rear%MaxSize和front%MaxSize来实现。这实际上是把队列空间想象成一个环形空间,环形空间中的存储单元循环使用,用这种方法管理的队列也就称为循环队列。除了一些简单应用之外,真正实用的队列是循环队列。
以上摘取至百度百科 队列百科
适用于广度优先收索。
5. 哈希表
哈希表存储是由键值对构成,Hash表的物理存储其实是一个数组。
原理类似于数组+链表结构。
我们会通过Key值通过HashCode()计算出一个哈希值,哈希值就对应了一个地址,如果此地址存储了值,就使用冲突算法,将此此地址存储结构改为链表形式。如果没有值,就直接存入再此地址中。
Hash表的查询时间复杂度是O(1)。
Hash表的修改或删除时间复杂度也是O(1)。
6.堆
堆其实是一种图的树形结构,被用于实现“优先队列”。
优先队列是一种数据结构,可以自由添加数据,但是取数据时要从最小值开始按顺序取出。在堆的树形结构中,各个顶点被称为“节点”,数据就存储在这些节点中。
在堆中存储数据时必须遵守这样一条规定:子节点必定大于父节点。因此,最小值被存储在顶端的根节点中。往堆中添加数据时,为了遵守这条规则,一般都会把新数据放在最下面一行靠左的位置。当下面一行没有空间的时候,就另起一行。每次添加的时候都要满足子节点是大于父节点的,否则,做交换操作。
堆中的顶端数据始终都是最小的,所以无论数据量是多少,取出最小值的时间复杂度是O(1)。另外,取出数据后需要将最后的数据移动到顶端,然后一边比较一边向下移动,所以取出数据的时间要和树的高度成正比。重构树的时间复杂度为O(logn)。添加数据也是类似,时间复杂度为O(logn)。
7.二叉查找树
采用了图的树形结构。
二叉查找树有2个性质:1.每一个节点的值均大于其左子树上的任意一个节点的值。2每一个节点的值均小于其右子树上的任意一个节点的值。
根据这2个性质可以得出:最小值是从顶端开始,往其左下寻找到最末端的值,最大值是从顶端开始,往其右下寻找到最末端的值。
添加数据a:从顶端比较 ,顶端值大于a,a向左子节点移动,大于则向右子节点移动。然后继续与节点值比较和移动。直到移动的位置没有可以比较的节点,那么值就可以添加到此处。
删除数据b:删除某个节点时,常规是从子左树中查找到最大值充当节点值(左树的最右端末节点), 或者从右子树中查找到最小值充当节点值(右树的最左末端节点)。
最理想的时间复杂度是O(logn)(树的结构均衡),最差的时间复杂度是O(n)(树只向单侧延伸)。
文中有差异不对之处,以及不明白之处,请指出或加以更正。