数组与链表
数组的本质是固定大小的连续的内存空间,并且这片连续的内存空间又被分割成等长的小空间。
特点是:连续存储,进一步,主要的特点是随机访问
为什么连续的内存空间又被分割成等长的小空间?
(1)数组的长度是固定的
(2)数组只能存储同一种数据类型的元素
注意:在Java中只有一维数组的内存空间是连续,多维数组的内存空间不一定连续。
那么数组又是如何实现随机访问的呢?
寻址公式:i_address = base_address + i * type_length
(基础地址+单个单元长度*下标)
为什么数组的索引是一般都是从0开始的呢?
假设索引不是从0开始的,而是从1开始的,那么我们有两种处理方式:
寻址公式变为: i_address = base_address + (i – 1) * type_length
浪费开头的一个内存空间,寻址公式不变。
在计算机发展的初期,不管是CPU资源,还是内存资源都极为宝贵,所以在设计编程语言的时候,索引就从0开始了,而我们也一直延续了下来。
为什么数组的效率比链表高?
本质:数组是连续存储, 链表是非连续存储。
数组的基本操作
添加 (保证元素的顺序)
最好情况:O(1)
最坏情况:移动n个元素,O(n)
平均情况:移动 n/2 个元素,O(n)
删除 (保证元素的顺序)
最好情况:O(1)
最坏情况:移动n-1个元素,O(n)
平均情况:移动(n-1)/2个元素,O(n)
查找
a. 根据索引查找元素:O(1)
b. 查找数组中与特定值相等的元素
①大小无序:O(n)
②大小有序:O(log2n)
总结: 数组增删慢,查找快。
循环链表我们用的一般比较少,但是当处理的数据具有环形结构时,就特别适合用循环链表
比如约瑟夫问题。
接下来我们讨论下单链表和双向链表。
单链表:
增加(在某个结点后面添加) O(1)
添加头结点:
Node newNode=new Node(“ ”,top);
top=newNode;
添加尾结点:
Node newNode=new Node(“ ”,null);
mid.next=newNode;
中间添加结点:
//创建了一个新结点,指向前一个结点的next
Node newNode=new Node(“ ",midF.next)
//然后将前一个节点的next指向自己
midF.next=newNode;
删除(在某个结点后面删除) O(1)
删除头结点:
top=top.next//头指针指向下一个结点就行
删除尾结点:
midF.next=null //尾部前一个结点指向空就行
删除中间结点:
midF.next=midF.next.next//前一个结点的指针指向下下个结点
查找(可能是2/n)
a. 根据索引查找元素 O(n)
b. 查找链表中与特定值相等的元素
①元素大小有序 O(n)
②元素大小无序 O(n)
总结:链表增删快,查找慢。
双向链表:(head end)
前面那些操作,双向链表和单链表的时间复杂度是一样的。那为什么在工程上,我们用的一般是双向链表而不是单链表呢 (比如JDK中的 LinkedList & LinkedHashMap)?
那自然是双向链表有单链表没有的独特魅力——它有一条指向前驱结点的链接。
增加 (在某个结点前面添加元素)
添加头结点:
Node newNode=new Node(null," ",head)//创建一个新结点
head.pre=newNode//先将原头结点前指针指向新结点
head=newNode//把头结点设置为新结点。
添加尾结点:
Node newNode=new Node(end," ",null)
end.next=newNode//原end结点的后指针指向新结点
end=newNode//把尾结点设置为新结点。
添加中间结点:
//创建一个新结点,前指向mid前一个结点,后指针指向mid结点
Node newNode=new Node(mid.pre," ",mid)
//将结点的前一个结点的后指针指向新结点,将原结点的前指针指向新结点
mid.pre.next=newNode;
mid.pre=newNode;
删除 (删除该结点)
删除中间结点:
mid.pre.next=mid.next//前结点的后指针指向后结点
mid.next.pre=mid.pre//后结点的前指针指向前结点
删除头结点:
head.next.pre=null;//将下一个结点的前指针指向null
head=head.next;//设置头结点是下一个节点
删除尾结点:
end.pre.next=null;//将尾结点前一个结点的后指针指向null
end=end.pre;//将尾结点设置是前一个结点。
查找(实际时间复杂度可能是4/n)
a. 查找前驱结点 O(n)
b. 根据索引查找元素
c. 查找链表中与特定值相等的元素
① 元素大小无序
② 元素大小有序
总结:虽然双向链表更占用内存空间,但是它在某些操作上的性能是优于单链表的。
思想:用空间换取时间。