Makes list traversal O(N) instead of O(N^2) (by Zhanyong Wan).
This commit is contained in:
parent
aaebfcdc40
commit
1b61f16aef
|
@ -257,7 +257,8 @@ class List {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Creates an empty list.
|
// Creates an empty list.
|
||||||
List() : head_(NULL), last_(NULL), size_(0) {}
|
List() : head_(NULL), last_(NULL), size_(0),
|
||||||
|
last_read_index_(-1), last_read_(NULL) {}
|
||||||
|
|
||||||
// D'tor.
|
// D'tor.
|
||||||
virtual ~List();
|
virtual ~List();
|
||||||
|
@ -276,8 +277,9 @@ class List {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Resets the member variables.
|
// 2. Resets the member variables.
|
||||||
head_ = last_ = NULL;
|
last_read_ = head_ = last_ = NULL;
|
||||||
size_ = 0;
|
size_ = 0;
|
||||||
|
last_read_index_ = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +300,8 @@ class List {
|
||||||
// Adds an element to the end of the list. A copy of the element is
|
// Adds an element to the end of the list. A copy of the element is
|
||||||
// created using the copy constructor, and then stored in the list.
|
// created using the copy constructor, and then stored in the list.
|
||||||
// Changes made to the element in the list doesn't affect the source
|
// Changes made to the element in the list doesn't affect the source
|
||||||
// object, and vice versa.
|
// object, and vice versa. This does not affect the "last read"
|
||||||
|
// index.
|
||||||
void PushBack(const E & element) {
|
void PushBack(const E & element) {
|
||||||
ListNode<E> * new_node = new ListNode<E>(element);
|
ListNode<E> * new_node = new ListNode<E>(element);
|
||||||
|
|
||||||
|
@ -312,7 +315,8 @@ class List {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds an element to the beginning of this list.
|
// Adds an element to the beginning of this list. The "last read"
|
||||||
|
// index is adjusted accordingly.
|
||||||
void PushFront(const E& element) {
|
void PushFront(const E& element) {
|
||||||
ListNode<E>* const new_node = new ListNode<E>(element);
|
ListNode<E>* const new_node = new ListNode<E>(element);
|
||||||
|
|
||||||
|
@ -324,12 +328,18 @@ class List {
|
||||||
head_ = new_node;
|
head_ = new_node;
|
||||||
size_++;
|
size_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (last_read_index_ >= 0) {
|
||||||
|
// A new element at the head bumps up an existing index by 1.
|
||||||
|
last_read_index_++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes an element from the beginning of this list. If the
|
// Removes an element from the beginning of this list. If the
|
||||||
// result argument is not NULL, the removed element is stored in the
|
// result argument is not NULL, the removed element is stored in the
|
||||||
// memory it points to. Otherwise the element is thrown away.
|
// memory it points to. Otherwise the element is thrown away.
|
||||||
// Returns true iff the list wasn't empty before the operation.
|
// Returns true iff the list wasn't empty before the operation. The
|
||||||
|
// "last read" index is adjusted accordingly.
|
||||||
bool PopFront(E* result) {
|
bool PopFront(E* result) {
|
||||||
if (size_ == 0) return false;
|
if (size_ == 0) return false;
|
||||||
|
|
||||||
|
@ -346,13 +356,21 @@ class List {
|
||||||
}
|
}
|
||||||
delete old_head;
|
delete old_head;
|
||||||
|
|
||||||
|
if (last_read_index_ > 0) {
|
||||||
|
last_read_index_--;
|
||||||
|
} else if (last_read_index_ == 0) {
|
||||||
|
last_read_index_ = -1;
|
||||||
|
last_read_ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inserts an element after a given node in the list. It's the
|
// Inserts an element after a given node in the list. It's the
|
||||||
// caller's responsibility to ensure that the given node is in the
|
// caller's responsibility to ensure that the given node is in the
|
||||||
// list. If the given node is NULL, inserts the element at the
|
// list. If the given node is NULL, inserts the element at the
|
||||||
// front of the list.
|
// front of the list. The "last read" index is adjusted
|
||||||
|
// accordingly.
|
||||||
ListNode<E>* InsertAfter(ListNode<E>* node, const E& element) {
|
ListNode<E>* InsertAfter(ListNode<E>* node, const E& element) {
|
||||||
if (node == NULL) {
|
if (node == NULL) {
|
||||||
PushFront(element);
|
PushFront(element);
|
||||||
|
@ -367,6 +385,11 @@ class List {
|
||||||
last_ = new_node;
|
last_ = new_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We aren't sure whether this insertion will affect the last read
|
||||||
|
// index, so we invalidate it to be safe.
|
||||||
|
last_read_index_ = -1;
|
||||||
|
last_read_ = NULL;
|
||||||
|
|
||||||
return new_node;
|
return new_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,20 +454,27 @@ class List {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a pointer to the i-th element of the list, or NULL if i is not
|
// Returns a pointer to the i-th element of the list, or NULL if i is not
|
||||||
// in range [0, size()).
|
// in range [0, size()). The "last read" index is adjusted accordingly.
|
||||||
const E* GetElement(int i) const {
|
const E* GetElement(int i) const {
|
||||||
if (i < 0 || i >= size())
|
if (i < 0 || i >= size())
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
const ListNode<E>* node = Head();
|
if (last_read_index_ < 0 || last_read_index_ > i) {
|
||||||
for (int index = 0; index < i && node != NULL; ++index, node = node->next())
|
// We have to count from the start.
|
||||||
continue;
|
last_read_index_ = 0;
|
||||||
|
last_read_ = Head();
|
||||||
|
}
|
||||||
|
|
||||||
return node ? &(node->element()) : NULL;
|
while (last_read_index_ < i) {
|
||||||
|
last_read_ = last_read_->next();
|
||||||
|
last_read_index_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &(last_read_->element());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the i-th element of the list, or default_value if i is not
|
// Returns the i-th element of the list, or default_value if i is not
|
||||||
// in range [0, size()).
|
// in range [0, size()). The "last read" index is adjusted accordingly.
|
||||||
E GetElementOr(int i, E default_value) const {
|
E GetElementOr(int i, E default_value) const {
|
||||||
const E* element = GetElement(i);
|
const E* element = GetElement(i);
|
||||||
return element ? *element : default_value;
|
return element ? *element : default_value;
|
||||||
|
@ -455,6 +485,15 @@ class List {
|
||||||
ListNode<E>* last_; // The last node of the list.
|
ListNode<E>* last_; // The last node of the list.
|
||||||
int size_; // The number of elements in the list.
|
int size_; // The number of elements in the list.
|
||||||
|
|
||||||
|
// These fields point to the last element read via GetElement(i) or
|
||||||
|
// GetElementOr(i). They are used to speed up list traversal as
|
||||||
|
// often they allow us to find the wanted element by looking from
|
||||||
|
// the last visited one instead of the list head. This means a
|
||||||
|
// sequential traversal of the list can be done in O(N) time instead
|
||||||
|
// of O(N^2).
|
||||||
|
mutable int last_read_index_;
|
||||||
|
mutable const ListNode<E>* last_read_;
|
||||||
|
|
||||||
// We disallow copying List.
|
// We disallow copying List.
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(List);
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(List);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user