DTLib - List、SeqList、StaticList、DynamicList

目录

1、线性表的本质和操作 

2、编程实验 

3、线性表的顺序存储结构和抽象实现

4、编程实验 

5、顺序存储线性表的效率分析 

6、StaticList 和 DynamicList 

7、小结 


1、线性表的本质和操作 

线性表( List )的表现形式

               -零个或多个数据元素组成的集合

               -数据元素在位置上是有序排列的 

               -数据元素的个数是有限的,数据元素的类型必须相同 

线性表( List )的抽象定义

                 线性表是具有相同类型的n(>=0)个数据元素的有限序列   ( a₀,a₁,...,an-1 ) 

                      ai 是表项(数据元素),n 是表长度 

线性表(List)的性质

               -a₀为线性表的第一个元素,只有一个后继 

               -an-1为线性表的最后一个元素,只有一个前驱 

               -除a₀和an-1外的其它元素 ai 既有前驱,又有后继 

               -直接支持逐项访问和顺序存取 

线性表的—些常用操作

               -将元素插入线性表

               -将元素从线性表中删除 

               -获取目标位置处元素的值 

               -设置目标位置处元素的值 

               -查找指定元素的位置

               -获取线性表的长度 

               -清空线性表 

2、编程实验 

线性表抽象类的创建     List.h

#ifndef LIST_H  
#define LIST_H  
  
#include "Object.h"  
  
namespace DTLib  
{  
  
template <typename T>  
class List : public Object  
{  
    List(const List& e);
    List& operator= (const List& e);
    // 对于容器类型的类,可以考虑禁用拷贝构造和赋值操作。
    // 如:存放的数据为堆空间地址时,赋值操作后,最后可能两次delete同一片空间
    // 如:s1指向堆空间一片空间,被拷贝构造后s2也指向这片空间
public:  
    List() {}
    virtual bool insert(const T& e) = 0;
    virtual bool insert(int i, const T& e) = 0;  
    virtual bool remove(int i) = 0;  
    virtual bool set(int i, const T& e) = 0;  
    virtual bool get(int i, T& e) const = 0; 
    virtual int find(const T& e) const = 0; 
    virtual int length() const = 0;  
    virtual void clear() = 0;  
};  
}  
  
#endif // LIST_H  

main.cpp

#include <iostream>  
#include "List.h"  
  
using namespace std;  
using namespace DTLib;  
  

int main()  
{  
    List<int>* l = NULL;  
  
    return 0;  
}

 

3、线性表的顺序存储结构和抽象实现

顺序存储的定义 

    - 线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表中的数据元素。

  

                           这片空间可能是栈空间也可能是堆空间

设计思路 

    - 可以用一维数组来实现顺序存储结构SeqList

    - SeqList设计要点 :抽象类模板,存储空间的位置和大小由子类完成 

 

4、编程实验 

顺序存储线性表     SeqList.h

#ifndef SEQLIST_H
#define SEQLIST_H

#include "List.h"
#include "Exception.h"

namespace DTLib
{

template <typename T>
class SeqList : public List<T>
{
protected:
    T* m_array;   // 顺序存储空间的位置和大小由子类决定
    int m_length; // 线性表的长度

public:
    /* 这里的i都是下标,[m_length-1]为最后一个元素 */
    bool insert(const T& e)
    {
        return insert(m_length, e);
    }

    bool insert(int i, const T& e)
    {
        bool ret = (0 <= i) && (i <= m_length);

        if( ret )
        {
            for(int p = m_length-1; p >= i; p--)
            {
                m_array[p+1] = m_array[p];
            }

            m_array[i] = e;
            m_length++;
        }

        return ret;
    }

    bool remove(int i)
    {
        bool ret = (0 <= i) && (i < m_length);

        if( ret )
        {
            for(int p = i; p < m_length; p++)
            {
                m_array[p] = m_array[p+1];
            }

            m_length--;
        }

        return ret;
    }

    bool set(int i, const T& e)
    {
        bool ret = (0 <= i) && (i < m_length);

        if( ret )
        {
            m_array[i] = e;
        }

        return ret;
    }

    bool get(int i, T& e) const
    {
        bool ret = (0 <= i) && (i < m_length);

        if( ret )
        {
            e = m_array[i];
        }

        return ret;
    }

    int find(const T& e) const
    {
        int ret = -1;

        for(int p = 0; p < m_length; p++)
        {
            if(m_array[p] == e)
            {
                ret = p;
                break;
            }
        }

        return ret;
    }

    int length() const
    {
        return m_length;
    }

    void clear()
    {
        m_length = 0;
    }

    T& operator[] (int i)
    {
        if( (0 <= i) && (i < m_length) )
        {
            return m_array[i];
        }
        else
        {
            THROW_EXCEPTION(IndexOutOfBoundsExpception, "Parameter i is invalid...");
        }
    }
    T operator[] (int i) const
    {
        return const_cast<T&>(*this)[i];
    }

    // 顺序存储空间的大小由子类完成
    virtual int capacity() const = 0;

};
}

#endif // SEQLIST_H

main.cpp

#include <iostream>
#include "SeqList.h"

using namespace std;
using namespace DTLib;

int main()
{
    SeqList<int>* l = NULL;

    return 0;
}

 

5、顺序存储线性表的效率分析 

