init lab 3
This commit is contained in:
		
							parent
							
								
									7b40a4243d
								
							
						
					
					
						commit
						2314cf70aa
					
				
							
								
								
									
										21
									
								
								lab_3/sequences-int/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								lab_3/sequences-int/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| binary = sequence-test | ||||
| folder = sequences-int | ||||
| objects = $(patsubst %.cpp,%.o,$(wildcard *.cpp)) | ||||
| 
 | ||||
| run: $(binary) | ||||
| 	./$(binary) | ||||
| 
 | ||||
| $(binary): $(objects) | ||||
| 	g++ -g3 -o $@ $^ | ||||
| 
 | ||||
| %.o: %.cpp | ||||
| 	g++ -g3 -c -o $@ $< | ||||
| 
 | ||||
| clean: | ||||
| 	rm -f *.o | ||||
| 
 | ||||
| clear: clean | ||||
| 	rm -f *.zip $(binary) | ||||
| 
 | ||||
| zip: clean | ||||
| 	zip $(folder) Makefile *.cpp *.h | ||||
							
								
								
									
										164
									
								
								lab_3/sequences-int/doubly-linked-list.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								lab_3/sequences-int/doubly-linked-list.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,164 @@ | ||||
| #include "doubly-linked-list.h" | ||||
| 
 | ||||
| using namespace seq; | ||||
| 
 | ||||
| // return the size (number of items in the doubly linked list)
 | ||||
| size_t DoublyLinkedList::size() const | ||||
| { | ||||
|    size_t count = 0; | ||||
|    for(DoublyLinkedListNode* n = this->head; n != nullptr; n = n->get_next()) count++; | ||||
|    return count; | ||||
| } | ||||
| 
 | ||||
| // return pointer to the node at position i, counting from 0
 | ||||
| // for negative numbers, count from the tail (-1) backward (-2, -3, ...)
 | ||||
| DoublyLinkedListNode* DoublyLinkedList::index(int i) const | ||||
| { | ||||
|    DoublyLinkedListNode* n = this->head; | ||||
|    if(i > 0) | ||||
|    {       | ||||
|       for(int k = 0; k < i; k++) | ||||
|       { | ||||
|          assert(n != nullptr); | ||||
|          n = n->get_next(); | ||||
|       } | ||||
|    } | ||||
|    else  /** in the doubly linked list, we can walk backward, so let us allow it for negative i **/ | ||||
|    { | ||||
|       DoublyLinkedListNode* n = this->tail; | ||||
|       for(int k = -1; k > i; k--) | ||||
|       { | ||||
|          assert(n != nullptr); | ||||
|          n = n->get_prev(); | ||||
|       } | ||||
|    } | ||||
|    return n; | ||||
| } | ||||
| 
 | ||||
| // add an item at the end of the list
 | ||||
| void DoublyLinkedList::push_back(const int& pushed_item) | ||||
| { | ||||
|    DoublyLinkedListNode* new_node = new DoublyLinkedListNode; | ||||
|    new_node->set_item(pushed_item); | ||||
|     | ||||
|    if(this->empty()) this->head = new_node; | ||||
|    else | ||||
|    { | ||||
|       this->tail->set_next(new_node); | ||||
|       new_node->set_prev(this->tail);  /** attachment in the doubly linked list goes both ways **/ | ||||
|    } | ||||
|    this->tail = new_node; | ||||
| } | ||||
| 
 | ||||
| // add an item at the beginning of the list
 | ||||
| void DoublyLinkedList::push_front(const int& pushed_item) | ||||
| { | ||||
|    DoublyLinkedListNode* new_node = new DoublyLinkedListNode; | ||||
|    new_node->set_item(pushed_item); | ||||
|     | ||||
|    if(this->empty()) this->tail = new_node; | ||||
|    else | ||||
|    { | ||||
|       new_node->set_next(this->head);  // FIX BUG from the previous version, which was "new_node->set_next(this->head->get_next());"
 | ||||
|       this->head->set_prev(new_node);  // FIX BUG from the previous version, which was "this->head->get_next()->set_prev(new_node);"
 | ||||
|    } | ||||
|    this->head = new_node; | ||||
| } | ||||
| 
 | ||||
| // remove the head node and item
 | ||||
| void DoublyLinkedList::pop_front() | ||||
| { | ||||
|    if(this->empty()) return;  // nothing there to remove
 | ||||
|    DoublyLinkedListNode* successor = this->head->get_next(); | ||||
| 
 | ||||
|    if(successor) successor->set_prev(nullptr);  /** we must detach the previous head node from its successor **/    | ||||
|    delete this->head; | ||||
|    this->head = successor;  // successor of the previous head is the new head
 | ||||
|    if(this->head == nullptr) this->tail = nullptr;  // catch special case: the list is now empty
 | ||||
| } | ||||
| 
 | ||||
| // remove the tail node and item
 | ||||
