第三章 表、栈和队列

     在这一章主要学习STL的向量和表的实现。

3.3 STL中的向量和表

     表ADT有两个流行的实现。vector给出了表ADT的可增长的数组实现。使用vector的有点在于其常量时间的索引。缺点就是插入新项或删除已有项代价昂贵,除了在末端。

      list提供了表ADT的双向链表实现。使用list的有点是,如果变化发生的位置已知的话,插入新项和删除已有项的代价是很小的,缺点是list不容易索引。

      常用的公共方法:

    int size() const :返回容器内的元素的个数。

    void clear():删除容器中所有元素。

    bool empty():如果容器没有元素,返回TRUE,否则返回 FALSE

    void push_back(const Object & x):在表末端添加元素。

    void pop_back():在表末端弹出元素。

    const Object &back() const:返回末端元素。

    const Object &front() const:返回前端元素。

    void push_front(const Object &x) 返回在list前端添加元素。

    void pop_front() 在list前端删除元素

    Object & operator[] (int idx):返回vector的索引值

    Object & at(int idx):带边界检测的索引对象值。

    int capacity() const :返回vector内部的容量。

    void reserve(int newCapacity) 设定vector新的容量。

    常用迭代器方法:

    iterator begin() 容器第一项。

    iterator end() 容器终止

    itr++和++itr: 推进迭代器itr 至下一个位置。

   *itr 返回存储在迭代器itr指定位置的对象的引用。

   itr1==itr2、 itr1!=itr2、

   需要迭代器的容器操作:

    iterator insert(iterator pos,const Object &x):添加x到表中迭代器pos 所指向的位置之前的位置。返回值是一个指向插入项位置的迭代器。

   iterator erase(iterator pos):删除迭代器给出位置的对象。返回调用之前pos指向元素的下一个元素的位置。

    iterator erase(iterator start,iterator end):删除所有的从位置start 开始,直到位置end的所有元素。


3.4 向量的实现

    vector 是C++的基本数组。vector可以复制并且占用的内存可以自动回收。C++数组的一些重要特性。

    1、数组就是指向一块内存的指针变量,实际的数组大小必须由程序员单独确定。

    2、内存卡可以通过new[]来分配,但是相应也就用delete[]来释放。

    3、内存块大小不能改变(可以重新分配大块空间,对原来释放)。

    vector的一些实现巧妙的地方后面具体分析,我们的vector的实现直接上代码吧:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//ADT 常见操作:add remove size contains union find
/*
Vector 常用功能:

1、Vector将仍然是基本数组(通过一个指针变量来指向分配的内存卡),数组的容量和当前数组项数目存储在Vector里。
2、Vector 将通过实现“三大函数”,为复制构造函数和operator=提供深复制,同时也提供机构函数来回收基本数组。
3、Vector提供resize改变Vector大小,reverse来改变容量。分配一块新的内存然后复制旧内存,再然后释放内存。
4、Vector 将提供operator[]实现
5、Vector将提供size empty clear back pop_back push_back
6、Vector将支持嵌套的iterator和const_iterator和begin() 和end() 方法。

*/


template < typename Object>
class Vector
{
public:
     //构造函数
     explicit Vector( int initSize =  0):
        theSize(initSize), theCapacity(initSize + SPARE_CAPACITY)
    {
        objects =  new Object[theCapacity];
    }
     //复制构造函数与operator =相同
    Vector( const Vector &rhs): objects( NULL)
    {
         operator=(rhs);
    }
     //析构函数
    ~Vector()
    {
         delete []objects;
    }
     //operator = 函数
     const Vector & operator=( const Vector &rhs)
    {
         if( this != rhs)
        {
             delete []objects;
            theSize = rhs.theSize;
            theCapacity = rhs.theCapacity;

            objects =  new Object[capacity()];

             for( int i =  0; k < size(); k++)
            {
                objects[i] = rhs.object[i];
            }

        }
         return * this;

    }
     //容量函数
     int capacity()  const
    {
         return theCapacity;
    }
     //大小函数
     int size()  const
    {
         return theSize;
    }

     //扩大小函数
     void resize( int newSize)
    {
         if(newSize > theCapacity)
            reserve(newSize *  2 +  1);
        theSize = newSize;
    }
     //扩容函数
     void reserve( int newSize)
    {
         if(newSize < theSize)
             return ;
        Object *oldArray = objects;
        objects =  new Object[newSize];

         for( int i =  0; i < theSize; i++)
        {
            objects[i] = oldArray[i];
        }

        theCapacity = newSize;
         delete []oldArray;

    }
     //索引操作
    Object & operator[]( int index)
    {
         return objects[index];
    }

     const Object & operator[]( int index)  const
    {
         return objects[index];
    }