template <typename T>
class SeqList : public List<T>
{ 
public:
    bool insert(const T& e)
    bool insert(int i, const T& e);// O(n)
    bool remove(int i);            // O(n)
    bool set(int i, const T& e);   // O(1)
    bool get(int i, T& e) const;   // O(1)
    int find(const T& e) const     // O(n)
    int length() const;            // O(1)
    void clear();                  // O(1)
    
    T& operator[] (int i);         // O(1)
    T operator[] (int i) const;    // O(1)
 
    virtual int capacity() const = 0; 
};

 

                  顺序存储线性表的插入和删除操作存在重大效率隐患 

问: 长度相同的两个SeqList ,  插入删除 操作的平均耗时是否相同?

答:取决于线性表存储数据元素类型,如插入字符串和整形差距很大

 

6、StaticList 和 DynamicList 

1、StaticList

目标:完成SeqList的子类StaticListDynamicList类的具体实现

StaticList设计要点 :使用原生数组作为顺序存储空间 使用模板参数决定数组大小 

2、编程实验  

StaticList的实现     StaticList. h

#ifndef STATICLIST_H
#define STATICLIST_H

#include "SeqList.h"

namespace DTLib
{

template <typename T, int N>
class StaticList : public SeqList<T>
{
    T m_space[N];
public:
    StaticList()
    {
        this->SeqList<T>::m_array = m_space; // this->m_array = m_space;
        this->m_length = 0;
    }

    int capacity() const
    {
        return N;
    }
};

}

#endif // STATICLIST_H

main.cpp

#include <iostream>
#include "StaticList.h"

using namespace std;
using namespace DTLib;

int main()
{
    StaticList<int, 5> l;

    for(int i=0; i < l.capacity(); i++)
    {
        l.insert(0, i);
    }

    for(int i = 0; i < l.length(); i++)
    {
        cout << l[i] << endl;
    }

    l[0] *= l[0];

    for(int i = 0; i < l.length(); i++)
    {
        cout << l[i] << endl;
    }

    try
    {
        l[5];
    }
    catch(const Exception& e)
    {
        cout << e.message() << endl;
        cout << e.location() << endl;
    }

    return 0;
}

   

3、DynamicList设计要点 

申请连续堆空间作为顺序存储空间 ,动态设置顺序存储空间的大小 ,保证重置顺序存储空间时的异常安全性

函数异常安全的概念 :不泄漏任何资源,不允许破坏数据 

函数异常安全的基本保证 :如果异常被抛出 

               ★ 对象内的任何成员仍然能保持有效状态 

               ★ 没有数据的破坏及资源泄漏

4、编程实验 

DynamicList的实现     DynamicList.h

#ifndef DYNAMICLIST_H
#define DYNAMICLIST_H

#include "SeqList.h"
#include "Exception.h"

namespace DTLib
{

template <typename T>
class DynamicList : public SeqList<T>
{
protected:
    int m_capacity; // 存储空间容量
    
public:
    DynamicList(int capacity)
    {
        this->m_array = new T[capacity];

        if(this->m_array != NULL)
        {
            this->m_length = 0;
            this->m_capacity = capacity;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicList object ...");
        }
    }   
    int capacity() const
    {
        return m_capacity;
    }

    /*重置存储空间大小*/
    void resize(int capacity)
    {
        if(m_capacity != capacity)
        {
            T* array = new T[capacity];
            
            if(array != NULL)
            {
                int length = (this->m_length < capacity ? this->m_length : capacity);

                for(int i = 0; i < length; i++)
                {
                    array[i] = this->m_array[i]; // 可能发生异常位置
                }

                T* temp = this->m_array; // 标记原来的堆空间
                /*若在这里直接delete调用析构函数,可能抛出异常,函数直接返回,顺序表不可用了*/
                
                this->m_array = array;
                this->m_length = length;
                this->m_capacity = capacity;

                delete[] temp;
            }
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to resize DynamicList object ...");
        }
    }
    
    ~DynamicList()
    {
        delete[] this->m_array;
    }
};
}


#endif // DYNAMICLIST_H

main.cpp

#include <iostream>
#include "DynamicList.h"

using namespace std;
using namespace DTLib;

int main()
{
    DynamicList<int> l(5);

    for(int i = 0; i < l.capacity(); i++)
    {
        l.insert(0, i);
    }

    for(int i = 0; i < l.length(); i++)
    {
        cout << l[i] << endl;
    }

    l[0] *= l[0];

    for(int i = 0; i < l.length(); i++)
    {
        cout << l[i] <<endl;
    }

    try
    {
        l[5];
    }
    catch(const Exception& e)
    {
        cout << e.message() << endl;
        cout << e.location() << endl;

        l.resize(10);
        l.insert(5, 50);
    }

    for(int i = 0; i < l.length(); i++)
    {
        cout << l[i] << endl;
    }

    cout << endl;

    l.resize(3);

    for(int i = 0; i < l.length(); i++)
    {
        cout << l[i] << endl;
    }

    return 0;
}

 

 

5、易错点

下面的代码正确吗?为什么?

StaticList<int, 5> list;

for(int i = 0; i < list.capacity(); i++)
{
    list[i] = i * i; // error 将线性表当数组使用
}

线性表必须先插入元素,才能使用操作符[]访问元素

 

7、小结 

           StaticList通过模板参数定义顺序存储空间

           DynamicList通过动态内存申请定义顺序存储空间

           DynamicList支持动态重置顺序存储空间的大小

           DynamicList中的resize()函数实现需要保证异常安全

顺序存储线性表可能被当成数组误用 

工程开发中可以考虑使用数组类代替原生数组使用 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值