C++数据结构
线性顺序表(数组)
线性顺序表(链表)
Python风格双向链表的实现
散列表简单实现(hash表)
栈和队列的应用
二叉树之一(数组存储)
二叉树之二(二叉搜索树)
二叉树之三(二叉搜索树扩展)
图结构入门
前言
将一个线性表存储到计算机中,把线性表的结点按逻辑顺序依次存放到一组地址连续的存储单元里,用这种方法存储的线性表称为顺序表。C++中数组、vector、list就是典型的顺序表,其中list是以链表实现的,本文主要以仿写一个数组实现的顺序表,进而更深入了解顺序表。
提示:以下是本篇文章正文内容,下面案例可供参考
一、vector简介
vector是一种动态数组,它可以根据需要自动调整大小,存储连续的元素。vector的优点是可以快速地随机访问任意元素,而不需要遍历。缺点是在中间或开头插入或删除元素时,需要移动后面的元素,效率较低。
vector在计算机中的存储方式与普通数组类似,都是采用连续的内存空间来存放元素。但是,vector有一个特殊的成员变量capacity,表示vector分配的内存空间的大小。当vector的元素个数超过capacity时,vector会自动分配更大的内存空间,并将原来的元素复制到新的空间中,然后释放原来的空间。
上图很形象的表示了,数组在计算机中的存储方式。其中存储地址 b 表示了数组的起始内存地址,实际情况上它的值类似于 0x00000000000b,一般在存储 int 数据情况下每个 int整数占4个字节,那么第二个地址就是 b+4,所以 b+d 就是 0x00000000000f ,以此类推。实际上我们在使用数组时,变量所存储的即是内存块的起始 b 的地址,直接 cout 数组的变量名即可得到这个地址。用C++语言描述:这就是一个指针。
笔者只是为了更好的了解数组,没必要也不可能完整的实现 vector 的功能。以下仅实现插入、删除、size()、begin()、end()等方法的部分简单功能。
二、泛型编程自定义数组结构
这里采用了泛型编程的方法,使得代码更有通用性。简单来说就是这个 Arrary 类可以用于存储 int、char、或string、bool 等各种C++数据类型,和STL中的各种容器一样。
1、定义类
#include <iostream>
using namespace std;
template <typename T> class Array{
private:
static const int default_size = 64;
T *elems;
int max_size;
int len;
public:
Array(int size=default_size){
if(size){
max_size = size;
len = 0;
elems = new T[max_size];
for (int i=0; i<max_size; ++i)
elems[i] = (T)NULL;
}
}
~Array(){
delete[] elems;
}
这里只是为了学习,所以定义了最大元素数量为64个,vector是可以自动调整这个值的。私有域中定义了各个变量、和函数名。构造函数Array初始化各值、分配了最大的内存空间。并写了析构函数用以释放内存。
2、删除、读取元素和首尾指针
- delete_elem()方法用于按下标删除元素、判断是否超界后直接将后面的元素前移就行了。因为用了元素数量为界。
- get_elem() 方法用于按下标取元素值,实现逻辑与删除基本类似。
- begins()、 ends() 简单的返回了首尾元素的地址,其也是可以用于迭代循环的。可以参考以下迭代循环用法:
for (auto it=arr.begins();it!=arr.ends();it++) { cout << *it <<endl; }
bool delete_elem(int loca){
if (loca >= len || loca < 0){
return false;
}else{
for (int i=loca; i<len; ++i){
elems[i] = elems[i+1];
}
len--;
return true;
}
}
T get_elem(int loca){
if (loca >= len || loca < 0){
return (T)NULL;
}else{
return elems[loca];
}
}
T* begins(){
return elems;
}
T* ends(){
return elems + len;
}
3、修改元素、获取元素数量、和插入
代码都相当的简单,逻辑也与其它几个函数基本一致。就不再单独介绍了,有兴趣的可以自己解读一下。
bool change_elem(int loca, T newdata){
if (loca >= len || loca <0){
return false;
}else{
elems[loca] = newdata;
return true;
}
}
int get_len(){
return len;
}
bool insert_elem(T data){
int curr_index = len;
if (len >= max_size){
return false;
}else{
elems[curr_index] = data;
len++;
return true;
}
}
};
以上代码虽然没有什么实用性,但相信对于了解数组的数据结构还是有一定帮助的,代码均是经过测试的。使用cout << "begins is " << arr.begins() << endl;
输出结果是:begins is 0x7fece55053e0 与预期一致。不要深究以上代码中的变量及函数名称,主要为了避免与STL中的模板同名,不好区分才取这些奇怪名字的,事实上只要类名不同,方法名一样是可以的。
总结
数组实现顺序表是很简单的,因为各成员功能函数均很容易实现,只是给数组用类再包装一下,实现几个有用的方法而已。虽然它并没有多大的实用性,但有兴趣的看官也可以试试,还可以给它加上类似python的排序方法,C++ STL中algorithm排序的参数太多。