     //空判断
     bool empty()  const
    {
         return size() ==  0;
    }
     //在末端插入
     void push_back( const Object &x)
    {
         if(theSize == theCapacity)
            reserve( 2 * theCapacity +  1);
        objects[theSize++] = x;
    }
     //在末端删除
     void pop_back()
    {
        theSize--;
    }
     //获取末端元素
     const Object &back()  const
    {
         return objects[theSize -  1];
    }
     //定义iterator
     //定义const_iterator
     typedef Object *iterator;
     typedef Object *const_iterator;
     //开始
    iterator begin()
    {
         return &objects[ 0];
    }
     //结束
    iterator end()
    {
         return &objects[theSize -  1];
    }
     //常量开始
    const_iterator begin()  const
    {
         return &objects[ 0];
    }
      //常量结束
    const_iterator end()  const
    {
         return &objects[theSize -  1];
    }
     //使用enum来实现宏定义,effetive C++
     enum { SPARE_CAPACITY =  10};

private:
     int theSize;   //大小
     int theCapacity;   //容量
    Object *objects;   //数组
};
     vector实现几个地方是比较有亮点的,具体如下:

     常规函数的实现:构造函数、析构函数、复制构造函数、operator=函数实现。其中构造函数和析构函数通过new[]和delete[]函数, 复制构造函数通过operator=函数实现。

     其中operator=函数,首先通过if(this!=rhs)来判断是否自身,删除旧空间,复制大小,容量,新建空间、用for循环复制。返回*this。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//构造函数
explicit Vector( int initSize =  0):
    theSize(initSize), theCapacity(initSize + SPARE_CAPACITY)
{
    objects =  new Object[theCapacity];
}
//复制构造函数与operator =相同
Vector( const Vector &rhs): objects( NULL)
{
     operator=(rhs);
}
//析构函数
~Vector()
{
     delete []objects;
}
//operator = 函数
const Vector & operator=( const Vector &rhs)
{
     if( this != rhs)
    {
         delete []objects;
        theSize = rhs.theSize;
        theCapacity = rhs.theCapacity;

        objects =  new Object[capacity()];

         for( int i =  0; k < size(); k++)
        {
            objects[i] = rhs.object[i];
        }

    }
     return * this;

}

    容量常见操作:扩容函数,需要重新分配一个新的空间,然后复制旧空间的数据到新空间的数据,重定义容量大小,再删除旧的空间。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//扩充大小函数
void resize( int newSize)
{
     if(newSize > theCapacity)
        reserve(newSize *  2 +  1);
    theSize = newSize;
}
//扩充容量大小函数
void reserve( int newSize)
{
     if(newSize < theSize)
         return ;
    Object *oldArray = objects;
    objects =  new Object[newSize];

     for( int i =  0; i < theSize; i++)
    {
        objects[i] = oldArray[i];
    }

    theCapacity = newSize;
     delete []oldArray;

}
      在C++中尽量少用define,应使用enum或者inline来实现。如下:

 C++ Code 
1
enum { SPARE_CAPACITY =  10};


3.5 表的实现

    表的实现采用双向链表实现。我们直接上代码吧,后面具体分析实现的一些巧妙的地方。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
//表的实现
/*
List 类是双向链表
(1) List 类本身。包含连接到表两端的链接,表的大小以及一系列方法。
(2) Node类。该类看起来像是私有的嵌套类。一个节点包含数据和用来指向其前和其后的节点的指针,以及适当的构造函数。
 (3)  const_iterator类。该类抽象了位置的概念,是一个公有的嵌套类。除了operator*操作返回所指向项的引用,而不是该项的常量引用的功能外,
 iterator具有和const_iterator相同功能。重载操作符= == != 和++。
 (4)  iterator类。该类抽象了位置的概念,是一个公有的嵌套类。
*/


template < typename Object>
class List
{
private:
     struct Node
    {
        Node *prev;
        Node *next;
        Object data;
        Node( const Object &d = Object(), Node *p =  NULL, Node *n =  NULL):  //构造函数,初始化指针。
            data(d), prev(p), next(n)
        {}
    };
public:
     class const_iterator
    {
     public:
        const_iterator(): current( NULL)
        { }
         const Object & operator*()  const  //
        {
             return retrieve();
        }
        const_iterator & operator++()  //这个表示++i
        {
            current = current->next;
             return * this;
        }
        const_iterator & operator++( int)
        {
            const_iterator old = * this;
            ++(* this)   //使用了上面的函数
             return old;
        }

         bool  operator==( const const_iterator &rhs)  const
        {
             return current == rhs.current;
        }
         bool  operator!=( const const_iterator &rhs)  const
        {
             return !(* this == rhs);
        }




     protected:
        Node *current;
        Object &retrieve()  const
        {
             return current->data;
        }
        const_iterator(Node *p): current(p)
        { }
         friend  class List<Object>;

    };

     class iterator:  public const_iterator
    {
     public:
        iterator()
        {}
        Object & operator*()
        {
             return retrieve();
        }
         const Object & operator*()  const
        {
             return const_iterator:: operator*();
        }
        iterator & operator++()
        {
            current = current->next;
             return * this;
        }

