This commit is contained in:
2024-03-12 20:52:56 +01:00
parent 80510b1d17
commit 61e230c0a5
9 changed files with 0 additions and 0 deletions

21
lab_3/17_and_18/Makefile Normal file
View 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

View File

@@ -0,0 +1,40 @@
#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;
}
// add an item at the end of the list
void DoublyLinkedList::enqueue(int value)
{
DoublyLinkedListNode* new_node = new DoublyLinkedListNode;
new_node->set_item(value);
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;
}
// remove the head node and item
int DoublyLinkedList::dequeue()
{
if(this->empty()) return 0; // 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 **/
int outgoing = this->head->item;
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
return outgoing;
}

View File

@@ -0,0 +1,64 @@
#ifndef DOUBLY_LINKED_LIST_H
#define DOUBLY_LINKED_LIST_H
#include <cassert>
#include <cstddef>
#include "queue.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 Queue
{
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)
void enqueue(int element);
int dequeue();
// return pointer to the head/tail node
DoublyLinkedListNode* begin() const { return this->head; }
DoublyLinkedListNode* end() const { return this->tail; }
void clear() { while(!this->empty()) this->dequeue(); } // remove all the items from the list
~DoublyLinkedList() { this->clear(); }
private:
DoublyLinkedListNode* head = nullptr;
DoublyLinkedListNode* tail = nullptr;
};
}
#endif

View File

@@ -0,0 +1,102 @@
#include <algorithm>
#include <cassert>
#include <iostream>
#include "queue.h"
#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;
}
void DynamicArray::enqueue(int value)
{
int i = this->logical_size;
// 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] = value;
}
int DynamicArray::dequeue()
{
if (this->size() == 0) {
return 0;
}
int i = 0;
// Save the first element wich we want to return:
int outgoing = this->values[0];
// shift all elements from onward one to the left
size_t shifted_elements = this->logical_size - 1;
int* temp_storage = new int[shifted_elements]();
// copy values[i+1] to values[logical_size-1] into temp_storage
std::copy(this->values + 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;
if (!this->empty()) {
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);
}
return outgoing;
}
// 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;
}

View File

@@ -0,0 +1,27 @@
#include "queue.h"
namespace seq
{
class DynamicArray: public Queue
{
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)
void enqueue(int element);
int dequeue();
void clear(); // remove all the items from the array
~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
};
}

12
lab_3/17_and_18/queue.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef QUEUE_H
#define QUEUE_H
namespace seq {
class Queue {
public:
virtual void enqueue(int element) = 0;
virtual int dequeue() = 0;
};
}
#endif

View File

@@ -0,0 +1,103 @@
#include <cassert>
#include <chrono>
#include <iostream>
#include <queue>
#include "dynamic-array.h"
#include "queue.h"
#include "singly-linked-list.h"
#include "doubly-linked-list.h"
namespace
{
/*
* run a simple test
*/
void test_queue(seq::Queue* sqn, int n, int m, std::ostream* os)
{
assert((m > 0) && (n > m));
if(os) *os << "Enqueue even numbers from 0 to " << 2*(n-1) << ".\n";
for(int i = 0; i < n; i++) sqn->enqueue(2*i);
for(int i = 0; i < n; i++) sqn->dequeue();
}
void test_queue(std::queue<int>* sqn, int n, int m, std::ostream* os)
{
assert((m > 0) && (n > m));
if(os) *os << "Enqueue even numbers from 0 to " << 2*(n-1) << ".\n";
for(int i = 0; i < n; i++) sqn->push(2*i);
for(int i = 0; i < n; i++) sqn->pop();
}
/*
* return time measurement in units of seconds
*/
float test_with_time_measurement(seq::Queue* sqn, int iterations)
{
int queue_length = 20001;
int deletions = 10;
test_queue(sqn, queue_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_queue(sqn, queue_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();
}
float test_with_time_measurement(std::queue<int>* sqn, int iterations)
{
int queue_length = 20001;
int deletions = 10;
test_queue(sqn, queue_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_queue(sqn, queue_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\n*** test with std::queue ***\n";
std::queue<int> queue;
float queue_time = test_with_time_measurement(&queue, 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";
std::cout << "Runtime for std::queue:\t" << queue_time << " s\n";
}

View File

@@ -0,0 +1,33 @@
#include <cstddef>
#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;
}
void SinglyLinkedList::enqueue(int value)
{
SinglyLinkedListNode* new_node = new SinglyLinkedListNode;
new_node->set_item(value);
if(this->empty()) this->head = new_node;
else this->tail->set_next(new_node);
this->tail = new_node;
}
int SinglyLinkedList::dequeue()
{
if(this->empty()) return 0; // nothing there to remove
SinglyLinkedListNode* successor = this->head->get_next();
int outgoing = this->head->item;
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
return outgoing;
}

View File

@@ -0,0 +1,50 @@
#ifndef SINGLY_LINKED_LIST_H
#define SINGLY_LINKED_LIST_H
#include <cassert>
#include "queue.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 Queue
{
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)
void enqueue(int value);
int dequeue();
void clear() { while(!this->empty()) this->dequeue(); } // remove all the items from the list
~SinglyLinkedList() { this->clear(); }
private:
SinglyLinkedListNode* head = nullptr;
SinglyLinkedListNode* tail = nullptr;
};
}
#endif