数据结构六: 数组

1. 数组的基本概念

数组由一组类型相同的数据组成的。可借助线性表的概念递归定义如下:
数组是一个可直接按序号寻找元素的线性表

a = ( a 0 , a 1 , , a m 1 )

a i ( i = 1 , 2 , , m 1 ) 是简单元素,则a是一维数组,当一维数组的每个元素本身又是一个一维数组时,则一维数组扩充成二维数组。以此类推,若 a i k 1 维数组时,则 a k 维数组。

2. 数组的顺序存储方式

由于数组很少有删除和插入操作,所以数组通常采用顺序存储方式。设n维数组有 m = i = 1 n b i 个元素,其中 b i 表示第i维的长度。假设数组存储的首地址为base,数组中的每个元素需要L个存储单元,则整个数组需要 m L 个存储空间。为了存取数组中每个特定下标的元素,必须确定下标为 i 1 , i 2 , , i n 的元素的存储位置。实际上就是把下标 i 1 , i 2 , , i n 映射到 [ 0 , m 1 ] 中的某个数 m a p ( i 1 , i 2 , , i n ) ,使得该下标所对应的元素值存储在一下位置:

L o c ( i 1 , i 2 , , i n ) = b a s e + m a p ( i 1 , i 2 , , i n ) L

L o c ( i 1 , i 2 , , i n ) 表示下标为 i 1 , i 2 , , i n 的数组元素的的存储地址。可以看到,如果已经知道数组的首地址,要确定其他元素的地址,只需要求出 m a p ( i 1 , i 2 , , i n ) 。接下来讲讲如何确定 m a p ( i 1 , i 2 , , i n )
对于一维数组: m a p ( i 1 ) = i 1
对于二维数组:各元素可按下图的形式进行排列。第一维下标相同的元素位于同一行,第二维下标相同的元素位于同一列。
这里写图片描述
对于上图中的元素从左至右连续编号,即可得到下图(a)的结果,这种按照行把二维数组中的元素位置映射为 [ 0 , m 1 ] 的方式叫做 行优先映射。下图(b)给出了按列进行连续编号的 列优先映射方式。(行列优先说的是在存储的时候是先存二维数组的列还是行)
这里写图片描述
对于一个 b 1 b 2 列的二维数组的行优先映射对应的映射函数为:
m a p ( i 1 , i 2 ) = i 1 b 2 + i 2

三维的行优先映射函数为:
m a p ( i 1 , i 2 i 3 ) = i 1 b 2 b 3 + i 2 b 3 + i 3

以此类推, n 维数组的行优先映射函数为:
(76) m a p ( i 1 , i 2 i 3 , , i n ) = i 1 b 2 b 3 b n + i 2 b 3 b n + + i n (77) = j = 1 n 1 i j j = 1 n b k + i n (78) = j = 1 n c j i j

其中, c n = 1 , c j 1 = b j c j , 1 < j n
在某些语言中,数组各维的下标范围不一定是 [ 0 , b j 1 ] 而是某个闭合区间 [ l j , h j ] 中的整数,则相应的行优先映射函数也要发生变化。设n维数组的第k维的小标范围为 [ l k , h k ] ,记:
d k = h k l k + 1 , k = 1 , 2 , 3 , , n

d k 为第k维的长度,则此时有:
(79) m a p ( i 1 , i 2 i 3 , , i n ) = ( i 1 l 1 ) d 2 d 3 b n + ( i 2 l 2 ) d 3 d n + + ( i n l n ) (80) = j = 1 n 1 ( i j l j ) j = 1 n d k + ( i n l n )

列优先的映射函数为:
这里写图片描述

//.hpp文件
#pragma once

#include <iostream>
#include <stdarg.h>
//数组类
template<typename elemType>
class Array
{
protected:
    //数组成员数据
    elemType* base;//数组元素的基地址
    int dim;//数组维度
    int *bounds;//数组各维度的长度
    int* constants;//数组映像函数常量

