当前位置 博文首页 > 让代码改变世界:数据结构之顺序表

    让代码改变世界:数据结构之顺序表

    作者:[db:作者] 时间:2021-07-10 22:18

    顺序表是最简单的一种数据结构,数组就是一个简单的顺序表,大家可以利用对数组的认识来理解顺序表。顺序表的最大特点就是存储在一片连续的地址空间。这一特点决定了顺序表的优点和不足:

    优点:由于地址连续,所以可利用一个首地址作为整个顺序表的标识。同时,地址连续的顺序表可以实现随机存储,也就是说可以利用下标访问元素,这一点最大的好处是可以节省访问时间,提高程序运行效率。

    不足:为保证地址空间的连续性,顺序表每删除一个元素都要移动后面所有元素,这会造成很多不必要的操作。

    由于顺序表实现简单,所以平时用的还是比较多的。以后各种复杂数据结构都是基于顺序表改进得到的。

    顺序表源代码:

    </pre><p align="left"><pre name="code" class="cpp">#ifndef _SEQLIST_H  
    #define _SEQLIST_H  
      
    #include <iostream>
    
    const int defaultSize=10;  
    
    template<typename dataType>
    class seqList;//声明模板类
    
    template<typename dataType>
    std::ostream& operator<<(std::ostream&,const seqList<dataType>&);//声明模板函数
    
    template<typename dataType>  
    class seqList  
    {  
    public:  
        seqList(const int size=defaultSize):maxSize(size),length(0)//(默认)构造函数  
        {  
            elements=new dataType[maxSize];  
        }  
        seqList(const seqList& other)//复制构造函数  
        {  
            this->maxSize=other.maxSize;  
            this->length=other.length;  
            this->elements=new dataType[maxSize];  
            for(int i=0;i<this->length;++i)  
                this->elements[i]=other.elements[i];  
        }  
        seqList& operator=(const seqList& other)//赋值操作符  
        {  
            if(this->elements==other.elements)  
                return *this;//自我赋值,返回自身  
            delete[] this->elements;  
            this->maxSize=other.maxSize;  
            this->length=other.length;  
            this->elements=new dataType[this->maxSize];  
            for(int i=0;i<this->length;++i)  
                this->elements[i]=other.elements[i];  
        }  
        ~seqList(){delete[] elements;}//析构函数  
      
        bool insertElement(const dataType&);//向表尾插入元素  
        bool deleteElement(const int&);//删除指定位置元素  
        dataType getElement(const int&) const;//返回指定位置的元素 
        bool changeElemet(const int&,const dataType&);//修改指定位置元素  
    	template<typename dataType>
    	friend std::ostream& //重载输出操作符
    		operator<< <dataType>(std::ostream&,const seqList<dataType>&);
    
    private:  
        dataType* elements;//存放表元素  
        int maxSize;//表的最大容量  
        int length;//表的有效长度  
      
    };  
      
    template<typename dataType>  //向表尾插入元素  
    bool seqList<dataType>::insertElement(const dataType& elmt)
    {  
        if(length>maxSize)  
        {  
            cerr<<"The seqList is full!"<<endl;  
            return false;  
        }  
        this->elements[length++]=elmt;  
        return true;  
    }  
    template<typename dataType>  //删除指定位置元素  
    bool seqList<dataType>::deleteElement(const int& index)
    {  
        if(index>=maxSize||index>=length||index<0)  
        {  
            cerr<<"The subscrip is illegal!"<<endl;  
            return false;  
        }  
        for(int i=index;i<length;++i)  
        {  
            this->elements[i]=this->elements[i+1];  
        }  
        --length;  
        return true;  
    }  
    template<typename dataType>  //返回指定位置的元素  
    dataType seqList<dataType>::getElement(const int& index) const
    {  
        if(index>=maxSize||index>=length||index<0)  
        {  
            cerr<<"The subscrip is illegal!"<<endl;  
        }  
        return this->elements[index];  
    }  
    template<typename dataType>  //修改指定位置元素  
    bool seqList<dataType>::changeElemet(const int& index,const dataType& elmt)
    {  
        if(index>=maxSize||index>=length||index<0)  
        {  
            cerr<<"The subscrip is illegal!"<<endl;  
            return false;  
        }  
        this->elements[index]=elmt;  
    }  
    template<typename dataType> //重载输出操作符
    std::ostream& operator<<(std::ostream& os,const seqList<dataType>& sqLst)
    {
    	for(int i=0;i<sqLst.length;++i)
    		os<<sqLst.elements[i]<<" ";
    	os<<std::endl;
    	return os;
    }
    
    #endif
    


    实现过程中要注意的两点:

    一是重载赋值操作符(=)时为防止自身赋值出问题所做的判断,针对不同的类的,这种判断不能一概而论,有的可以直接利用某个元素的相等来判断(如本例)。而有的类则需要判断类中很多个元素,甚至要考虑为此重载相等操作符。这里最常犯的错误是没有判断或者直接利用”==”判断了却没有重载”==”。前者会造成自我赋值时的错误,而后者主要是在手写代码时要注意。

    二是对输出操作符(<<)的重载。这里问题比较多,要多说几句。

    首先是输出操作符一般重载为友元函数,且其形式是较为固定的,这一点不明白的可以参考相关资料。

    其次是关于模板类的友元关系声明,C++支持三种模板类的声明:

    1.?? 普通函数的友元声明

    即将友元关系授予一个特定的非模板函数,这个情况最简单,可以直接声明

    template<typenameT>
    class A
    {
           friend void fun(int&);//fun函数是非模板函数
    };




    2.?? 一般模板函数的友元声明

    这种情况下,函数本身也是一个模板函数,函数所用的模板和类的模板“没有关系”,即两个模板是不同的

    template<typenameT1>
    class B
    {
           template<typename T2>
           friend void fun(T2&);//fun是T2模板函数这和类的模板T1不同
    };


    这种情况下要注意友元声明前的template<typename T2>是必须要有的


    3.?? 特定模板的友元声明

    这种情况最为复杂,其要求将友元关系授予某些特定的模板函数,其中最常用的是和类模板相同的函数

    template<typename T>
    void fun(T&);//必须再前面声明
    template<typename T>
    class C
    {
           friendvoid fun<T>(T&);//fun函数所用模板和类相同
    };

    这里要注意fun后面的<T>是要写的(不写不会再编译时出错,但会在使用该函数时出问题),而且函数必须再类前做出声明

    更为复杂的情况是模板函数有以类的模板为参数

    template<typename T>
    class C;//先声明类
    template<typename T>
    voidfun(T&,C<T>&);//必须再前面声明
    template<typename T>
    class C
    {
           friendvoid fun<T>(T&,C<T>);//fun函数所用模板和类相同
    };


    这里要想声明类,然后用类声明又有函数,再在类里面声明友元关系。本例中重载输出操作符及是这种情况。
    最后要说明的是这些情况仅限于友元声明是的不同,友元函数实现是和正常情况下一样的。

    cs
    下一篇:没有了