| /** note how this is much easier for the doubly than for the singly linked list;        **
 | ||||
|  ** we can simply take the "pop_front()" implementation and do everything symmetrically **/ | ||||
| void DoublyLinkedList::pop_back() | ||||
| { | ||||
|    if(this->empty()) return;  // nothing there to remove
 | ||||
|    DoublyLinkedListNode* predecessor = this->tail->get_prev(); | ||||
|     | ||||
|    if(predecessor) predecessor->set_next(nullptr); | ||||
|    delete this->tail; | ||||
|    this->tail = predecessor;  /** predecessor of the previous tail is the new tail **/ | ||||
|    if(this->tail == nullptr) this->head = nullptr;  /** catch special case: the list is now empty **/ | ||||
| } | ||||
| 
 | ||||
| // insert an item at index i
 | ||||
| // for negative numbers, count from the tail (-1) backward (-2, -3, ...)
 | ||||
| void DoublyLinkedList::insert_at(int i, const int& inserted_item) | ||||
| { | ||||
|    if(i == 0) | ||||
|    { | ||||
|       this->push_front(inserted_item); | ||||
|       return; | ||||
|    } | ||||
|    else if(i == -1) | ||||
|    { | ||||
|       this->push_back(inserted_item); | ||||
|       return; | ||||
|    } | ||||
|    DoublyLinkedListNode* predecessor = this->index(i-1); | ||||
|    this->insert_successor_to(predecessor, inserted_item); | ||||
| } | ||||
| 
 | ||||
| // remove the item at index i
 | ||||
| // for negative numbers, count from the tail (-1) backward (-2, -3, ...)
 | ||||
| void DoublyLinkedList::erase_at(int i) | ||||
| { | ||||
|    if(i == 0) | ||||
|    { | ||||
|       this->pop_front(); | ||||
|       return; | ||||
|    } | ||||
|    else if(i == -1) | ||||
|    { | ||||
|       this->pop_back(); | ||||
|       return; | ||||
|    } | ||||
|    DoublyLinkedListNode* erased_node = this->index(i); | ||||
|    this->erase_node(erased_node); | ||||
| } | ||||
| 
 | ||||
| // insert an item after given node
 | ||||
| void DoublyLinkedList::insert_successor_to(DoublyLinkedListNode* predecessor, const int& inserted_item) | ||||
| { | ||||
|    DoublyLinkedListNode* new_node =  new DoublyLinkedListNode; | ||||
|    new_node->set_item(inserted_item); | ||||
|     | ||||
|    DoublyLinkedListNode* successor = predecessor->get_next(); | ||||
|    predecessor->set_next(new_node); | ||||
|    new_node->set_prev(predecessor); /** attach both ways **/ | ||||
|    new_node->set_next(successor); | ||||
|     | ||||
|    if(!successor) this->tail = new_node; | ||||
|    else successor->set_prev(new_node);  /** attach both ways **/ | ||||
| } | ||||
| 
 | ||||
| /** remove "erased_node" from the doubly linked list **/ | ||||
| void DoublyLinkedList::erase_node(DoublyLinkedListNode* erased_node) | ||||
| { | ||||
|    if(erased_node->get_prev() == nullptr)  /** erase the head **/ | ||||
|    { | ||||
|       this->pop_front(); | ||||
|       return; | ||||
|    } | ||||
|    if(erased_node->get_next() == nullptr)  /** erase the tail **/ | ||||
|    { | ||||
|       this->pop_back(); | ||||
|       return; | ||||
|    } | ||||
|     | ||||
|    /** now attach predecessor and successor to each other **/ | ||||
|    erased_node->get_prev()->set_next(erased_node->get_next()); | ||||
|    erased_node->get_next()->set_prev(erased_node->get_prev()); | ||||
| 
 | ||||
|    delete erased_node;  /** "erased_node" detached now, we can delete it **/ | ||||
| } | ||||
							
								
								
									
										104
									
								
								lab_3/sequences-int/doubly-linked-list.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								lab_3/sequences-int/doubly-linked-list.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | ||||
| #ifndef DOUBLY_LINKED_LIST_H | ||||
| #define DOUBLY_LINKED_LIST_H | ||||
| 
 | ||||
| #include <cassert> | ||||
| #include "sequence.h" | ||||
| 
 | ||||
