zoukankan      html  css  js  c++  java
  • 第二十二课 单链表的具体实现

    本节目标:

    添加LinkList.h文件:

      1 #ifndef LINKLIST_H
      2 #define LINKLIST_H
      3 
      4 #include "List.h"
      5 #include "Exception.h"
      6 
      7 namespace DTLib
      8 {
      9 
     10 template < typename T >
     11 class LinkList : public List<T>
     12 {
     13 protected:
     14     struct Node : public Object
     15     {
     16         T value;
     17         Node* next;
     18     };
     19 
     20     mutable Node m_header;
     21     int m_length;
     22 public:
     23     LinkList()
     24     {
     25         m_header.next = NULL;
     26         m_length = 0;
     27     }
     28 
     29     bool insert(const T& e)
     30     {
     31         return insert(m_length, e);
     32     }
     33 
     34     bool insert(int i, const T& e)
     35     {
     36         bool ret = ((0 <= i) && (i <= m_length));
     37 
     38         if( ret )
     39         {
     40             Node* node = new Node();
     41 
     42             if( node != NULL )
     43             {
     44                 Node* current = &m_header;
     45 
     46                 for(int p=0; p<i; p++)
     47                 {
     48                     current = current->next;
     49                 }
     50 
     51                 node->value = e;
     52                 node->next = current->next;
     53                 current->next = node;
     54 
     55                 m_length++;
     56             }
     57             else
     58             {
     59                 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element...");
     60             }
     61         }
     62 
     63         return ret;
     64     }
     65 
     66     bool remove(int i)
     67     {
     68         bool ret = ((0 <= i) && (i < m_length));
     69 
     70         if( ret )
     71         {
     72             Node* current = &m_header;
     73 
     74             for(int p=0; p<i; p++)
     75             {
     76                 current = current->next;
     77             }
     78 
     79             Node* toDel = current->next;
     80 
     81             current->next = toDel->next;
     82 
     83             delete toDel;
     84 
     85             m_length--;
     86         }
     87 
     88         return ret;
     89     }
     90 
     91     bool set(int i, const T& e)
     92     {
     93         bool ret = ((0 <= i) && (i < m_length));
     94 
     95         if( ret )
     96         {
     97             Node* current = &m_header;
     98 
     99             for(int p=0; p<i; p++)
    100             {
    101                 current = current->next;
    102             }
    103 
    104             current->next->value = e;
    105         }
    106 
    107         return ret;
    108     }
    109 
    110     bool get(int i, T& e) const
    111     {
    112         bool ret = ((0 <= i) && (i < m_length));
    113 
    114         if( ret )
    115         {
    116             Node* current = &m_header;
    117 
    118             for(int p=0; p<i; p++)
    119             {
    120                 current = current->next;
    121             }
    122 
    123             e = current->next->value;
    124         }
    125 
    126         return ret;
    127     }
    128 
    129     int length() const
    130     {
    131         return m_length;
    132     }
    133 
    134     void clear()
    135     {
    136         while( m_header.next )
    137         {
    138             Node* toDel = m_header.next;
    139 
    140             m_header.next = toDel->next;
    141 
    142             delete toDel;
    143         }
    144 
    145         m_length = 0;
    146     }
    147 
    148     ~LinkList()
    149     {
    150         clear();
    151     }
    152 };
    153 
    154 }
    155 
    156 #endif // LINKLIST_H

    第110行的get是const函数,116行我们取m_header的指针,这时编译器会认为我们要改变m_header,编译会报错,因此,我们在第20行给m_header这个变量加上mutable属性。

    测试程序如下:

     1 #include <iostream>
     2 #include "LinkList.h"
     3 
     4 
     5 using namespace std;
     6 using namespace DTLib;
     7 
     8 
     9 int main()
    10 {
    11 
    12     LinkList<int> list;
    13 
    14     for(int i = 0; i<5; i++)
    15     {
    16         list.insert(i);
    17     }
    18 
    19     for(int i = 0; i < list.length(); i++)
    20     {
    21         int v = 0;
    22 
    23         list.get(i, v);
    24 
    25         cout << v << endl;
    26     }
    27     return 0;
    28 }

    运行结果如下

     get函数的使用不是很方便,我们添加一个重载函数:

      1 #ifndef LINKLIST_H
      2 #define LINKLIST_H
      3 
      4 #include "List.h"
      5 #include "Exception.h"
      6 
      7 namespace DTLib
      8 {
      9 
     10 template < typename T >
     11 class LinkList : public List<T>
     12 {
     13 protected:
     14     struct Node : public Object
     15     {
     16         T value;
     17         Node* next;
     18     };
     19 
     20     mutable Node m_header;
     21     int m_length;
     22 public:
     23     LinkList()
     24     {
     25         m_header.next = NULL;
     26         m_length = 0;
     27     }
     28 
     29     bool insert(const T& e)
     30     {
     31         return insert(m_length, e);
     32     }
     33 
     34     bool insert(int i, const T& e)
     35     {
     36         bool ret = ((0 <= i) && (i <= m_length));
     37 
     38         if( ret )
     39         {
     40             Node* node = new Node();
     41 
     42             if( node != NULL )
     43             {
     44                 Node* current = &m_header;
     45 
     46                 for(int p=0; p<i; p++)
     47                 {
     48                     current = current->next;
     49                 }
     50 
     51                 node->value = e;
     52                 node->next = current->next;
     53                 current->next = node;
     54 
     55                 m_length++;
     56             }
     57             else
     58             {
     59                 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element...");
     60             }
     61         }
     62 
     63         return ret;
     64     }
     65 
     66     bool remove(int i)
     67     {
     68         bool ret = ((0 <= i) && (i < m_length));
     69 
     70         if( ret )
     71         {
     72             Node* current = &m_header;
     73 
     74             for(int p=0; p<i; p++)
     75             {
     76                 current = current->next;
     77             }
     78 
     79             Node* toDel = current->next;
     80 
     81             current->next = toDel->next;
     82 
     83             delete toDel;
     84 
     85             m_length--;
     86         }
     87 
     88         return ret;
     89     }
     90 
     91     bool set(int i, const T& e)
     92     {
     93         bool ret = ((0 <= i) && (i < m_length));
     94 
     95         if( ret )
     96         {
     97             Node* current = &m_header;
     98 
     99             for(int p=0; p<i; p++)
    100             {
    101                 current = current->next;
    102             }
    103 
    104             current->next->value = e;
    105         }
    106 
    107         return ret;
    108     }
    109 
    110     T get(int i) const
    111     {
    112         T ret;
    113 
    114         if( get(i, ret) )
    115         {
    116             return ret;
    117         }
    118         else
    119         {
    120             THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
    121         }
    122 
    123         return ret;
    124     }
    125 
    126     bool get(int i, T& e) const
    127     {
    128         bool ret = ((0 <= i) && (i < m_length));
    129 
    130         if( ret )
    131         {
    132             Node* current = &m_header;
    133 
    134             for(int p=0; p<i; p++)
    135             {
    136                 current = current->next;
    137             }
    138 
    139             e = current->next->value;
    140         }
    141 
    142         return ret;
    143     }
    144 
    145     int length() const
    146     {
    147         return m_length;
    148     }
    149 
    150     void clear()
    151     {
    152         while( m_header.next )
    153         {
    154             Node* toDel = m_header.next;
    155 
    156             m_header.next = toDel->next;
    157 
    158             delete toDel;
    159         }
    160 
    161         m_length = 0;
    162     }
    163 
    164     ~LinkList()
    165     {
    166         clear();
    167     }
    168 };
    169 
    170 }
    171 
    172 #endif // LINKLIST_H

    第110行为添加的重载的get函数,这个函数直接返回需要的值。

    测试程序如下:

     1 #include <iostream>
     2 #include "LinkList.h"
     3 
     4 
     5 using namespace std;
     6 using namespace DTLib;
     7 
     8 
     9 int main()
    10 {
    11 
    12     LinkList<int> list;
    13 
    14     for(int i = 0; i<5; i++)
    15     {
    16         list.insert(i);
    17     }
    18 
    19     for(int i = 0; i < list.length(); i++)
    20     {
    21         cout << list.get(i) << endl;
    22     }
    23 
    24     list.remove(2);
    25 
    26     for(int i = 0; i < list.length(); i++)
    27     {
    28         cout << list.get(i) << endl;
    29     }
    30 
    31     return 0;
    32 }

    结果如下:

    问题:

    头结点是否存在隐患?实现代码是否需要优化?

    我们定义的Node结构里面,只用到了next成员,而value成员是由用户指定的,如果用户自己定义了一个类型Test,在这个类型的构造函数中抛出异常。 如果用户使用这个Test类型来定义list的话,就会出现问题。

    测试程序:

    结果如下:

    我们根本没有创建有问题的类Test的对象,只是创建了一个单链表LinkList的对象,而这确报错了。

    没有创建Test类对象,就报错了,这时库的创建者是要负责的,而不是用户来负责。

     原因:

    我们的LinkList类中有一个m_header成员,在构造LinkList对象的时候,也会构造Node对象,进一步构造T对象,如下:

    构造T对象时就和用户定义的对象类型有关了。

    改进程序:

      1 #ifndef LINKLIST_H
      2 #define LINKLIST_H
      3 
      4 #include "List.h"
      5 #include "Exception.h"
      6 
      7 namespace DTLib
      8 {
      9 
     10 template < typename T >
     11 class LinkList : public List<T>
     12 {
     13 protected:
     14     struct Node : public Object
     15     {
     16         T value;
     17         Node* next;
     18     };
     19 
     20     mutable struct
     21     {
     22         char reserved[sizeof(T)];
     23         Node* next;
     24     }m_header;
     25 
     26     int m_length;
     27 public:
     28     LinkList()
     29     {
     30         m_header.next = NULL;
     31         m_length = 0;
     32     }
     33 
     34     bool insert(const T& e)
     35     {
     36         return insert(m_length, e);
     37     }
     38 
     39     bool insert(int i, const T& e)
     40     {
     41         bool ret = ((0 <= i) && (i <= m_length));
     42 
     43         if( ret )
     44         {
     45             Node* node = new Node();
     46 
     47             if( node != NULL )
     48             {
     49                 Node* current = reinterpret_cast<Node*>(&m_header);
     50 
     51                 for(int p=0; p<i; p++)
     52                 {
     53                     current = current->next;
     54                 }
     55 
     56                 node->value = e;
     57                 node->next = current->next;
     58                 current->next = node;
     59 
     60                 m_length++;
     61             }
     62             else
     63             {
     64                 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element...");
     65             }
     66         }
     67 
     68         return ret;
     69     }
     70 
     71     bool remove(int i)
     72     {
     73         bool ret = ((0 <= i) && (i < m_length));
     74 
     75         if( ret )
     76         {
     77             Node* current = reinterpret_cast<Node*>(&m_header);
     78 
     79             for(int p=0; p<i; p++)
     80             {
     81                 current = current->next;
     82             }
     83 
     84             Node* toDel = current->next;
     85 
     86             current->next = toDel->next;
     87 
     88             delete toDel;
     89 
     90             m_length--;
     91         }
     92 
     93         return ret;
     94     }
     95 
     96     bool set(int i, const T& e)
     97     {
     98         bool ret = ((0 <= i) && (i < m_length));
     99 
    100         if( ret )
    101         {
    102             Node* current = reinterpret_cast<Node*>(&m_header);
    103 
    104             for(int p=0; p<i; p++)
    105             {
    106                 current = current->next;
    107             }
    108 
    109             current->next->value = e;
    110         }
    111 
    112         return ret;
    113     }
    114 
    115     T get(int i) const
    116     {
    117         T ret;
    118 
    119         if( get(i, ret) )
    120         {
    121             return ret;
    122         }
    123         else
    124         {
    125             THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
    126         }
    127 
    128         return ret;
    129     }
    130 
    131     bool get(int i, T& e) const
    132     {
    133         bool ret = ((0 <= i) && (i < m_length));
    134 
    135         if( ret )
    136         {
    137             Node* current = reinterpret_cast<Node*>(&m_header);
    138 
    139             for(int p=0; p<i; p++)
    140             {
    141                 current = current->next;
    142             }
    143 
    144             e = current->next->value;
    145         }
    146 
    147         return ret;
    148     }
    149 
    150     int length() const
    151     {
    152         return m_length;
    153     }
    154 
    155     void clear()
    156     {
    157         while( m_header.next )
    158         {
    159             Node* toDel = m_header.next;
    160 
    161             m_header.next = toDel->next;
    162 
    163             delete toDel;
    164         }
    165 
    166         m_length = 0;
    167     }
    168 
    169     ~LinkList()
    170     {
    171         clear();
    172     }
    173 };
    174 
    175 }
    176 
    177 #endif // LINKLIST_H

    我们将m_header定义成了匿名类型,在内存布局中m_header和Node是一样的,这时构造LinkList对象时就不会调用T类型的构造函数了。

    此外,还需要改动49、77、102、137行,需要进行类型转换,否则编译不过。使用的类型转换为内存重解释reinterpret_cast<Node*>(&m_header)。

    测试程序:

     1 #include <iostream>
     2 #include "LinkList.h"
     3 
     4 
     5 using namespace std;
     6 using namespace DTLib;
     7 
     8 class Test
     9 {
    10 public:
    11     Test()
    12     {
    13         throw 0;
    14     }
    15 };
    16 
    17 int main()
    18 {
    19 
    20     LinkList<Test> list;
    21 
    22     cout << "Hello World" << endl;
    23 
    24     return 0;
    25 }

    这时就正常了,结果如下:

     代码优化:

    我们的程序中多次使用了current定位,因此可以进行重构:

    改进程序:

      1 #ifndef LINKLIST_H
      2 #define LINKLIST_H
      3 
      4 #include "List.h"
      5 #include "Exception.h"
      6 
      7 namespace DTLib
      8 {
      9 
     10 template < typename T >
     11 class LinkList : public List<T>
     12 {
     13 protected:
     14     struct Node : public Object
     15     {
     16         T value;
     17         Node* next;
     18     };
     19 
     20     mutable struct
     21     {
     22         char reserved[sizeof(T)];
     23         Node* next;
     24     }m_header;
     25 
     26     int m_length;
     27 
     28     Node* position(int i) const
     29     {
     30         Node* ret = reinterpret_cast<Node*>(&m_header);
     31 
     32         for(int p = 0; p < i; p++)
     33         {
     34             ret = ret->next;
     35         }
     36 
     37         return ret;
     38     }
     39 public:
     40     LinkList()
     41     {
     42         m_header.next = NULL;
     43         m_length = 0;
     44     }
     45 
     46     bool insert(const T& e)
     47     {
     48         return insert(m_length, e);
     49     }
     50 
     51     bool insert(int i, const T& e)
     52     {
     53         bool ret = ((0 <= i) && (i <= m_length));
     54 
     55         if( ret )
     56         {
     57             Node* node = new Node();
     58 
     59             if( node != NULL )
     60             {
     61                 Node* current = position(i);
     62 
     63                 node->value = e;
     64                 node->next = current->next;
     65                 current->next = node;
     66 
     67                 m_length++;
     68             }
     69             else
     70             {
     71                 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element...");
     72             }
     73         }
     74 
     75         return ret;
     76     }
     77 
     78     bool remove(int i)
     79     {
     80         bool ret = ((0 <= i) && (i < m_length));
     81 
     82         if( ret )
     83         {
     84             Node* current = position(i);
     85 
     86             Node* toDel = current->next;
     87 
     88             current->next = toDel->next;
     89 
     90             delete toDel;
     91 
     92             m_length--;
     93         }
     94 
     95         return ret;
     96     }
     97 
     98     bool set(int i, const T& e)
     99     {
    100         bool ret = ((0 <= i) && (i < m_length));
    101 
    102         if( ret )
    103         {
    104             position(i)->next->value = e;
    105         }
    106 
    107         return ret;
    108     }
    109 
    110     T get(int i) const
    111     {
    112         T ret;
    113 
    114         if( get(i, ret) )
    115         {
    116             return ret;
    117         }
    118         else
    119         {
    120             THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
    121         }
    122 
    123         return ret;
    124     }
    125 
    126     bool get(int i, T& e) const
    127     {
    128         bool ret = ((0 <= i) && (i < m_length));
    129 
    130         if( ret )
    131         {
    132             e = position(i)->next->value;
    133         }
    134 
    135         return ret;
    136     }
    137 
    138     int length() const
    139     {
    140         return m_length;
    141     }
    142 
    143     void clear()
    144     {
    145         while( m_header.next )
    146         {
    147             Node* toDel = m_header.next;
    148 
    149             m_header.next = toDel->next;
    150 
    151             delete toDel;
    152         }
    153 
    154         m_length = 0;
    155     }
    156 
    157     ~LinkList()
    158     {
    159         clear();
    160     }
    161 };
    162 
    163 }
    164 
    165 #endif // LINKLIST_H

    我们添加了position函数用来定位。

    测试程序:

     1 #include <iostream>
     2 #include "LinkList.h"
     3 
     4 
     5 using namespace std;
     6 using namespace DTLib;
     7 
     8 class Test
     9 {
    10 public:
    11     Test()
    12     {
    13         throw 0;
    14     }
    15 };
    16 
    17 int main()
    18 {
    19 
    20     LinkList<Test> list;
    21 
    22     cout << "Hello World" << endl;
    23 
    24     Test t;
    25 
    26     list.insert(t);
    27 
    28     return 0;
    29 }

    结果如下:

    可以看到是先打印的Hello World然后报的异常,这就是用户定义Test类对象的问题了,这应该由用户自己负责了。

     再次运行测试程序:

     1 #include <iostream>
     2 #include "LinkList.h"
     3 
     4 
     5 using namespace std;
     6 using namespace DTLib;
     7 
     8 
     9 int main()
    10 {
    11     LinkList<int> list;
    12 
    13     for(int i = 0; i<5; i++)
    14     {
    15         list.insert(0,i);
    16         list.set(0, i * i);
    17     }
    18 
    19     for(int i = 0; i < list.length(); i++)
    20     {
    21         cout << list.get(i) << endl;
    22     }
    23 
    24     list.clear();
    25 
    26     for(int i=0; i<list.length(); i++)
    27     {
    28         cout << list.get(i) << endl;
    29     }
    30 
    31     return 0;
    32 }

    这时只打印出了16,这不是我们想要的,可见重构代码出现了问题。

    这时因为我们的匿名类型变量m_header没有继承自Object,这样的话导致m_header和Node的内存布局有可能不一样,改进程序:

      1 #ifndef LINKLIST_H
      2 #define LINKLIST_H
      3 
      4 #include "List.h"
      5 #include "Exception.h"
      6 
      7 namespace DTLib
      8 {
      9 
     10 template < typename T >
     11 class LinkList : public List<T>
     12 {
     13 protected:
     14     struct Node : public Object
     15     {
     16         T value;
     17         Node* next;
     18     };
     19 
     20     mutable struct : public Object
     21     {
     22         char reserved[sizeof(T)];
     23         Node* next;
     24     }m_header;
     25 
     26     int m_length;
     27 
     28     Node* position(int i) const
     29     {
     30         Node* ret = reinterpret_cast<Node*>(&m_header);
     31 
     32         for(int p = 0; p < i; p++)
     33         {
     34             ret = ret->next;
     35         }
     36 
     37         return ret;
     38     }
     39 public:
     40     LinkList()
     41     {
     42         m_header.next = NULL;
     43         m_length = 0;
     44     }
     45 
     46     bool insert(const T& e)
     47     {
     48         return insert(m_length, e);
     49     }
     50 
     51     bool insert(int i, const T& e)
     52     {
     53         bool ret = ((0 <= i) && (i <= m_length));
     54 
     55         if( ret )
     56         {
     57             Node* node = new Node();
     58 
     59             if( node != NULL )
     60             {
     61                 Node* current = position(i);
     62 
     63                 node->value = e;
     64                 node->next = current->next;
     65                 current->next = node;
     66 
     67                 m_length++;
     68             }
     69             else
     70             {
     71                 THROW_EXCEPTION(NoEnoughMemoryException, "No memery to insert new element...");
     72             }
     73         }
     74 
     75         return ret;
     76     }
     77 
     78     bool remove(int i)
     79     {
     80         bool ret = ((0 <= i) && (i < m_length));
     81 
     82         if( ret )
     83         {
     84             Node* current = position(i);
     85 
     86             Node* toDel = current->next;
     87 
     88             current->next = toDel->next;
     89 
     90             delete toDel;
     91 
     92             m_length--;
     93         }
     94 
     95         return ret;
     96     }
     97 
     98     bool set(int i, const T& e)
     99     {
    100         bool ret = ((0 <= i) && (i < m_length));
    101 
    102         if( ret )
    103         {
    104             position(i)->next->value = e;
    105         }
    106 
    107         return ret;
    108     }
    109 
    110     T get(int i) const
    111     {
    112         T ret;
    113 
    114         if( get(i, ret) )
    115         {
    116             return ret;
    117         }
    118         else
    119         {
    120             THROW_EXCEPTION(IndexOutOfBoundsException, "Invalid parameter i to get element ...");
    121         }
    122 
    123         return ret;
    124     }
    125 
    126     bool get(int i, T& e) const
    127     {
    128         bool ret = ((0 <= i) && (i < m_length));
    129 
    130         if( ret )
    131         {
    132             e = position(i)->next->value;
    133         }
    134 
    135         return ret;
    136     }
    137 
    138     int length() const
    139     {
    140         return m_length;
    141     }
    142 
    143     void clear()
    144     {
    145         while( m_header.next )
    146         {
    147             Node* toDel = m_header.next;
    148 
    149             m_header.next = toDel->next;
    150 
    151             delete toDel;
    152         }
    153 
    154         m_length = 0;
    155     }
    156 
    157     ~LinkList()
    158     {
    159         clear();
    160     }
    161 };
    162 
    163 }
    164 
    165 #endif // LINKLIST_H

    第20行使匿名类继承自Object,测试程序如下:

     1 #include <iostream>
     2 #include "LinkList.h"
     3 
     4 
     5 using namespace std;
     6 using namespace DTLib;
     7 
     8 
     9 int main()
    10 {
    11     LinkList<int> list;
    12 
    13     for(int i = 0; i<5; i++)
    14     {
    15         list.insert(0,i);
    16         list.set(0, i * i);
    17     }
    18 
    19     for(int i = 0; i < list.length(); i++)
    20     {
    21         cout << list.get(i) << endl;
    22     }
    23 
    24     list.clear();
    25 
    26     for(int i=0; i<list.length(); i++)
    27     {
    28         cout << list.get(i) << endl;
    29     }
    30 
    31     return 0;
    32 }

    结果如下:

    问题已然解决了。

    小结:

  • 相关阅读:
    PAT (Advanced Level) 1114. Family Property (25)
    PAT (Advanced Level) 1113. Integer Set Partition (25)
    PAT (Advanced Level) 1112. Stucked Keyboard (20)
    PAT (Advanced Level) 1111. Online Map (30)
    PAT (Advanced Level) 1110. Complete Binary Tree (25)
    PAT (Advanced Level) 1109. Group Photo (25)
    PAT (Advanced Level) 1108. Finding Average (20)
    PAT (Advanced Level) 1107. Social Clusters (30)
    PAT (Advanced Level) 1106. Lowest Price in Supply Chain (25)
    PAT (Advanced Level) 1105. Spiral Matrix (25)
  • 原文地址:https://www.cnblogs.com/wanmeishenghuo/p/9650109.html
Copyright © 2011-2022 走看看