-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbtree.h
221 lines (196 loc) · 7.62 KB
/
btree.h
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
/**
* The btree is a linked structure which operates much like
* a binary search tree, save the fact that multiple client
* elements are stored in a single node. Whereas a single element
* would partition the tree into two ordered subtrees, a node
* that stores m client elements partition the tree
* into m + 1 sorted subtrees.
*/
#ifndef BTREE_H
#define BTREE_H
#include <iostream>
#include <cstddef>
#include <utility>
#include <vector>
// we better include the iterator
#include "btree_iterator.h"
#include <iterator>
// we do this to avoid compiler errors about non-template friends
// what do we do, remember? :)
template <class> class btree;
template<typename T>
std::ostream& operator<<(std::ostream& os, const btree<T>& tree);
template <typename T>
class btree {
public:
friend class btree_iterator<T>;
friend class const_btree_iterator<T>;
typedef btree_iterator<T> iterator;
typedef const_btree_iterator<T> const_iterator;
/** Hmm, need some iterator typedefs here... friends? **/
/**
* Constructs an empty btree. Note that
* the elements stored in your btree must
* have a well-defined zero-arg constructor,
* copy constructor, operator=, and destructor.
* The elements must also know how to order themselves
* relative to each other by implementing operator<
* and operator==. (These are already implemented on
* behalf of all built-ins: ints, doubles, strings, etc.)
*
* @param maxNodeElems the maximum number of elements
* that can be stored in each B-Tree node
*/
btree(size_t maxNodeElems = 40);
/**
* The copy constructor and assignment operator.
* They allow us to pass around B-Trees by value.
* Although these operations are likely to be expensive
* they make for an interesting programming exercise.
* Implement these operations using value semantics and
* make sure they do not leak memory.
*/
/**
* Copy constructor
* Creates a new B-Tree as a copy of original.
*
* @param original a const lvalue reference to a B-Tree object
*/
btree(const btree<T>& original);
/**
* Move constructor
* Creates a new B-Tree by "stealing" from original.
*
* @param original an rvalue reference to a B-Tree object
*/
btree(btree<T>&& original);
/**
* Copy assignment
* Replaces the contents of this object with a copy of rhs.
*
* @param rhs a const lvalue reference to a B-Tree object
*/
btree<T>& operator=(const btree<T>& rhs);
/**
* Move assignment
* Replaces the contents of this object with the "stolen"
* contents of original.
*
* @param rhs a const reference to a B-Tree object
*/
btree<T>& operator=(btree<T>&& rhs);
/**
* Puts a breadth-first traversal of the B-Tree onto the output
* stream os. Elements must, in turn, support the output operator.
* Elements are separated by space. Should not output any newlines.
*
* @param os a reference to a C++ output stream
* @param tree a const reference to a B-Tree object
* @return a reference to os
*/
friend std::ostream& operator<< <T> (std::ostream& os, const btree<T>& tree);
iterator begin(){ return iterator(this);}
iterator end(){ return iterator(this,_maxNodeElems);}
const_iterator begin() const{ return const_iterator(this);}
const_iterator end() const{ return const_iterator(this, _maxNodeElems);}
reverse_iterator<btree_iterator<T>> rbegin(){
reverse_iterator<btree_iterator<T>> temp(begin());
return temp;
}
reverse_iterator<btree_iterator<T>> rend(){
reverse_iterator<btree_iterator<T>> temp(end());
return temp;
}
const_iterator cbegin(){ return const_iterator(this);}
const_iterator cend() const{ return const_iterator(this, _maxNodeElems);}
reverse_iterator<const_btree_iterator<T>> crbegin(){
reverse_iterator<const_btree_iterator<T>> temp(cbegin());
return temp;
}
reverse_iterator<const_btree_iterator<T>> crend(){
reverse_iterator<const_btree_iterator<T>> temp(cend());
return temp;
}
/**
* Returns an iterator to the matching element, or whatever
* the non-const end() returns if the element could
* not be found.
*
* @param elem the client element we are trying to match. The elem,
* if an instance of a true class, relies on the operator< and
* and operator== methods to compare elem to elements already
* in the btree. You must ensure that your class implements
* these things, else code making use of btree<T>::find will
* not compile.
* @return an iterator to the matching element, or whatever the
* non-const end() returns if no such match was ever found.
*/
iterator find(const T& elem);
/**
* Identical in functionality to the non-const version of find,
* save the fact that what's pointed to by the returned iterator
* is deemed as const and immutable.
*
* @param elem the client element we are trying to match.
* @return an iterator to the matching element, or whatever the
* const end() returns if no such match was ever found.
*/
const_iterator find(const T& elem) const;
/**
* Operation which inserts the specified element
* into the btree if a matching element isn't already
* present. In the event where the element truly needs
* to be inserted, the size of the btree is effectively
* increases by one, and the pair that gets returned contains
* an iterator to the inserted element and true in its first and
* second fields.
*
* If a matching element already exists in the btree, nothing
* is added at all, and the size of the btree stays the same. The
* returned pair still returns an iterator to the matching element, but
* the second field of the returned pair will store false. This
* second value can be checked to after an insertion to decide whether
* or not the btree got bigger.
*
* The insert method makes use of T's zero-arg constructor and
* operator= method, and if these things aren't available,
* then the call to btree<T>::insert will not compile. The implementation
* also makes use of the class's operator== and operator< as well.
*
* @param elem the element to be inserted.
* @return a pair whose first field is an iterator positioned at
* the matching element in the btree, and whose second field
* stores true if and only if the element needed to be added
* because no matching element was there prior to the insert call.
*/
std::pair<iterator, bool> insert(const T& elem);
/**
* Disposes of all internal resources, which includes
* the disposal of any client objects previously
* inserted using the insert operation.
* Check that your implementation does not leak memory!
*/
~btree();
private:
btree(size_t maxNodeElems, btree<T>* parent);
bool isFull(){ return _vec.size() >=_maxNodeElems;}
bool isEmpty(){ return _vec.empty();}
void swap(btree<T>& lhs, btree<T>& rhs);
// The details of your implementation go here
size_t _maxNodeElems;
vector< pair<T,btree<T>*> > _vec;
btree<T>* _parent;
btree<T>* _last;
};
/**
* The template implementation needs to be visible to whatever
* translation unit makes use of templatized btree methods.
* The unconventional practice is to #include the implementation
* file just before the end of the interface (sort of like
* sneaking it in and hoping it isn't noticed). Because the
* .tem file is included here, the .h file is NOT #included in
* the .tem file. We'd otherwise have circular inclusions
* and the compiler would be peeved.
*/
#include "btree.tem"
#endif