    //辅助函数
    int Locate(int sub0, va_list &va) const;//求元素在顺序存储中的位置

public:
    //抽象数据类型方法声明以及重载函数
    Array(int d, ...);//由维数和其后各维长度构造数组
    ~Array();//析构函数
    elemType &operator()(int sub0, ...);//重载函数运算符
    Array(const Array<elemType> &copy);//复制构造函数
    Array<elemType>& operator = (const Array<elemType>& copy);//赋值运算符重载
};

//数组类的实现
template<typename elemType>
int Array<elemType>::Locate(int sub0, va_list &va) const
{
    if (!(sub0 >=0 && sub0 < bounds[0]))
    {
        std::cout << "下标出界!" << std::endl;
        return -1;
    }
    else
    {
        int off = constants[0] * sub0;//初始化元素在顺序存储中的位置
        for (int i = 1; i < dim; i++)
        {
            int sub = va_arg(va, int);//取出数组元素下标
            if (!(sub >= 0 && sub < bounds[i]))
            {
                std::cout << "下标越界!" << std::endl;
                return -1;
            }
            off = off + constants[i]*sub;//累加乘积求元素在顺序存储中的位置
        }
        return off;
    }
}

template<typename elemType>
Array<elemType>::Array(int d, ...)
{
    if (d<1)
    {
        std::cout << "维度不能小于1!" << std::endl;
        return;
    }
    dim = d;//数组的维数为1
    bounds = new int[dim];
    va_list va;//变长参数变量
    int elemTotal = 1;//元素总数

    //初始化变量va,用于存储变长参数信息,d为省略号左侧最右边的参数标识符
    va_start(va, d);//第二个参数必须和传入的第一个参数名一致
    for (int i = 0; i < dim; i++)
    {
        bounds[i] = va_arg(va, int);
        elemTotal *= bounds[i];//统计数组总元素个数
    }
    va_end(va);
    base = new elemType[elemTotal];
    constants = new int[dim];//分配数组映射函数常量
    constants[dim - 1] = 1;
    for (int i = dim - 2; i >= 0;i--)
    {
        constants[i] = bounds[i + 1] * constants[i + 1];
    }
}

template<typename elemType>
Array<elemType>::~Array()
{
    if (base != NULL)
    {
        delete[] base;
    }
    if (bounds != NULL)
    {
        delete[] bounds;
    }
    if (constants != NULL)
    {
        delete[] constants;
    }
}


template<typename elemType>
elemType & Array<elemType>::operator()(int sub0, ...)
{
    va_list va;
    va_start(va, sub0);
    int position = Locate(sub0, va);
    va_end(va);

    return *(base + position);
}

template<typename elemType>
Array<elemType>::Array(const Array<elemType> &copy)
{
    dim = copy.dim;

    int elemTotal = 1;
    bounds = new int[dim];
    constants = new int[dim];
    for (int i = 0; i < dim;i++)
    {
        bounds[i] = copy.bounds[i];
        constants[i] = copy.bounds[i];
        elemTotal *= copy.bounds[i];
    }

    base = new elemType[elemTotal];
    for (int i = 0; i < elemTotal; i++)
    {
        base[i] = copy.base[i];
    }
}


template<typename elemType>
Array<elemType>& Array<elemType>::operator = (const Array<elemType>& copy)
{
    if (&copy != this)
    {
        if (base != NULL) delete[] base;
        if (bounds != NULL) delete[] bounds;
        if (constants != NULL) delete[] constants;

        dim = copy.dim;

        int elemTotal = 1;
        bounds = new int[dim];
        constants = new int[dim];
        for (int i = 0; i < dim; i++)
        {
            bounds[i] = copy.bounds[i];
            constants[i] = copy.bounds[i];
            elemTotal *= copy.bounds[i];
        }

        base = new elemType[elemTotal];
        for (int i = 0; i < elemTotal; i++)
        {
            base[i] = copy.base[i];
        }
    }
    return *this;
}
//测试文件
#include "Array.hpp"

int main(int argc, char* argv[])
{
    Array<int> myArry(2,3,3);
    int count = 1;
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3;j++)
        {
            myArry(i, j) = count;
            count++;
        }
    }

    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            std::cout << myArry(i, j) << std::endl;
        }
    }

    Array<int> myArry2(myArry);
    Array<int> myArry3(3,2,2,2);

    myArry3 = myArry;
    getchar();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wuye999/article/details/79562447