        iterator  operator++( int)
        {
            iterator old = * this;
            ++(* this);
             return old;
        }
     protected:
        iterator(Node *p): const_iterator(p)
        {}
         friend  class List<Object>;

    };
public:
    List()
    {
        init();
    }
    List( const List &rhs)
    {
        init();
        * this = rhs;
    }
    ~List()
    {
        clear();
         delete head;
         delete tail;
    }
     const List & operator=( const List &rhs)
    {
         if( this == &rhs)
             return * this;
        clear();
         for(const_iterator itr = rhs.begin(); itr != rhs.end(); ++it)
        {
            push_back(*itr);
        }
         return * this;
    }

    iterator begin()
    {
         return iterator(head->next);
    }
    const_iterator begin()  const
    {
         return const_iterator(head->next);
    }
    iterator end()
    {
         return iterator(tail);
    }
    const_iterator end()  const
    {
         return const_iterator(tail);
    }
     int size()  const
    {
         return theSize;
    }
     bool empty()  const
    {
         return size() ==  0;
    }
     void clear()
    {
         while( != empty())
        {
            pop_front();
        }
    }

    Object &front()
    {
         return *begin();
    }
     const Object &front()  const
    {
         return *begin();
    }
    Object &back()
    {
         return *--end();
    }
     const Object &back()  const
    {
         return *--end();
    }
     void push_front( const Object &x)
    {
        insert(begin(), x);
    }
     void push_back( const Object &x)
    {
        insert(end(), x);
    }
     void pop_front()
    {
        erase(begin(), x);
    }
     void pop_back()
    {
        erase(--end());
    }

    iterator insert(iterator itr,  const Object &x)
    {
        Node *p = itr.current;
        theSize++;
         return iterator(p->prv = p->prev->next =  new Node(x, p->prev, p));
    }
    iterator erase(iterator itr)
    {
        Node *p = itr.current;
        iterator retVal(p->next);
        p->prev->next = p->next;

        p->next->prev = p->prev;
         delete p;
        theSize--;

         return retVal;
    }
    iterator erase(iterator itr, iterator end)
    {
         for(iterator = itr; itr != end;)
        {
            itr = erase(itr);
        }
         return itr;
    }
private:
     int theSize;
    Node *head;
    Node *tail;

     void init()
    {
        theSize =  0;
        head =  new Node;
        tail =  new Node;
        head->next = tail;
        tail->prev = head;
    }

};



下面我们来分析一下list实现的一些巧妙的地方:

首先要实现const_iterator 这个内部类。其中特别要注意的是itr++和++itr的实现,需要对不同形式分别编写例程。它们拥有相同的名字,因此必须用不通的符号来区分。C++需要通过给前缀形式指定空参数表,给后缀形式指定一个匿名的int参数来赋予前缀和后缀形式以不同的标识。然后++itr调用零参数的operator++,而itr++调用单参数operator++。这个int参数永远也不使用,其存在的意义仅仅在于给出一个不同的标识。

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public:
class const_iterator
{
public:
    const_iterator(): current( NULL)
    { }
     const Object & operator*()  const  //
    {
         return retrieve();
    }
    const_iterator &operator++() //这个表示++i
    {
        current = current->next;
        return *this;
    }
    const_iterator &operator++(int//这个表示i++
    {
        const_iterator old = *this;
        ++(*this)  //使用了上面的函数
        return old;
    }


     bool  operator==( const const_iterator &rhs)  const
    {
         return current == rhs.current;
    }
     bool  operator!=( const const_iterator &rhs)  const
    {
         return !(* this == rhs);
    }




protected:
    Node *current;
    Object &retrieve()  const
    {
         return current->data;
    }
    const_iterator(Node *p): current(p)
    { }
     friend  class List<Object>;

};

     下图是构造函数和三大函数,因为零参数构造函数和复制函数两者都必须分配表头节点和尾节点,我们给出了一个私有的init例程,init生成一个空List。析构函数回收表头节点和尾节点,所有的其他节点在析构调用clear时回收,相似的,operator=是通过调用公有方法实现,而没有试图使用低级指针操作。

   

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
List()  //构造函数
{
    init();
}
List( const List &rhs)   //复制构造函数
{
    init();
    * this = rhs;   //调用=实现。
}
~List()    //析构函数
{
    clear();
     delete head;
     delete tail;
}
const List & operator=( const List &rhs)   //operator函数
{
     if( this == &rhs)
         return * this;
    clear();
     for(const_iterator itr = rhs.begin(); itr != rhs.end(); ++it)
    {
        push_back(*itr);
    }
     return * this;
}
void init()   //初始化
{
    theSize =  0;
    head =  new Node;
    tail =  new Node;
    head->next = tail;
    tail->prev = head;
}

     最后的插入,删除也是一般的链表操作,在这里我们就不仔细讲解了。

     栈和队列在STL采用stack和queue。

    

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值