| namespace seq | ||||
| { | ||||
|    class DoublyLinkedListNode | ||||
|    { | ||||
|    public: | ||||
|       // return a reference to the stored item
 | ||||
|       int& get_item() { return this->item; } | ||||
| 
 | ||||
|       // return pointer to next node, or nullptr if this is the final node
 | ||||
|       DoublyLinkedListNode* get_next() const { return this->next; } | ||||
| 
 | ||||
|       // return pointer to previous node, or nullptr if this is the initial node
 | ||||
|       DoublyLinkedListNode* get_prev() const { return this->prev; } | ||||
| 
 | ||||
|       // overwrite the item stored in this node
 | ||||
|       void set_item(int in_item) { this->item = in_item; } | ||||
| 
 | ||||
|    private: | ||||
|       int item = 0; | ||||
|       DoublyLinkedListNode* next = nullptr; | ||||
|       DoublyLinkedListNode* prev = nullptr; | ||||
| 
 | ||||
|       // attach a node behind this node
 | ||||
|       // if there was a node attached to this previously, it is NOT deleted!
 | ||||
|       void set_next(DoublyLinkedListNode* in_next) { this->next = in_next; } | ||||
| 
 | ||||
|       // attach a node before this node
 | ||||
|       // if there was a node attached to this previously, it is NOT deleted!
 | ||||
|       void set_prev(DoublyLinkedListNode* in_prev) { this->prev = in_prev; } | ||||
|        | ||||
|       friend class DoublyLinkedList;  // allow DoublyLinkedList to access private members
 | ||||
|    }; | ||||
|     | ||||
|    class DoublyLinkedList: public Sequence | ||||
|    { | ||||
|    public: | ||||
|       bool empty() const { return (this->head == nullptr); }  // test whether the doubly linked list is empty
 | ||||
|       size_t size() const;  // return the size (number of items in the doubly linked list)
 | ||||
| 
 | ||||
|       // it is the caller's responsibility to ensure that the list is not empty when calling front() or back()!
 | ||||
|       int& front() { assert(this->head); return this->head->get_item(); }  // return a reference to the first item
 | ||||
|       int& back()  { assert(this->tail); return this->tail->get_item(); }  // return a reference to the final item
 | ||||
| 
 | ||||
|       // return a reference to the item at position i of the list, counting from 0
 | ||||
|       // for negative numbers, count from the tail (-1) backward (-2, -3, ...)
 | ||||
|       // it is the caller's responsibility that the index is within range
 | ||||
|       int& at(int i) { return this->index(i)->get_item(); } | ||||
|        | ||||
|       // return pointer to the head/tail node
 | ||||
|       DoublyLinkedListNode* begin() const { return this->head; } | ||||
|       DoublyLinkedListNode* end() const { return this->tail; } | ||||
|        | ||||
|       // return pointer to the node at position i, counting from 0
 | ||||
|       // for negative numbers, count from the tail (-1) backward (-2, -3, ...)
 | ||||
|       DoublyLinkedListNode* index(int i) const; | ||||
| 
 | ||||
|       /*
 | ||||
|        * accepts an additional item into the doubly linked list; | ||||
|        * by default, this is done at the back end of the list | ||||
|        * call push_front(...) to push an element at the front | ||||
|        *  | ||||
|        * the list takes ownership of the copy (but not of the original!) | ||||
|        */ | ||||
|       void push(const int& pushed_item) { this->push_back(pushed_item); } | ||||
|       void push_back(const int& pushed_item); | ||||
|       void push_front(const int& pushed_item); | ||||
| 
 | ||||
|       /*
 | ||||
|        * removes an item from the list (front end by default) | ||||
|        * to do the same at the back, call pop_back() | ||||
|        */ | ||||
|       void pop() { this->pop_front(); } | ||||
|       void pop_front(); | ||||
|       void pop_back(); | ||||
|       void clear() { while(!this->empty()) this->pop(); }  // remove all the items from the list
 | ||||
| 
 | ||||
|       // it is the caller's responsibility that the index is within range
 | ||||
|       // for negative numbers, count from the tail (-1) backward (-2, -3, ...)
 | ||||
|       void insert_at(int i, const int& inserted_item);  // insert an item at index i
 | ||||
|       void erase_at(int i);  // remove the item at index i
 | ||||
| 
 | ||||
|       // it is the caller's responsibility that the node is actually part of the list
 | ||||
|       void insert_successor_to(DoublyLinkedListNode* predecessor, const int& inserted_item);  // insert an item after given node
 | ||||
|       void erase_successor_to(DoublyLinkedListNode* predecessor)  // remove the item after given node
 | ||||
|       { | ||||
|          this->erase_node(predecessor->get_next()); | ||||
|       } | ||||
|       void erase_node(DoublyLinkedListNode* erased_node); | ||||
|        | ||||
|       ~DoublyLinkedList() { this->clear(); } | ||||
| 
 | ||||
|    private: | ||||
|       DoublyLinkedListNode* head = nullptr; | ||||
|       DoublyLinkedListNode* tail = nullptr; | ||||
|    }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										95
									
								
								lab_3/sequences-int/dynamic-array.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								lab_3/sequences-int/dynamic-array.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| #include <algorithm> | ||||
| #include <cassert> | ||||
| 
 | ||||
| #include "dynamic-array.h" | ||||
| 
 | ||||
| using namespace seq; | ||||
| 
 | ||||
| // remove all the items from the array
 | ||||
| void DynamicArray::clear() | ||||
| { | ||||
|    if(this->values) delete this->values; | ||||
|    this->values = nullptr; | ||||
|    this->logical_size = 0; | ||||
|    this->capacity = 0; | ||||
| } | ||||
| 
 | ||||
| // insert an item at index i
 | ||||
| void DynamicArray::insert_at(int i, const int& inserted_item) | ||||
| { | ||||
|    assert((i >= 0) && (this->logical_size >= i)); | ||||
|     | ||||
|    // catch the case where capacity is exhausted and we need to allocate more memory
 | ||||
|    if(this->logical_size == this->capacity) | ||||
|    { | ||||
|       size_t new_capacity = 2 * this->capacity; | ||||
|       if(new_capacity == 0) new_capacity = 1; | ||||
|       this->resize(new_capacity); | ||||
|    } | ||||
|    assert(this->capacity > this->logical_size); | ||||
|     | ||||
|    // shift all elements from index i onward one to the right
 | ||||
|    // we use a temporary storage and copy() from <algorithm> to do this efficiently
 | ||||
|    size_t shifted_elements = this->logical_size - i; | ||||
|    if(shifted_elements > 0) | ||||
|    { | ||||
|       int* temp_storage = new int[shifted_elements](); | ||||
|        | ||||
|       // copy values[i] to values[i+shifted_elements-1] into temp_storage
 | ||||
|       std::copy(this->values + i, this->values + i + shifted_elements, temp_storage); | ||||
|        | ||||
|       // copy all of temp_storage into values[i+1] to values[logical_size]
 | ||||
|       std::copy(temp_storage, temp_storage + shifted_elements, &this->values[i+1]); | ||||
|       delete temp_storage; | ||||
|    } | ||||
|     | ||||
|    // now we can write the inserted item into values[i]
 | ||||
|    this->logical_size++; | ||||
|    this->values[i] = inserted_item; | ||||
| } | ||||
| 
 | ||||
| // remove the item at index i
 | ||||
| void DynamicArray::erase_at(int i) | ||||
| { | ||||
|    assert((i >= 0) && this->logical_size > i); | ||||
|     | ||||
|    // shift all elements from index i+1 onward one to the left
 | ||||
|    // we use a temporary storage and copy() from <algorithm> to do this efficiently
 | ||||
|    size_t shifted_elements = this->logical_size - i - 1; | ||||
|    if(shifted_elements > 0) | ||||
|    { | ||||
|       int* temp_storage = new int[shifted_elements](); | ||||
|        | ||||
|       // copy values[i+1] to values[logical_size-1] into temp_storage
 | ||||
|       std::copy(this->values + i+1, this->values + this->logical_size, temp_storage); | ||||
|        | ||||
|       // copy all of temp_storage into values[i] to values[logical_size-2]
 | ||||
|       std::copy(temp_storage, temp_storage + shifted_elements, &this->values[i]); | ||||
|       delete temp_storage; | ||||
|    } | ||||
|    this->logical_size--;  // with this, we are done with the task
 | ||||
|     | ||||
|    // now let us see whether we have deleted so many items that we should resize to save memory
 | ||||
|    if(this->capacity/2 >= this->logical_size) this->resize(this->capacity/2); | ||||
| } | ||||
| 
 | ||||
| // allocate static array with "new_capacity" elements,
 | ||||
| // copy the contents there, and delete the previous static array
 | ||||
| void DynamicArray::resize(size_t new_capacity) | ||||
| { | ||||
|    assert(new_capacity >= this->logical_size); | ||||
|    int* allocated_memory = new int[new_capacity](); | ||||
|     | ||||
|    if(this->values != nullptr) | ||||
|    { | ||||
|       // use <algorithm> library construct for efficient memory-level copying
 | ||||
|       // take values[0] to values[logical-size-1] and copy it into allocated_memory
 | ||||
|       std::copy(this->values, this->values + this->logical_size, allocated_memory); | ||||
| 
 | ||||
|       delete this->values; // now we can delete the old storage
 | ||||
|    } | ||||
| 
 | ||||
|    // update class properties
 | ||||
|    this->values = allocated_memory; | ||||
|    this->capacity = new_capacity; | ||||
| } | ||||
							
								
								
									
										58
									
								
								lab_3/sequences-int/dynamic-array.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								lab_3/sequences-int/dynamic-array.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | ||||
| #include "sequence.h" | ||||
| 
 | ||||
| namespace seq | ||||
| { | ||||
|    class DynamicArray: public Sequence | ||||
|    { | ||||
|    public: | ||||
|       bool empty() const { return (this->logical_size == 0); }   // test whether the array is empty
 | ||||
|       size_t size() const { return this->logical_size; }  // return the logical size (number of items in the array)
 | ||||
| 
 | ||||
|       // it is the caller's responsibility to ensure that the array is not empty!
 | ||||
|       int& front() { return this->values[0]; }  // return a reference to the first item
 | ||||
|       int& back() { return this->values[this->logical_size - 1]; }   // return a reference to the final item
 | ||||
| 
 | ||||
|       // return a reference to the item at position i of the sequence, counting from 0
 | ||||
|       // we use modulo arithmetics to avoid over-/underflow; will still fail for an empty array
 | ||||
|       int& at(int i) { return this->values[i % this->logical_size]; } | ||||
|        | ||||
|       /*
 | ||||
|        * accepts an additional item into the dynamic array; | ||||
|        * by default, this is done at the back end of the array | ||||
|        * call push_front(...) to push an element at the front | ||||
|        *  | ||||
|        * the array takes ownership of the copy (but not of the original!) | ||||
|        */ | ||||
|       void push(const int& pushed_item) { this->push_back(pushed_item); } | ||||
|       void push_back(const int& pushed_item) { this->insert_at(this->logical_size, pushed_item); } | ||||
|       void push_front(const int& pushed_item) { this->insert_at(0, pushed_item); } | ||||
| 
 | ||||
|       /*
 | ||||
|        * removes an item from the list (back end by default) | ||||
|        * to do the same at the front, call pop_front() | ||||
|        */ | ||||
|       void pop() { this->pop_back(); } | ||||
|       void pop_front() { this->erase_at(0); } | ||||
|       void pop_back() { this->erase_at(this->logical_size - 1); } | ||||
|       void clear();  // remove all the items from the array
 | ||||
|        | ||||
|       void insert_at(int i, const int& inserted_item);  // insert an item at index i
 | ||||
|       void erase_at(int i);  // remove the item at index i
 | ||||
|        | ||||
|       // overwrite the element at index i
 | ||||
|       // we use modulo arithmetics to avoid over-/underflow; will still fail for an empty array
 | ||||
|       void set_value_at(int i, const int& in_item) { this->values[i % this->logical_size] = in_item; } | ||||
|        | ||||
|       ~DynamicArray() { this->clear(); } | ||||
| 
 | ||||
|    private: | ||||
|       // this is a static C/C++ array containing the actual data
 | ||||
|       // it is owned by the dynamic array
 | ||||
|       int* values = nullptr; | ||||
|        | ||||
|       size_t logical_size = 0;  // how many data items are we actually storing?
 | ||||
|       size_t capacity = 0;  // how much memory did we allocate?
 | ||||
|        | ||||
|       void resize(size_t new_capacity);  // shift to static array with increased/decreased capacity
 | ||||
|    }; | ||||
| } | ||||
							
								
								
									
										101
									
								
								lab_3/sequences-int/sequence-test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								lab_3/sequences-int/sequence-test.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,101 @@ | ||||
| #include <cassert> | ||||
| #include <chrono> | ||||
| #include <iostream> | ||||
| 
 | ||||
| #include "dynamic-array.h" | ||||
| #include "singly-linked-list.h" | ||||
| #include "doubly-linked-list.h" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|    /*
 | ||||
|     * run a simple test | ||||
|     */ | ||||
|    void test_sequence(seq::Sequence* sqn, int n, int m, std::ostream* os) | ||||
|    { | ||||
|       assert((m > 0) && (n > m)); | ||||
|       if(os) *os << "Push even numbers from 0 to " << 2*(n-1) << ".\n"; | ||||
|       for(int i = 0; i < n; i++) sqn->push(2*i); | ||||
|        | ||||
|       if(os) *os << "Overwrite element at index " << m/2 << " with 0.\n"; | ||||
|       int& item = sqn->at(m/2); | ||||
|       item = 0; | ||||
|        | ||||
|       if(os) *os << "\nSize of sequence: " << sqn->size() << ".\n"; | ||||
|       if(os) *os << "Detaching " << m/2 << " elements from front:"; | ||||
|       for(int i = 0; i < m/2; i++) | ||||
|       { | ||||
|          if(os) *os << " " << sqn->front(); | ||||
|          sqn->pop_front(); | ||||
|       } | ||||
|       if(os) *os << ".\nDetaching " << m/2 << " elements from back:"; | ||||
|       for(int i = 0; i < m/2; i++) | ||||
|       { | ||||
|          if(os) *os << " " << sqn->back(); | ||||
|          sqn->pop_back(); | ||||
|       } | ||||
|       if(os) *os << ".\nSize of sequence: " << sqn->size() << ".\n"; | ||||
|        | ||||
|       if(os) *os << "\nElement at index " << (n-m)/3 << ": " << sqn->at((n-m)/3) << ".\n"; | ||||
|       if(os) *os << "Element at index " << 2*(n-m)/3 << ": " << sqn->at(2*(n-m)/3) << ".\n"; | ||||
|        | ||||
|       if(os) *os << "\nInsert " << m << " at index " << 2*(n-m)/3 << ".\n"; | ||||
|       sqn->insert_at(2*(n-m)/3, m); | ||||
|       if(os) *os << "Size of sequence: " << sqn->size() << ".\n"; | ||||
|        | ||||
|       if(os) *os << "\nDelete element at index " << (n-m)/3 << ".\n"; | ||||
|       sqn->erase_at((n-m)/3); | ||||
|       if(os) *os << "Size of sequence: " << sqn->size() << ".\n"; | ||||
|       if(os) *os << "Element at index " << 2*(n-m)/3 - 1 << ": " << sqn->at(2*(n-m)/3 - 1) << ".\n"; | ||||
|        | ||||
|       if(os) *os << "\nClearing.\n"; | ||||
|       sqn->clear(); | ||||
|       if(os) *os << "Size of sequence: " << sqn->size() << ".\n"; | ||||
|    } | ||||
|     | ||||
|    /*
 | ||||
|     * return time measurement in units of seconds | ||||
|     */ | ||||
|    float test_with_time_measurement(seq::Sequence* sqn, int iterations) | ||||
|    { | ||||
|       int sequence_length = 200001; | ||||
|       int deletions = 10; | ||||
|       test_sequence(sqn, sequence_length, deletions, &std::cout); | ||||
| 
 | ||||
|       int log_entries = 10; | ||||
|       std::cout << "\nNow repeat the above " << iterations << " times:\n"; | ||||
|       auto t0 = std::chrono::high_resolution_clock::now(); | ||||
|       for(int i = 0; i < iterations; i++) | ||||
|       { | ||||
|          test_sequence(sqn, sequence_length, deletions, nullptr); | ||||
|          if((i+1) % (iterations/log_entries) == 0) | ||||
|          { | ||||
|             std::cout << "\t" << i+1 << "\n"; | ||||
|             std::cout.flush();  // make sure that status output is shown without delay
 | ||||
|          } | ||||
|       } | ||||
|       auto t1 = std::chrono::high_resolution_clock::now(); | ||||
|       return 1.0e-06 * std::chrono::duration_cast<std::chrono::microseconds>(t1-t0).count(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| int main() | ||||
| { | ||||
|    int iterations = 200; | ||||
|     | ||||
|    std::cout << "*** test with dynamic array ***\n"; | ||||
|    seq::DynamicArray dyna; | ||||
|    float dyna_time = test_with_time_measurement(&dyna, iterations); | ||||
|     | ||||
|    std::cout << "\n\n*** test with singly linked list ***\n"; | ||||
|    seq::SinglyLinkedList sll; | ||||
|    float sll_time = test_with_time_measurement(&sll, iterations); | ||||
|     | ||||
|    std::cout << "\n\n*** test with doubly linked list ***\n"; | ||||
|    seq::DoublyLinkedList dll; | ||||
|    float dll_time = test_with_time_measurement(&dll, iterations); | ||||
|     | ||||
|    std::cout << "\n\nRuntime for dynamic array:\t" << dyna_time << " s\n"; | ||||
|    std::cout << "Runtime for singly linked list:\t" << sll_time << " s\n"; | ||||
|    std::cout << "Runtime for doubly linked list:\t" << dll_time << " s\n"; | ||||
| } | ||||
							
								
								
									
										42
									
								
								lab_3/sequences-int/sequence.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								lab_3/sequences-int/sequence.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| #ifndef SEQUENCE_H | ||||
| #define SEQUENCE_H | ||||
| 
 | ||||
| #include <iostream> | ||||
| 
 | ||||
| namespace seq | ||||
| { | ||||
|    class Sequence | ||||
|    { | ||||
|    public: | ||||
|       virtual bool empty() const = 0;   // test whether the sequence is empty
 | ||||
|       virtual size_t size() const = 0;  // return the size (number of items in the sequence)
 | ||||
|        | ||||
|       // it is the caller's responsibility to ensure that the sequence is not empty when calling front() or back()!
 | ||||
|       virtual int& front() = 0;  // return a reference to the first item
 | ||||
|       virtual int& back() = 0;   // return a reference to the final item
 | ||||
|        | ||||
|       // return a reference to the item at position i of the sequence, counting from 0
 | ||||
|       // it is the caller's responsibility that the index is within range
 | ||||
|       virtual int& at(int i) = 0; | ||||
| 
 | ||||
|       /*
 | ||||
|        * accepts an additional item into the sequence (front or back); | ||||
|        * the pushed item is passed by reference, but a copy of it is stored in the Sequence | ||||
|        * the Sequence takes ownership of the copy (but not of the original!) | ||||
|        */ | ||||
|       virtual void push(const int& pushed_item) = 0;  // default push operation (front or back)
 | ||||
|       virtual void push_front(const int& pushed_item) = 0; | ||||
|       virtual void push_back(const int& pushed_item) = 0; | ||||
| 
 | ||||
|       virtual void pop() = 0;  // default pop operation (front or back)
 | ||||
|       virtual void pop_front() = 0;  // remove the first element
 | ||||
|       virtual void pop_back() = 0;   // remove the last element
 | ||||
|       virtual void clear() = 0;  // remove all the items from the sequence
 | ||||
| 
 | ||||
|       // it is the caller's responsibility that the index is within range
 | ||||
|       virtual void insert_at(int i, const int& inserted_item) = 0;  // insert an item at index i
 | ||||
|       virtual void erase_at(int i) = 0;  // remove the item at index i
 | ||||
|    }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										140
									
								
								lab_3/sequences-int/singly-linked-list.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								lab_3/sequences-int/singly-linked-list.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,140 @@ | ||||
| #include "singly-linked-list.h" | ||||
| 
 | ||||
| using namespace seq; | ||||
| 
 | ||||
| // return the size (number of items in the singly linked list)
 | ||||
| size_t SinglyLinkedList::size() const | ||||
| { | ||||
|    size_t count = 0; | ||||
|    for(SinglyLinkedListNode* n = this->head; n != nullptr; n = n->get_next()) count++; | ||||
|    return count; | ||||
| } | ||||
| 
 | ||||
| // return pointer to the node at position i, counting from 0
 | ||||
| SinglyLinkedListNode* SinglyLinkedList::index(int i) const | ||||
| { | ||||
|    SinglyLinkedListNode* n = this->head; | ||||
|    for(int k = 0; k < i; k++) | ||||
|    { | ||||
|       assert(n != nullptr); | ||||
|       n = n->get_next(); | ||||
|    } | ||||
|    return n; | ||||
| } | ||||
| 
 | ||||
| // add an item at the end of the list
 | ||||
| void SinglyLinkedList::push_back(const int& pushed_item) | ||||
| { | ||||
|    SinglyLinkedListNode* new_node = new SinglyLinkedListNode; | ||||
|    new_node->set_item(pushed_item); | ||||
|     | ||||
|    if(this->empty()) this->head = new_node; | ||||
|    else this->tail->set_next(new_node); | ||||
|    this->tail = new_node; | ||||
| } | ||||
| 
 | ||||
| // add an item at the beginning of the list
 | ||||
| void SinglyLinkedList::push_front(const int& pushed_item) | ||||
| { | ||||
|    SinglyLinkedListNode* new_node = new SinglyLinkedListNode; | ||||
|    new_node->set_item(pushed_item); | ||||
|     | ||||
|    if(this->empty()) this->tail = new_node; | ||||
|    else new_node->set_next(this->head);  // FIG BUG from previous version, which was "else new_node->set_next(this->head->get_next());"
 | ||||
|    this->head = new_node; | ||||
| } | ||||
| 
 | ||||
| // remove the head node and item
 | ||||
| void SinglyLinkedList::pop_front() | ||||
| { | ||||
|    if(this->empty()) return;  // nothing there to remove
 | ||||
|    SinglyLinkedListNode* successor = this->head->get_next(); | ||||
|     | ||||
|    delete this->head; | ||||
|    this->head = successor;  // successor of the previous head is the new head
 | ||||
|    if(this->head == nullptr) this->tail = nullptr;  // catch special case: the list is now empty
 | ||||
| } | ||||
| 
 | ||||
| // remove the tail node and item
 | ||||
| // note: in a singly linked list, we need to walk step by step to the end!
 | ||||
| void SinglyLinkedList::pop_back() | ||||
| { | ||||
|    if(this->empty()) return;  // nothing there to remove
 | ||||
|     | ||||
|    // list only contains one element
 | ||||
|    if(this->head->get_next() == nullptr) | ||||
|    { | ||||
|       delete this->head; | ||||
|       this->head = nullptr; | ||||
|       this->tail = nullptr; | ||||
|       return; | ||||
|    } | ||||
|     | ||||
|    /*
 | ||||
|     * walk through until "third" is nullptr, second is the last element, and first is the second-to-last | ||||
|     */ | ||||
|    SinglyLinkedListNode* first = this->head; | ||||
|    SinglyLinkedListNode* second = first->get_next(); | ||||
|    SinglyLinkedListNode* third = second->get_next(); | ||||
|    while(third != nullptr) | ||||
|    { | ||||
|       first = second; | ||||
|       second = third; | ||||
|       third = third->get_next(); | ||||
|    } | ||||
|     | ||||
|    /*
 | ||||
|     * remove "second" | ||||
|     */ | ||||
|    if(this->tail == second) this->tail = first; | ||||
|    first->set_next(nullptr); | ||||
|    delete second; | ||||
| } | ||||
| 
 | ||||
| // insert an item at index i
 | ||||
| void SinglyLinkedList::insert_at(int i, const int& inserted_item) | ||||
| { | ||||
|    if(i == 0) | ||||
|    { | ||||
|       this->push_front(inserted_item); | ||||
|       return; | ||||
|    } | ||||
|    SinglyLinkedListNode* predecessor = this->index(i-1); | ||||
|    this->insert_successor_to(predecessor, inserted_item); | ||||
| } | ||||
| 
 | ||||
| // remove the item at index i
 | ||||
| void SinglyLinkedList::erase_at(int i) | ||||
| { | ||||
|    if(i == 0) | ||||
|    { | ||||
|       this->pop_front(); | ||||
|       return; | ||||
|    } | ||||
|    SinglyLinkedListNode* predecessor = this->index(i-1); | ||||
|    this->erase_successor_to(predecessor); | ||||
| } | ||||
| 
 | ||||
| // insert an item after given node
 | ||||
| void SinglyLinkedList::insert_successor_to(SinglyLinkedListNode* predecessor, const int& inserted_item) | ||||
| { | ||||
|    SinglyLinkedListNode* new_node =  new SinglyLinkedListNode; | ||||
|    new_node->set_item(inserted_item); | ||||
|     | ||||
|    SinglyLinkedListNode* successor = predecessor->get_next(); | ||||
|    predecessor->set_next(new_node); | ||||
|    new_node->set_next(successor); | ||||
|     | ||||
|    if(!successor) this->tail = new_node; | ||||
| } | ||||
| 
 | ||||
| // remove the item after given node
 | ||||
| void SinglyLinkedList::erase_successor_to(SinglyLinkedListNode* predecessor) | ||||
| { | ||||
|    SinglyLinkedListNode* erased_node = predecessor->get_next(); | ||||
|    SinglyLinkedListNode* new_successor = erased_node->get_next(); | ||||
|     | ||||
|    predecessor->set_next(new_successor); | ||||
|    if(new_successor == nullptr) this->tail = predecessor; | ||||
|    delete erased_node; | ||||
| } | ||||
							
								
								
									
										89
									
								
								lab_3/sequences-int/singly-linked-list.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								lab_3/sequences-int/singly-linked-list.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | ||||
| #ifndef SINGLY_LINKED_LIST_H | ||||
| #define SINGLY_LINKED_LIST_H | ||||
| 
 | ||||
| #include <cassert> | ||||
| #include "sequence.h" | ||||
| 
 | ||||
| namespace seq | ||||
| { | ||||
|    class SinglyLinkedListNode | ||||
|    { | ||||
|    public: | ||||
|       // return a reference to the stored item
 | ||||
|       int& get_item() { return this->item; } | ||||
| 
 | ||||
|       // return pointer to next node, or nullptr if this is the final node
 | ||||
|       SinglyLinkedListNode* get_next() const { return this->next; } | ||||
| 
 | ||||
|       // overwrite the item stored in this node
 | ||||
|       void set_item(int in_item) { this->item = in_item; } | ||||
| 
 | ||||
|    private: | ||||
|       int item = 0; | ||||
|       SinglyLinkedListNode* next = nullptr; | ||||
| 
 | ||||
|       // attach a node to this node
 | ||||
|       // if there was a node attached to this previously, it is NOT deleted!
 | ||||
|       void set_next(SinglyLinkedListNode* in_next) { this->next = in_next; } | ||||
|        | ||||
|       friend class SinglyLinkedList;  // allow SinglyLinkedList to access private members
 | ||||
|    }; | ||||
| 
 | ||||
|    class SinglyLinkedList: public Sequence | ||||
|    { | ||||
|    public: | ||||
|       bool empty() const { return (this->head == nullptr); }  // test whether the singly linked list is empty
 | ||||
|       size_t size() const;  // return the size (number of items in the singly linked list)
 | ||||
| 
 | ||||
|       // it is the caller's responsibility to ensure that the list is not empty when calling front() or back()!
 | ||||
|       int& front() { assert(this->head); return this->head->get_item(); }  // return a reference to the first item
 | ||||
|       int& back()  { assert(this->tail); return this->tail->get_item(); }  // return a reference to the final item
 | ||||
| 
 | ||||
|       // return a reference to the item at position i of the list, counting from 0
 | ||||
|       // it is the caller's responsibility that the index is within range
 | ||||
|       int& at(int i) { return this->index(i)->get_item(); } | ||||
|        | ||||
|       // return pointer to the head/tail node
 | ||||
|       SinglyLinkedListNode* begin() const { return this->head; } | ||||
|       SinglyLinkedListNode* end() const { return this->tail; } | ||||
|        | ||||
|       // return pointer to the node at position i, counting from 0
 | ||||
|       SinglyLinkedListNode* index(int i) const; | ||||
| 
 | ||||
|       /*
 | ||||
|        * accepts an additional item into the singly linked list; | ||||
|        * by default, this is done at the back end of the list | ||||
|        * call push_front(...) to push an element at the front | ||||
|        *  | ||||
|        * the list takes ownership of the copy (but not of the original!) | ||||
|        */ | ||||
|       void push(const int& pushed_item) { this->push_back(pushed_item); } | ||||
|       void push_back(const int& pushed_item); | ||||
|       void push_front(const int& pushed_item); | ||||
| 
 | ||||
|       /*
 | ||||
|        * removes an item from the list (front end by default) | ||||
|        * to do the same at the back, call pop_back() | ||||
|        */ | ||||
|       void pop() { this->pop_front(); } | ||||
|       void pop_front(); | ||||
|       void pop_back(); | ||||
|       void clear() { while(!this->empty()) this->pop(); }  // remove all the items from the list
 | ||||
| 
 | ||||
|       // it is the caller's responsibility that the index is within range
 | ||||
|       void insert_at(int i, const int& inserted_item);  // insert an item at index i
 | ||||
|       void erase_at(int i);  // remove the item at index i
 | ||||
|        | ||||
|       // it is the caller's responsibility that the node is actually part of the list
 | ||||
|       void insert_successor_to(SinglyLinkedListNode* predecessor, const int& inserted_item);  // insert an item after given node
 | ||||
|       void erase_successor_to(SinglyLinkedListNode* predecessor);  // remove the item after given node
 | ||||
|        | ||||
|       ~SinglyLinkedList() { this->clear(); } | ||||
| 
 | ||||
|    private: | ||||
|       SinglyLinkedListNode* head = nullptr; | ||||
|       SinglyLinkedListNode* tail = nullptr; | ||||
|    }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user