随机访问
链表适合插入,删除,时间复杂度是O(1),数组支持随机访问,根据下标随机访问的时间复杂度是O(1)。
插入,删除
插入操作
假设数组的长度为 n,现在,如果我们需要将一个数据插入到数组中的第K个位置。为了把第K个位置腾出来,我们需要把 k后的 n-k 位置顺序向后移动一位。
最好时间复杂度:在最后一位插入,时间复杂度为:O(1)。
最坏时间复杂度:在第一位插入,时间复杂度是:O(n)。
平均时间复杂度:我们有可能在n个位置插入,每一种情况的可能都是1/n,所以平均情况时间复杂度为 (1+2+…n)/n=O(n)。
如果数组中的数据是有序的,我们在某个位置插入一个新的元素时,就必须按照刚才的方法搬移 k 之后的数据。但是,如果数组中存储的数据并没有任何规律,数组只是被当作一个存储数据的集合。那么这种情况之下,我们可以把插入的数据放到数组的最后一位,然后与第K个位置进行交换,这样就把时间复杂度降到O(1)了。
删除操作
与插入一样,我们删除数据之后,也要搬移数据。
最好时间复杂度:在末位进行删除,O(1)。
最坏时间复杂度:搬移n-1个位置,O(n)。
平均时间复杂度:与插入类似,时间复杂度为O(n)。
优化:在某些情景下,我们可以将多次删除操作在一起执行,删除的效率会提高。我们先记录下已经删除的数据,当数组没有空间储存的时候,再触发一次真正的数据搬移。
容器与数组
容器最大的优势就是可以将很多数组操作的细节封装起来。比如,数组插入,删除数据时候,需要搬移其他数据。另外,它还支持动态扩容。
1.如果特别关注性能,首选数组。
2.如果数据的大小已经事先知道,优先使用数组。
3.二维矩阵,使用数组更加直观。
所以,对于平常的开发,直接使用容器就足够了,省时省力。毕竟损耗一丢丢性能,完全不会影响系统整体的性能。但如果是一些底层的开发,什么网络框架,性能优化需要做到极致,就要使用数组。
为什么大多数编程语言,数组要从0开始编号?
因为数组在计算某个位置的时候(假设从0开始),是通过下面这个公式来计算的:
a[k]_address = base_address + k * type_size
如果这个时候,是从1开始的话,计算某个数据的位置就是:
a[k]_address = base_address + (k-1)*type_size
对比这两个公式,从1开始编号,每次随机访问数组元素都要进行多了一次减法运算,对与CPU来说,就是多了一次减法指令。
如何实现一个动态扩容的数组?
我们事先给数组指定一个大小size,并且通过一个used表示已经使用了的数量,当used大于等于size的时候,我们就把size扩大两倍。这里我们需要一个size*2的数组,然后把之前的数组复制到这个扩大之后的数组。它的均摊时间复杂度是O(1)。
这里是c++代码实现:
#include <iostream>
using namespace std;
class array
{
private:
int* arr;
int capacity;
int used;
public:
array(int size){
if(size == 0){
size = 1;
}
arr = new int[size];
capacity = size;
used = 0;
}
void print(){
for(int i=0;i<used;++i){
cout << arr[i] << " " << endl;
}
}
// 末位添加数据
void append(int data){
if(capacity <= used){
int* temp = new int[capacity*2];
for(int i=0;i<used;++i){
temp[i] = arr[i];
}
arr = temp;
}
arr[used++] = data;
}
};
测试代码:
#include <array.h>
int main(){
array a(0);
a.append(10);
a.append(9);
a.print();
}
如何实现合并两个有序的数组,成为一个有序的数组?
思路是这样的:假设第一个数组L1有m个数据,第二个数组L2有n个数据,我们创建一个新的,大小为m+n的数组,用两个游标 i和 j 遍历L1和L2,两个数组的数据每一次都相互比较一次,把小的放到新的数组,然后对应的游标向右移动,直到某个游标到达界限,这时候我们再判断哪个数组还没有遍历完,然后将剩下的数据复制到新的数组之中就可以了。它的时间和空间复杂度都是O(n)。
这里是代码实现:
int main(){
int L1[5] = {1,3,5,7,9};
int L2[6] = {2,4,6,8,10,12};
int L3[11] = {0};
int i=0,j=0,k=0;
while(i < 5 && j < 6){
if(L1[i] < L2[j]){
L3[k++] = L1[i++];
}else{
L3[k++] = L2[j++];
}
}
if(i<5){
for(;i<5;++i){
L3[k++] = L1[i];
}
}else{
for(;j<6;++j){
L3[k++] = L2[j];
}
}
for(int i=0;i<11;++i){
cout << L3[i] << " " << endl;
}
return 0;
}