CPSC 5005
Data Structures
Yueting Chen
Data Structure
Data Structure
• What is data structure?
• Linear Data Structure
• Data is organized sequentially, one
element after another.
• Non-linear Data Structure
• Data is organized in a non-sequential
way. E.g., organized hierarchically.
Basic Data Structure
• Array/2D Array
• Linked List
• Abstract Data Type
• Vector
• List
• Stack
• Queue
Two-dimensional (2D) Arrays
• 2D Array: Array of Arrays
• If size is known before compilation
int matrix[5][7]; //space reserved in stack
• Otherwise, dynamic allocation is needed
int n = 5, m = 7;
int** M = new int*[n]; //array of row pointers
for(int i=0; i<n; i++)
M[i] = new int[m]; //allocate the ith row
2D Arrays
M
M is a pointer to an int
int*
pointer, therefore:
int** M int*
int*
int*
int*
int*
2D Arrays
• De-allocate the matrix after use.
• De-allocate rows first,
• Then deallocate the array of row pointers.
for (int i = 0; i < n; i++)
delete[] M[i]; // delete the i-th row
delete[] M; // delete the array of row pointers
Problem: Maintain a Scoreboard
• A scoreboard contains a list of (name,
score) pairs.
• The items are sorted by score.
• Question:
• What are the classes/structs needed in
the problem?
Scoreboard - Definitions
• Define a class for each (name, score) pair (GameEntry).
• Define a class for the entire scoreboard (Scores).
What are the operations needed by two classes?
GameEntry
class GameEntry { // a game score entry
public:
GameEntry(const string& n="", int s=0); // constructor
Class string getName() const; // get player name
Definition int getScore() const; // get score
private:
string name; // player's name
int score; // player's score
OR };
struct GameEntry { // stores a game entry
Struct GameEntry(const std::string& n = "", int s = 0) : name(n), score(s) {}
std::string name; // player's name
Definition int score; // player's score
};
Scores
class Scores { // stores game high scores
• Main methods public:
Scores(int maxEnt = 10); // constructor
• Constructor ~Scores(); // destructor
void add(const GameEntry& e); // add a game entry
• Destructor GameEntry remove(int i); // remove the ith entry
• Add elements private:
int maxEntries; // maximum number of entries
• Remove elements int numEntries; // actual number of entries
GameEntry* entries; // array of game entries
};
Question:
• What to do if?
• Remove is invoked with parameter i > 10?
Side Note: Define Exception
#include <iostream>
#include <exception>
• You can also define #include <string>
using namespace std;
exception to indicate illegal
inputs class IndexOutOfBounds : public exception {
private:
string message;
• (Not Required for this course) public:
// constructor with index and size info
IndexOutOfBounds(int index, int size) {
class Scores { message = "Index " + to_string(index) +
public: " is out of bounds for array of size " +
GameEntry remove(int i) to_string(size);
throw(IndexOutOfBounds); }
}; // override what()
const char* what() const noexcept override {
return message.c_str();
}
https://cplusplus.com/doc/tutorial/exceptions/ };
Constructor and Destructor
class Scores {
• How to implement constructor & destructor? public:
Scores(int maxEnt = 10);
~Scores();
};
Scores::Scores(int maxEnt) { // constructor
maxEntries = maxEnt; // save the max size
entries = new GameEntry[maxEntries]; // allocate array storage
numEntries = 0; // initially no elements
}
Scores::~Scores() { // destructor
delete[] entries;
}
Add
• How to implement add elements in Scores?
• add(e):
• Insert game entry e into the collection of high scores. If this causes the number of
entries to exceed maxEntries, the smallest is removed.
Steps:
1. Test if the new element can be added to the
array.
1. If the array is full and the smallest > e.score,
ignore.
2. Find where to insert the new element.
3. Make space for the new element.
4. Insert the new element to the target position.
Add
void Scores::add(const GameEntry& e) { // add a game entry
int newScore = e.getScore(); // score to add
if (numEntries == maxEntries) { // the array is full
if (newScore <= entries[maxEntries-1].getScore()) Step 1: Test if the new element can be added
return; // not high enough − ignore to the array & update the # of elements
}
else numEntries++; // if not full, one more entry
int i = numEntries-2; // start with the next to last
while ( i >= 0 && newScore > entries[i].getScore() ) { Step 2 & 3: find the target position and make
entries[i+1] = entries[i]; // shift right if smaller
i--;
space for the new element
}
entries[i+1] = e; // put e in the empty spot Step 4: Insert the new element to the target position.
}
Remove
• remove(i)
• Remove and return the game entry e at index i in the entries array.
• For a valid index i, the entries array is updated to remove the object at index i and
all objects previously stored at indices higher than i are "shifted left" to fill in for the
removed object.
Steps:
1. Test if the index i is valid.
1. If not, throw an Exception or return a default
value.
2. Find where to insert the new element.
3. Make space for the new element.
4. Insert the new element to the target position.
Remove
GameEntry Scores::remove(int i) throw(IndexOutOfBounds) {
if ((i < 0) | | (i >= numEntries)) // invalid index
throw IndexOutOfBounds("Invalid index");
GameEntry e = entries[i]; // save the removed object
for (int j = i+1; j < numEntries; j++)
entries[j-1] = entries[j]; // shift entries left
numEntries--; // one fewer entry
return e; // return the removed object
}
Summary
• We have designed and implemented a scoreboard
• The scoreboard can be seen as a data structure
• The data structure:
• Stores player records (name, score) utilizing a dynamic array.
• Supports adding and updating players
• Allows retrieving and comparing scores.
• More functions can be extended.
General Collections
• Can we make it more general?
• In the previous example, we require the scoreboard to be sorted.
• In many scenarios, we only require a collection of elements.
• The collection should provide some general operations
• E.g., add(), remove(), first(), last(), size(), etc.
Abstract Data Type (ADT)
• An abstract data type (ADT) is a set of objects together with a set
of operations.
ADT Public Functions
Data Structure Application Program
(Interface) (Client)
Implementation
add, remove, etc.
• ADT: focus on what operations can be applied to the objects.
• Does not mention how these operations are implemented.
Abstract Data Type (ADT)
• Container (collection)
• One of the basic ADTs.
• A data type that is capable of holding a collection of items.
• Required Operations
• size(): return the # elements in the container.
• clear(): removes all elements from the container.
• empty(): returns true if the container contains no
elements, and false otherwise.
Vector ADT
• A vector is an ADT that supports the following
operations, assume 0 ≤ 𝑖 ≤ 𝑠𝑖𝑧𝑒() − 1
• at(i): returns the element at index i.
• set(i, e): replace the element at index i with e.
• insert(i, e): insert a new element e to have index i.
• erase(i): remove the element at index i.
How can you implement a vector ADT?
Vector ADT
• Can you implement vector with a simple array?
• Key operations: class Vector {
private:
int* data; // pointer to array
• constructor int capacity; // total allocated size
• insert(i, e) int length; // current number of elements
• erase(i) public:
Vector(int cap = 10); // constructor
• size() ~Vector(); // destructor
void insert(int i, int e); // insert element at index i
void erase(int i); // remove element at index i
int size() const; // return number of elements
};
Vector ADT
• Question:
• What to do if the vector is full when inserting a new element?
void insert(int i, int e);
Resize & insert
- resize the array first, void resize(int newCap); How to resize?
- then insert the new element.
Two options:
- newCap = oldCap + 1 We prefer always double the size.
- newCap = oldCap *2 (reduce # of resize function calls)
Resize
• Resize
void Vector::resize(int newCap) {
int* newData = new int[newCap];
void Vector::insert(int i, int e) { for (int i = 0; i < length; i++) {
if (length == capacity) resize(capacity * 2); newData[i] = data[i];
}
for (int j = length; j > i; j--) {
data[j] = data[j - 1]; delete[] data;
} data = newData;
data[i] = e; capacity = newCap;
length++; }
}
Similarly, you can shrink the size after # of elements fall below a certain threshold.
Summary on Vector ADT
• Vector ADT
• Some basic operations:
• Insert, erase, size, …
• In our implementation
• Use array to store elements.
• Resize to copy all elements to a larger array when needed.
• In fact, we do have a standard vector implementation in STL (standard template
library)
• https://cplusplus.com/reference/vector/a/
An Alternative Approach
• Is using array the only way to store a list of elements?
https://www.happycoders.eu/algorithms/array-vs-linked-list/
Linked List
• Linked List
• A collection of nodes that together form a linear ordering.
• Node: stores one element and pointers (links) to the next node in the list.
• Define a base class/struct
struct Node{ struct Node{
string data; int data; Aka. Singly Linked List
Node* next; Node* next;
• Each node stores a single pointer.
} }
Linked List - Operations
Insert to the front
void SinglyLinkedList::addFront(const
string& e) { // add to front of list
Node* v = new Node; // create new node
v->elem = e; // store data
v->next = head; // head now follows v
head = v; // v is now the head
}
Linked List - Operations
Remove from the front
void SinglyLinkedList::removeFront() { //
remove front item
Node* old = head; // save current head
head = old->next; // skip over old
head
delete old; // delete the old head
}
Question
• What about inserting/deleting at a specific location?
• You will always need to start from the front!
• Any better ways to do this?
• Doubly Linked List
• Two pointers for each node:
• next and prev
Linked List
• Pros • Cons
• Access any item as long as • Overhead of links
external link to first item • No longer have direct access to
maintained each element of the list
• Insert/delete new item without • Access of nth item now less
shifting efficient
• Can expand/contract as necessary • Must go through first element, and
then second, and then third, etc
The Idea of Iterator
• Two ways of navigating in a linear data structure
• Array • Linked List
• Navigate using indices, e.g., arr[i] • Navigate using relative positions, e.g., next, previous.
• Iterator
• The object that is used to navigate through the container by advancing to the next
position in the container.
• Acts like a pointer.
List ADT
• A list is a linear, ordered collection of elements using the concept of an
iterator.
• Required Operations
• begin(): return an iterator referring to the first element.
• end(): return an iterator referring to an imaginary element just after the last
element
• insert(p, e): insert a new element e before position p.
• erase(p): remove the element at position p.
Note: p is an iterator that refers to a specific position in the list.
Stack ADT
• A stack is a container of objects that are inserted
and removed according to the last-in first-out
(LIFO) principle.
• Required Operations
• push(e): insert element e at the top of the stack
• pop(): remove the top element from the stack; an
error occurs if the stack is empty.
• peek(): return a reference to the top element on the
stack, without removing it; an error occurs if the stack is
empty.
Stack Applications
• Applications of stack?
• Stack of plates/cups…
• Undo / Redo Operations
• Broswer history
• Call logs in phones
•…
Stack Implementation
• How to implement stack?
• Static: Use a fixed size array.
• Dynamic: grow in size as needed, use a vector/list, etc.
• For a static stack of integers:
• How would you outline the class?
• What are the pros and cons?
• For a dynamic stack of integers:
• What is the main difference between static and dynamic?
• Can you use linked list to implement it? How?
• Pros and Cons?
Queue ADT
• A queue is a container of elements that are inserted
and removed according to the first-in first-out (FIFO)
principle.
• Required Operations
• enqueue(e): insert element e at the rear of the queue.
• dequeue(): remove element at the front of the queue;
an error occurs if the queue is empty.
• front(): return, but do not remove, a reference to the
front element in the queue; an error occurs if the queue is
empty.
Queue Applications
• Applications of Queue?
• Waiting lines in stores
• Printer jobs
• Booking systems
• Messages
•…
Queue Implementation
• How to implement queue?
• Static: Use a fixed size array.
• Dynamic: grow in size as needed, use a vector/list, etc.
• For a static queue of integers:
• How would you outline the class?
• What are the pros and cons?
• For a dynamic queue of integers:
• What is the main difference between static and dynamic?
• Can you use linked list to implement it? How?
• Pros and Cons?
Double-Ended Queue (Deque)
• Double-Ended Queues
• Supports insertion and deletion at both the front
and rear of the queue.
• Required Operations
• insertFront(e): insert element e at the front of
the queue.
• insertBack(e): insert element e at the end of the
queue. Look Familiar?
• eraseFront(): remove element at the front of the
queue;
Deque can be implemented using a
• eraseBack(): remove element at the end of the doubly linked list OR array.
queue;
Deque can be used to implement
Queue AND Stack
STL library
• Standard Template Library
• Defines ADT using template classes & functions.
#include <stack>
using std::stack; // make stack accessible
stack<int> myStack; // a stack of integers
#include <queue>
using std::queue; // make queue accessible
queue<float> myQueue; // a queue of floats
#include <list>
using std::list; // make list accessible
list<float> myList; // an empty list of floats
Interface vs Implementation
• Stack and Queue are two different ADTs
• However, by default they both utilizing deque (double ended
queue) as underlying container (data structure) in C++ STL.
Resources
• Chapter 3.1-3.3
• Chapter 5
• Chapter 6.1-6.2
• Visualization tool
• https://pythontutor.com/visualize.html#mode=edit
• C++ references
• https://cplusplus.com/reference/