zoukankan      html  css  js  c++  java
  • 第6课

    1. 线性表的顺序存储结构

    【顺序存储结构定义】

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

    【设计思路】

    可以用一维数组来实现顺序存储结构的线性表

    1 template <typename T>
    2 class SeqList : public List<T>
    3 {
    4 protected:
    5     T *m_array;    //顺序存储空间
    6     int m_length;  //当前线性表长度
    7 };

    【顺序存储线性表类的组成】

    DTLib顺序存储结构线性表相关的类有三个:

     - SeqList:顺序存储结构线性表的抽象父类

     - StaticList:SeqList的子类,使用原生数组作为顺序存储空间

     - DynamicList:SeqList的子类,申请连续堆空间作为顺序存储空间

    2. SeqList类的实现

    【设计要点】

     - 抽象类模板

     - 仅实现顺序存储结构线性表的关键操作(增,删,改,等)

     - 存储空间的位置和大小由子类完成

     - 提供数组操作符,方便快速获取元素

    【接口设计】

     1 template <typename T>
     2 class SeqList : public List<T>
     3 {
     4 public:
     5     bool insert(int i, const T &e);    //在位置i处插入新元素e
     6     bool insertTail(const T &e);       //在尾部插入新元素e
     7     bool remove(int i);                //删除位置i处的元素
     8     bool set(int i, const T &e);       //设置位置i处的元素为e
     9     int find(const T &e) const;        //查找e在线性表中第一次出现的位置;若e不存在,则返回-1
    10     bool get(int i, T &e) const;       //获取位置i处的元素,通过参数e返回
    11     T get(int i) const;                //获取位置i处的元素,通过返回值返回
    12     int length() const;                //获取线性表长度
    13     void clear();                      //清空线性表
    14 
    15     //数组访问形式,提供const和非const版本
    16     T &operator[] (int i);
    17     T &operator[] (int i) const;
    18 
    19     //顺序存储空间的大小,由子类实现
    20     virtual int capacity() const = 0;
    21 };

    【插入功能实现】

    1. 检查目标位置是否合法
    2. 检查是否有剩余存储空间可供插入
    3. 将目标位置处元素及目标位置之后的所有元素后移一个位置
    4. 将新元素插入目标位置
    5. 线性表长度加1
     1 //在位置i处插入新元素e
     2 bool insert(int i, const T &e)
     3 {
     4     bool ret = (0 <= i) && (i <= m_length);       //目标位置可以为m_length,表示在尾部插入
     5     ret = ret && ((m_length + 1) <= capacity());  //确保有剩余空间可供插入
     6 
     7     if (ret)
     8     {
     9         //将目标位置处元素及目标位置之后的所有元素后移一个位置
    10         for (int p = m_length - 1; p >= i; p--)
    11         {
    12             m_array[p + 1] = m_array[p];
    13         }
    14 
    15         m_array[i] = e;
    16         m_length++;
    17     }
    18 
    19     return ret;
    20 }
    21 
    22 //在尾部插入新元素e
    23 bool insertTail(const T &e)
    24 {
    25     return insert(m_length, e);
    26 }

    【删除功能实现】

    1. 检查目标位置是否合法
    2. 将目标位置之后的所有元素前移一个位置
    3. 线性表长度减一
     1 //删除位置i处的元素
     2 bool remove(int i)
     3 {
     4     bool ret = (0 <= i) && (i < m_length);
     5 
     6     if (ret)
     7     {
     8         //将目标位置之后的所有元素前移一个位置
     9         for (int p = i + 1; p < m_length; p++)
    10         {
    11             m_array[p - 1] = m_array[p];
    12         }
    13 
    14         m_length--;
    15     }
    16 
    17     return ret;
    18 }

    【设置功能实现】

    1. 检查目标位置合法性
    2. 将目标位置处的元素替换为要设置的值
     1 //设置位置i处的元素为e
     2 bool set(int i, const T &e)
     3 {
     4     bool ret = (0 <= i) && (i < m_length);
     5 
     6     if (ret)
     7     {
     8         m_array[i] = e;
     9     }
    10 
    11     return ret;
    12 }

    【查找功能实现】

    1. 遍历顺序表,比较目标元素与各位置处元素是否相等
    2. 若相等,结束遍历,返回其位置;若遍历结束仍未找到,则返回-1
     1 //查找e在线性表中第一次出现的位置;若e不存在,则返回-1
     2 int find(const T &e) const
     3 {
     4     int ret = -1;
     5 
     6     for (int i = 0; i < m_length; i++)
     7     {
     8         if (m_array[i] == e)
     9         {
    10             ret = i;
    11             break;
    12         }
    13     }
    14 
    15     return ret;
    16 }

    【获取功能实现】

    1. 检查目标位置合法性
    2. 将目标位置作为数组下标获取元素
     1 //获取位置i处的元素,通过参数e返回
     2 bool get(int i, T &e) const
     3 {
     4     bool ret = (0 <= i) && (i < m_length);
     5 
     6     if (ret)
     7     {
     8         e = m_array[i];
     9     }
    10 
    11     return ret;
    12 }
    13 
    14 //获取位置i处的元素,通过返回值返回
    15 T get(int i) const
    16 {
    17     T ret;
    18 
    19     if (get(i, ret))
    20     {
    21         return ret;
    22     }
    23     else
    24     {
    25         THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ...");
    26     }
    27 }

    【获取长度与清空功能实现】

    1. 获取线性表长度,直接将m_length返回
    2. 清空线性表,只需将m_length清0
     1 //获取线性表长度
     2 int length() const
     3 {
     4     return m_length;
     5 }
     6 
     7 //清空线性表
     8 void clear()
     9 {
    10     m_length = 0;
    11 }

    【数组访问方式实现】

    1. 检查目标位置合法性
    2. 对于非const版本,直接将目标位置处元素返回
    3. 对于const版本,先对当前对象进行const_cast强制类型转换,去除其const属性,然后再调用非const版本的数组访问函数
     1 //数组访问,用于非const对象
     2 T &operator[] (int i)
     3 {
     4    if ((0 <= i) && (i < m_length))
     5    {
     6        return m_array[i];
     7    }
     8    else
     9    {
    10        THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ...");
    11    }
    12 }
    13 
    14 //数组访问,用于const对象
    15 T &operator[] (int i) const
    16 {
    17     return (const_cast<SeqList<T> &>(*this))[i];
    18 }

    【SeqList完整实现】

    以下是SeqList.h的完整源码。

      1 #ifndef SEQLIST_H
      2 #define SEQLIST_H
      3 
      4 #include "List.h"
      5 #include "Exception.h"
      6 
      7 namespace DTLib
      8 {
      9 
     10 template <typename T>
     11 class SeqList : public List<T>
     12 {
     13 protected:
     14     T *m_array;    //顺序存储空间
     15     int m_length;  //当前线性表长度
     16 public:
     17     //在位置i处插入新元素e
     18     bool insert(int i, const T &e)
     19     {
     20         bool ret = (0 <= i) && (i <= m_length);       //目标位置可以为m_length,表示在尾部插入
     21         ret = ret && ((m_length + 1) <= capacity());  //确保有剩余空间可供插入
     22 
     23         if (ret)
     24         {
     25             //将目标位置处元素及目标位置之后的所有元素后移一个位置
     26             for (int p = m_length - 1; p >= i; p--)
     27             {
     28                 m_array[p + 1] = m_array[p];
     29             }
     30 
     31             m_array[i] = e;
     32             m_length++;
     33         }
     34 
     35         return ret;
     36     }
     37 
     38     //在尾部插入新元素e
     39     bool insertTail(const T &e)
     40     {
     41         return insert(m_length, e);
     42     }
     43 
     44     //删除位置i处的元素
     45     bool remove(int i)
     46     {
     47         bool ret = (0 <= i) && (i < m_length);
     48 
     49         if (ret)
     50         {
     51             //将目标位置之后的所有元素前移一个位置
     52             for (int p = i + 1; p < m_length; p++)
     53             {
     54                 m_array[p - 1] = m_array[p];
     55             }
     56 
     57             m_length--;
     58         }
     59 
     60         return ret;
     61     }
     62 
     63     //设置位置i处的元素为e
     64     bool set(int i, const T &e)
     65     {
     66         bool ret = (0 <= i) && (i < m_length);
     67 
     68         if (ret)
     69         {
     70             m_array[i] = e;
     71         }
     72 
     73         return ret;
     74     }
     75 
     76     //查找e在线性表中第一次出现的位置;若e不存在,则返回-1
     77     int find(const T &e) const
     78     {
     79         int ret = -1;
     80 
     81         for (int i = 0; i < m_length; i++)
     82         {
     83             if (m_array[i] == e)
     84             {
     85                 ret = i;
     86                 break;
     87             }
     88         }
     89 
     90         return ret;
     91     }
     92 
     93     //获取位置i处的元素,通过参数e返回
     94     bool get(int i, T &e) const
     95     {
     96         bool ret = (0 <= i) && (i < m_length);
     97 
     98         if (ret)
     99         {
    100             e = m_array[i];
    101         }
    102 
    103         return ret;
    104     }
    105 
    106     //获取位置i处的元素,通过返回值返回
    107     T get(int i) const
    108     {
    109         T ret;
    110 
    111         if (get(i, ret))
    112         {
    113             return ret;
    114         }
    115         else
    116         {
    117             THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ...");
    118         }
    119 
    120         return ret;
    121     }
    122 
    123     //获取线性表长度
    124     int length() const
    125     {
    126         return m_length;
    127     }
    128 
    129     //清空线性表
    130     void clear()
    131     {
    132         m_length = 0;
    133     }
    134 
    135     //数组访问,用于非const对象
    136     T &operator[] (int i)
    137     {
    138        if ((0 <= i) && (i < m_length))
    139        {
    140            return m_array[i];
    141        }
    142        else
    143        {
    144            THROW_EXCEPTION(IndexOutOfBoundsException, "Parameter i is invalid ...");
    145        }
    146     }
    147 
    148     //数组访问,用于const对象
    149     T &operator[] (int i) const
    150     {
    151         return (const_cast<SeqList<T> &>(*this))[i];
    152     }
    153 
    154     //顺序存储空间的最大容量,由子类实现
    155     virtual int capacity() const = 0;
    156 };
    157 
    158 }
    159 
    160 #endif // SEQLIST_H
    View Code

    3. StaticList类的实现

    【设计要点】

     - 类模板

     - 使用原生数组作为顺序存储空间

     - 使用模板参数决定数组大小

    【设计实现】

    StaticList实现比较简单,就不多说了,只需要注意一点:在构造函数中将父类的m_array指针指向子类的m_space数组。

     1 #ifndef STATICLIST_H
     2 #define STATICLIST_H
     3 
     4 #include "SeqList.h"
     5 
     6 namespace DTLib
     7 {
     8 
     9 template <typename T, int N>
    10 class StaticList : public SeqList<T>
    11 {
    12 protected:
    13     T m_space[N];
    14 public:
    15     StaticList()
    16     {
    17         this->m_array = m_space;
    18         this->m_length = 0;
    19     }
    20 
    21     int capacity() const
    22     {
    23         return N;
    24     }
    25 };
    26 
    27 }
    28 
    29 #endif // STATICLIST_H

     【StaticList类简单测试】

    下面写一点代码对StaticList类进行简单的功能测试。

     1 #include "StaticList.h"
     2 #include <iostream>
     3 
     4 using namespace DTLib;
     5 using namespace std;
     6 
     7 int main()
     8 { 
     9     StaticList<int, 5> list;
    10 
    11     for (int i = 0; i < (list.capacity() - 1); i++)
    12     {
    13         list.insert(list.length(), i);
    14     }
    15 
    16     list.insertTail(4);
    17 
    18     for (int i = 0; i < list.length(); i++)
    19     {
    20         cout << list.get(i) << endl;
    21     }
    22 
    23     cout << endl;
    24 
    25     list[4] = 16;
    26     cout << list[4] << endl;
    27 
    28     int v = 0;
    29     list.set(3, 9);
    30     list.get(3, v);
    31     cout << v << endl;
    32 
    33     cout << endl;
    34 
    35     try
    36     {
    37         list[5] = 5;
    38     }
    39     catch(const Exception &e)
    40     {
    41         cout << e.location() << endl;
    42         cout << e.message() << endl;
    43     }
    44 
    45     return 0;
    46 }

    运行结果如下,初步测试功能正常。

    4. DynamicList类的实现

    【设计要点】

     - 类模板

     - 申请连续堆空间作为顺序存储空间

     - 支持动态设置(resize)顺序存储空间的大小

     1 class DynamicList : public SeqList<T>
     2 {
     3 protected:
     4     int m_capacity; 
     5 public:
     6     DynamicList(int capacity); //动态申请存储空间
     7     int capacity() const;      //获取存储空间大小
     8     void resize(int capacity); //重设存储空间大小
     9     ~DynamicList();            //释放存储空间
    10 };

     【设计实现】

    首先实现构造和析构函数,分别用于申请和释放顺序存储空间。

     1 DynamicList(int capacity)
     2 {
     3     this->m_array = new T[capacity];
     4 
     5     if (this->m_array != NULL)
     6     {
     7         this->m_length = 0;
     8         this->m_capacity = capacity;
     9     }
    10     else
    11     {
    12         THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicList object ...");
    13     }
    14 }
    15 
    16 ~DynamicList()
    17 {
    18     delete[] this->m_array;
    19 }

    然后实现父类的纯虚函数capaticy()。

    1 int capacity() const
    2 {
    3     return m_capacity;
    4 }

    最后实现DynamicList的关键函数resize(),该函数用于在当前存储空间过大或过小时重设存储空间。

     1 void resize(int capacity)
     2 {
     3     if (capacity != m_capacity)
     4     {
     5         T *array = new T[capacity]; //申请新的存储空间
     6 
     7         if (array != NULL)
     8         {
     9             /*拷贝数据到新的存储空间*/
    10             int length = (this->m_length < capacity) ? this->m_length : capacity;
    11 
    12             for (int i = 0; i < length; i++)
    13             {
    14                 /*
    15                  * 若T是类类型,且[]操作符被重载,那么array[i]是有可能产生异常的;
    16                  * 但DTLib无法顾全这一点,只能由使用者保证。
    17                 */
    18                 array[i] = this->m_array[i];
    19             }
    20 
    21             /*保证异常安全*/
    22             T *temp = this->m_array;
    23 
    24             this->m_array = array;
    25             this->m_length = length;
    26             this->m_capacity = capacity;
    27 
    28             delete[] temp;
    29         }
    30         else
    31         {
    32             THROW_EXCEPTION(NoEnoughMemoryException, "No memory to resize DynamicList object ...");
    33         }
    34     }
    35 }

    其中,resize()第22~28行的设计可以保证函数异常安全。所谓函数异常安全,指的是在调用函数时如果有异常被抛出,那么:

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

     - 没有数据的破坏以及资源的泄露

    如果将第22~28行代码改为如下方式(非异常安全),那么当T为类类型的时候delete[] this->m_array会触发析构函数调用,析构函数中有可能抛出异常,

    使后面三行代码无法执行,最终造成DynamicList对象的数据被破坏。

    1 delete[] this->m_array;
    2 
    3 this->m_array = array;
    4 this->m_length = length;
    5 this->m_capacity = capacity;

    【DynamicList完整实现】

     1 #ifndef DYNAMICLIST_H
     2 #define DYNAMICLIST_H
     3 
     4 #include "SeqList.h"
     5 #include "Exception.h"
     6 
     7 namespace DTLib
     8 {
     9 
    10 template <typename T>
    11 class DynamicList : public SeqList<T>
    12 {
    13 protected:
    14     int m_capacity;
    15 public:
    16     DynamicList(int capacity)
    17     {
    18         this->m_array = new T[capacity];
    19 
    20         if (this->m_array != NULL)
    21         {
    22             this->m_length = 0;
    23             this->m_capacity = capacity;
    24         }
    25         else
    26         {
    27             THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create DynamicList object ...");
    28         }
    29     }
    30 
    31     int capacity() const
    32     {
    33         return m_capacity;
    34     }
    35 
    36     void resize(int capacity)
    37     {
    38         if (capacity != m_capacity)
    39         {
    40             T *array = new T[capacity]; //申请新的存储空间
    41 
    42             if (array != NULL)
    43             {
    44                 /*拷贝数据到新的存储空间*/
    45                 int length = (this->m_length < capacity) ? this->m_length : capacity;
    46 
    47                 for (int i = 0; i < length; i++)
    48                 {
    49                     /*
    50                      * 若T是类类型,且[]操作符被重载,那么array[i]是有可能产生异常的;
    51                      * 但DTLib无法顾全这一点,只能由使用者保证。
    52                     */
    53                     array[i] = this->m_array[i];
    54                 }
    55 
    56                 /*保证异常安全*/                        
    57                 T *temp = this->m_array;
    58 
    59                 this->m_array = array;
    60                 this->m_length = length;
    61                 this->m_capacity = capacity;
    62 
    63                 delete[] temp;
    64             }
    65             else
    66             {
    67                 THROW_EXCEPTION(NoEnoughMemoryException, "No memory to resize DynamicList object ...");
    68             }
    69         }
    70     }
    71 
    72     ~DynamicList()
    73     {
    74         delete[] this->m_array;
    75     }
    76 };
    77 
    78 }
    79 
    80 #endif // DYNAMICLIST_H
    View Code

    【DynamicList功能测试】

    测试代码:

     1 #include "DynamicList.h"
     2 #include <iostream>
     3 
     4 using namespace DTLib;
     5 using namespace std;
     6 
     7 int main()
     8 { 
     9     DynamicList<int> list(5);
    10 
    11     for (int i = 0; i < list.capacity(); i++)
    12     {
    13         list.insert(0, i);
    14     }
    15 
    16     for (int i = 0; i < list.length(); i++)
    17     {
    18         cout << list[i] << endl;
    19     }
    20 
    21     cout << endl;
    22 
    23     try
    24     {
    25         list[5] = 5;
    26     }
    27     catch(const Exception &e)
    28     {
    29         cout << e.location() << endl;
    30         cout << e.message() << endl;
    31 
    32         list.resize(10);
    33         list.insert(5, 50);
    34 
    35         for (int i = 0; i < list.length(); i++)
    36         {
    37             cout << list[i] << endl;
    38         }
    39 
    40         cout << endl;
    41     }
    42 
    43     list.resize(3);
    44 
    45     for (int i = 0; i < list.length(); i++)
    46     {
    47         cout << list[i] << endl;
    48     }
    49 
    50     return 0;
    51 }

    测试结果:

    5. 小结

    1. StaticList通过模板参数定义顺序存储空间
    2. DynamicList通过动态内存申请定义顺序存储空间
    3. DynamicList支持动态重置顺序存储空间的大小
    4. DynamicList中的resize()函数需要保证异常安全

    注:本文整理于狄泰《数据结构开发实战教程》课程内容

  • 相关阅读:
    MIX11大会WP7主题演讲中文字幕版
    日本战神——源义经
    System.Web.HttpUtility for .Net Compact Framework
    VS2010 SP1
    <如何成为一个成功的职业经理人>读书笔记2
    <左手曾国藩,右手胡雪岩>读书笔记
    <福布斯荐75本经商必读06基业长青>读书笔记
    <中国人聪明之道>读书笔记
    <浮沉>读书笔记
    <79个潜规则:改变生活的心理学法则>读书笔记
  • 原文地址:https://www.cnblogs.com/songhe364826110/p/9195820.html
Copyright © 2011-2022 走看看