数组是数据结构里线性表示的一种,它的特点:
- 数组在内存中是一块连续的空间,在创建数组时需要预先准备它的最大容量;
- 当数组长度达到最大容量时,再次插入元素前需要先为其扩容;
- 根据下标(索引)访问元素效率极高,时间复杂度为
O(1)
; - 在中间插入、删除元素有代价,需要移动元素,平均时间复杂度为
O(n)
。
操作
创建
对于数组来说,操作系统会给它在内存里分配一块连续的空间,数组下标为0处是它在该内存空间的起始地址。每个内存空间都有其唯一的起始地址。
由于程序在运行时,会不断地申请内存创建变量,而数组的特性是要有连续空间。因此,创建数组时,必须指定它的最大容量,也就是数组的长度必须是固定的。
以Java、C/C++等静态语言为例,创建数组的方式很简单,数组中每个元素类型必须是相同的。下面这个例子就是创建长度为5的数组,其中每个元素类型为int。
int[] nums = new int[5];
C/C++还可以通过为指针分配一块内存空间的形式,创建类似数组的结构,比如下面这个例子创建出来的nums变量在C/C++中可以也通过数组的形式去访问它。
int* nums = (int*) calloc(5, sizeof(int));
在部分动态类型的语言中,数组中的元素类型可以是不一样的。
访问
以上面创建的数组为例,访问的方式就是通过它的下标,比如:
nums[0] = 1;
int a = nums[1];
在C/C++中,不管是数组还是指针变量,都还可以通过*
操作符加在指针的形式去访问该位置的元素:
*nums = 1;
*(nums + 1) = 5;
int b = *(nums + 2);
应用 - ArrayList
声明:
template <class T> struct Node {
T data;
Node(T& data) {
this->data = data;
}
};
template <class T> class ArrayList {
private:
static const unsigned int initial_length = 16;
Node<T>** nodes;
unsigned int current_size;
unsigned int array_length;
void inflate();
public:
ArrayList();
ArrayList(int length);
ArrayList(ArrayList<T>& list);
~ArrayList();
void add(T& data);
void add(T& data, unsigned int index);
/**
* Remove node at pos
* @param index starts from 0
*/
void removeAt(int index);
void printList();
unsigned int size();
Node<T>* operator[](int index);
Node<T>* get(int index);
};
定义:
template <class T> ArrayList<T>::ArrayList() : ArrayList(initial_length) {}
template <class T> ArrayList<T>::ArrayList(int length) {
array_length = (unsigned int) length;
nodes = new Node<T>*[array_length];
current_size = 0;
}
template <class T> ArrayList<T>::ArrayList(ArrayList<T>& list) {
array_length = list.array_length;
nodes = new Node<T>*[array_length];
current_size = 0;
for (int i = 0; i < list.current_size; i++) {
T& data = list.get(i)->data;
Node<T>* node = new Node<T>(data);
nodes[i] = node;
}
current_size = list.current_size;
}
template <class T> ArrayList<T>::~ArrayList() {
delete []nodes;
}
template <class T> void ArrayList<T>::add(T& data) {
if (current_size == array_length) {
inflate();
}
Node<T> * node = new Node<T>(data);
nodes[current_size] = node;
++current_size;
}
template <class T> void ArrayList<T>::add(T& data, unsigned int index) {
if (index == current_size) {
this->add(data);
} else {
if (current_size == array_length) {
inflate();
}
for (unsigned int i = current_size; i > index; i--) {
nodes[i] = nodes[i-1];
}
Node<T> * node = new Node<T>(data);
nodes[index] = node;
++current_size;
}
}
template <class T> void ArrayList<T>::inflate() {
int new_size = current_size * 2;
Node<T>** newNodes = new Node<T>*[new_size];
for (int i = 0; i < current_size; ++i) {
newNodes[i] = nodes[i];
}
delete []nodes;
nodes = newNodes;
cout << "The array list length inflates to: " << new_size << endl;
}
template <class T> void ArrayList<T>::removeAt(int index) {
Node<T> * node = nodes[index];
for (unsigned int i = index; i < current_size; i++) {
nodes[i] = nodes[i+1];
}
delete node;
--current_size;
}
template <class T> void ArrayList<T>::printList() {
for (unsigned int i = 0; i < current_size; ++i) {
Node<T> * node = nodes[i];
cout << (*node).data << " ";
}
cout << endl;
}
template <class T> unsigned int ArrayList<T>::size() {
return current_size;
}
template <class T> Node<T>* ArrayList<T>::operator[](int index) {
return nodes[index];
}
template <class T> Node<T>* ArrayList<T>::get(int index) {
return nodes[index];
}