Data Structures Flashcards
Principle for a Stack
LIFO: Last in first out
Principle for a Que
FIFO: First in first out
Best analogy for explaining how a stack works
It’s like a “stack” of dishes. When you wash dishes, you start at the top and work your way down. When someone adds a new dish, they add it to the top of the stack.
Best analogy to explain how a que works
Similar to a line of people waiting for a service, where the first person to join the line is the first to be served.
Data Structure
Organization of data in memory that behaves in a predictable way.
A data structure is an actual implementation or realization of an abstract data type (ADT). It involves organizing and storing data in a specific way to facilitate efficient access, modification, and retrieval of the data.
Characteristics:
1) Defines how the data is organized, stored, and accessed.
2) Specifies the internal representation and implementation details.
3) Determines the efficiency and performance of operations based on the chosen representation.
Abstract Datatype (ADT)
Focuses on what operations can be performed on the data and what behavior those operations should have, independent of any particular language
Characteristics:
1) Defines the operations that can be performed on the data.
2) Specifies the interface (operations and behavior) without revealing the implementation details.
3) Provides a high-level abstraction, encapsulating the data and its associated operations.
Data Structures v. Abstract Data Types
1) An abstract data type (ADT) is a theoretical concept that defines a set of operations and behavior without specifying implementation details, while a data structure is the actual implementation of an ADT, providing concrete ways to store and manipulate data.
2) ADT focuses on the behavior and interface, allowing for various implementations (data structures) to achieve that behavior. On the other hand, a data structure defines how data is organized and accessed, emphasizing implementation specifics.
3) ADT is more concerned with the logical view of the data and operations, while a data structure deals with the physical representation and manipulation of data in memory.
Most Basic Data Types
1) Stack
2) Que
3) List
4) Trees
5) Heaps
6) Graphs
Stack
Operates as a container
Can hold other things (possibly even other stacks)
All insertions and deletions can only occur at the TOP of the stack (LIFO)
Pros of using a stack
Pros:
1) Efficient Push and Pop Operations: Adding an element (push) or removing the top element (pop) is highly efficient and takes constant time (O(1)).
2) Simplified Data Structure: Simple and intuitive structure based on the Last-In-First-Out (LIFO) principle, making it easy to understand and use.
3) Space Efficiency (with Proper Implementation): Can be space-efficient, especially when implemented using a linked list. Efficient memory usage for managing function call contexts.
4) Algorithm Design: Suited for specific algorithms, simplifying their design and implementation. Useful for tasks like backtracking and parsing.
5) Function Call Management: Integral for managing function calls, recursion, and execution contexts in programming languages.
Cons of using a stack
Cons:
1) Limited Access: Restricts access to the topmost element; direct access or modification of other elements requires removing the top elements.
2) No Random Access: Lacks random access to elements, limiting flexibility in accessing or modifying specific elements in the middle of the stack.
3) Memory Management (with Array-based Implementation): If implemented using an array, frequent push/pop operations may lead to resizing, potentially causing memory wastage and performance issues.
4) Potential Stack Overflow: Excessive recursion or deep function call stacks can lead to stack overflow issues, affecting program stability.
5) Context Sensitivity: Context and state are highly sensitive to the order of operations, which can be challenging to manage in certain scenarios.
Operations that can be performed on a stack
Push: Adds an element to the top of the stack.
Pop: Removes and retrieves the top element of the stack.
Peek: Retrieves the top element without removing it.
Accessibility concerns with using a stack
Only the top element is accessible for reading or removal. Access to other elements requires popping off the top elements.
Makes it much less accessible than a queue
Accessibility of a queue
Both the front and rear elements are accessible. Front element is for reading or removal, while rear element is for insertion.
Makes it much more accessible than a stack
Operations that can be done on a que
Enqueue: Adds an element to the end of the queue.
Dequeue: Removes and retrieves the front element of the queue.
Peek: Retrieves the front element without removing it.
Applications of stacks
1) Algorithmic Problems: Various algorithmic problems, such as Tower of Hanoi, can be solved efficiently using stacks, demonstrating their usefulness in solving complex puzzles and scenarios.
2) Compiler Design: In compiler design, stacks are employed to implement the parsing and evaluation of algebraic expressions during the compilation process.
3) Depth-First Traversal: Depth-first traversal of trees and graphs, such as in graph traversal algorithms (e.g., depth-first search), can be efficiently implemented using stacks to manage nodes to be visited.
4) Memory Management: Stacks are used in memory management, like in the call stack of an operating system, to keep track of the execution context of programs, memory allocations, and deallocations.
5) Function Call Management: Stacks are used by programming languages to manage function calls. When a function is called, its context (local variables, return addresses) is pushed onto the stack, and it’s popped off when the function completes its execution.
6) Expression Evaluation: Stacks are vital for evaluating arithmetic expressions, postfix expressions, and handling infix-to-postfix conversions. Operators and operands are pushed and popped based on their precedence.
7) Backtracking Algorithms: Algorithms like depth-first search (DFS) use stacks to keep track of visited nodes and paths, making it easier to backtrack and explore alternative paths.
8) Parenthesis Matching and Syntax Parsing: Stacks are used to match opening and closing brackets, braces, and parentheses in expressions, ensuring proper syntax and balanced structures.
Applications of Queues
1) Message Queues: Queues are extensively used in message-driven architectures and communication systems. They help manage messages between different parts of a system or between different systems, ensuring reliable message delivery and processing.
2) Task Processing and Load Balancing: Queues are used in task processing systems to distribute and manage tasks across workers or processing units. This helps balance the workload and ensures efficient task execution.
3) Breadth-First Search (BFS) in Graphs: BFS traversal of graphs uses a queue to explore nodes at the current level before moving on to nodes at the next level, making it an essential component for graph algorithms.
4) Job Scheduling: Queues are employed in job scheduling systems where jobs or tasks are added to a queue and processed in the order they were added, facilitating task scheduling and prioritization.
5) Print Queue in Operating Systems: In operating systems, print queues are used to manage print requests from multiple users. Jobs are placed in a queue and processed one after another.
6) Request Handling in Web Servers: Web servers use queues to manage incoming requests. Each request is added to a queue, and the server processes them sequentially, preventing overload and ensuring responsive performance.
7) Buffering in I/O Operations: Queues are used to buffer I/O operations, especially in scenarios where data is produced faster than it can be consumed. The queue allows for smooth data flow between producers and consumers.
8) Background Task Execution: Background tasks, such as periodic updates or data processing, can be scheduled and executed using a queue, ensuring orderly processing and optimal resource utilization.
9) Event Handling in GUI Applications: Queues are utilized in graphical user interfaces (GUIs) to handle user-generated events like mouse clicks and keyboard inputs. Events are queued and processed in the order they occur.
10) Buffering in Networking: Queues are employed in networking for buffering data packets when transferring data between devices. This helps manage traffic and maintain a smooth flow of data.
11) Caching: Queues can be used for caching to store frequently accessed data, ensuring a consistent and ordered approach to data access.
Operations that can be performed on a stack
1) Push: Adds an element to the top of the stack.
2) Pop: Removes and returns the element at the top of the stack.
3) Peek (or Top): Returns the element at the top of the stack without removing it.
isEmpty: Checks if the stack is empty or not.
4) isFull: Checks if the stack is full, applicable in fixed-size stack implementations.
5) Size (or Count): Returns the number of elements currently in the stack.
6) Clear (or Empty): Removes all elements from the stack, making it empty.
Syntax of an implementation of the ‘push’ command for a stack
in C++:
class MyStack {
private:
std::stack<int> data; // Use an STL stack for simplicity
public:
void push(int value) {
data.push(value);
}
};</int>
Syntax for an implementation of the ‘top’ command for a stack
in C++:
class MyStack {
private:
std:stack<int> data;
public:
int top() {
if (!data.empty()) {
return data.top();
} else {
std::cout << "Stack is empty.\n";
return -1; // Or any sentinel value indicating an error
}
}
};</int>
Syntax for an implementation of the ‘pop’ command for a stack
in C++:
class MyStack {
private:
std::stack<int> data;
public:
void pop() {
if (!data.empty()) {
data.pop();
} else {
std::cout << "Stack is empty. Cannot pop.\n";
}
}
};</int>
Syntax for an implementation of the ‘isEmpty’ command for a stack
in C++:
class MyStack {
private:
std::stack<int> data;
public:
// Check if the stack is empty
bool isEmpty() {
return data.empty();
}
};</int>
What happens if ‘Push’ is called when the stack is already full?
“Stack Overflow”
Exaclty how this is handled depends on which language you are using. Four basic possibilities (the first two are the most common):
1) Error or Exception:
Many programming languages and stack implementations will raise an error, throw an exception, or terminate the program when a push operation is attempted on a full stack.
2) Undefined Behavior:
In some cases, attempting to push an element onto a full stack might result in undefined behavior. This means the program might crash, produce unpredictable results, or corrupt data.
*these two are much less common, but can still happen in some fairly popular languages
3) No Action:
Some implementations may choose to silently ignore the push operation when the stack is full, without modifying the stack. This can be risky as it doesn’t provide feedback to the program that the operation was unsuccessful. (example: Python)
4) Resizing or Dynamic Stack:
If the stack is dynamically resizable, the implementation might allocate additional memory, resize the stack, and then push the element. However, if resizing is not possible (e.g., due to memory constraints), this may revert to an error or exception. (Example: Java)
Error Handling in for Stack Overflows
Option #1 – Client is responsible
*Client code must contain defensive code to detect and avoid these situations in which the operations of the container class are undefined
Option #2 – Container is responsible
*Container class must contain defensive code that signals client code if these situations occur so that client can take appropriate action