一、数组
数组是一个存储元素的线性集合,它使用一块连续的内存空间保存数据,保存的数据的个数在分配内存的时候就是确定的。
访问数组中第 n 个元素的时间花费是O(1) ,在数组中查找一个指定的元素则是O(N)。
向数组中插入或删除元素时,最好的情况是在数组的末尾进行操作,时间复杂度是O(1) ,最坏情况是插入或者删除第一个元素,时间复杂度是O(N) 。
在数组的任意位置插入或删除元素时,后面的元素全部需要移动,移动的元素和元素个数有关,总体的时间复杂度仍然是O(N) 。
二、链表
链表是由一组节点组成的集合。每个节点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。每个节点都使用一个对象的引用指向它的后继,最后一个节点的指针指向NULL。
链表不需要提前分配固定大小存储空间,当需要存储数据的时候分配一块内存并将这块内存插入链表中。
链表分为单链表、双链表、循环链表。
在链表中查找第 n 个数据以及查找指定的数据的时间复杂度是 O(N) ,插入和删除数据的时间复杂度是 O(1) 。
数组和链表的区别:
|
数组
|
链表
|
动态存储分配
|
数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费。
|
链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项)。
|
逻辑结构角度
|
数组是将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素。但是如果要在数组中增加一个元素,需要移动大量元素,在内存中空出一个元素的空间,然后将要增加的元素放在其中。同理,如果想删除一个元素,同样需要移动大量元素去填掉被移动的元素。如果应用需要快速访问数据,很少或不插入和删除元素,就应该用数组。
|
链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起。比如:上一个元素有个指针指到下一个元素,以此类推,直到最后一个元素。如果要访问链表中一个元素,需要从第一个元素开始,一直找到需要的元素位置。但是增加和删除一个元素对于链表数据结构就非常简单了,只要修改元素中的指针就可以了。如果应用需要经常插入和删除元素你就需要用链表数据结构了。
|
内存存储角度
|
(静态)数组从栈中分配空间, 对于程序员方便快速,但自由度小。
|
链表从堆中分配空间, 自由度大但申请管理比较麻烦。
|
总结
|
1)数组静态分配内存,链表动态分配内存;
2)数组在内存中连续,链表不连续;
3)数组元素在栈区,链表元素在堆区;
4)数组利用下标定位,时间复杂度为O(1),链表定位元素时间复杂度O(n);
5)数组插入或删除元素的时间复杂度O(n),链表的时间复杂度O(1)。
|
三、队列
队列是一种先进先出(First-In-First-Out,FIFO)的数据结构,用于存储按顺序排列的数据。
队列只能在队尾插入元素,在队首删除元素。
队列可以使用数组和链表来实现。
队列的两种操作主要是:在队尾插入新元素和删除队首的元素。插入操作也叫做入队,删除操作也叫做出队。
四、栈
栈是一种后进先出(Last-In-First-Out,LIFO)的数据结构,栈内的元素只能通过列表的一端访问,这一端称为栈顶。
由于栈具有后入先出的特点,所以任何不在栈顶的元素都无法访问。为了得到栈底的元素,必须先拿掉上面的元素。
栈也可以使用数组和链表来实现。
栈的两种操作主要是:将一个元素压入栈和将一个元素弹出栈。