330 lines
9.7 KiB
Markdown
330 lines
9.7 KiB
Markdown
# Lecture 14 --- Stack and Queue
|
||
|
||
## Today’s Lecture
|
||
|
||
- STL Queue and STL Stack
|
||
|
||
<!-- ## 14.0 Some Special Syntax
|
||
|
||
The following program demonstrates some special syntax about C++ constructors.
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
// custom class definition
|
||
class MyClass {
|
||
public:
|
||
// constructor
|
||
MyClass() {
|
||
std::cout << "Constructor called" << std::endl;
|
||
}
|
||
|
||
// destructor
|
||
~MyClass() {
|
||
std::cout << "Destructor called" << std::endl;
|
||
}
|
||
};
|
||
|
||
int main() {
|
||
MyClass();
|
||
|
||
MyClass A;
|
||
MyClass B;
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
What is the output of this program?
|
||
|
||
You can compile and run the [program](constructor.cpp).
|
||
|
||
## 14.1 Function Objects, a.k.a. Functors
|
||
|
||
- In addition to the basic mathematical operators + - * / < > , another operator we can overload for our C++
|
||
classes is the function call operator.
|
||
Why do we want to do this? This allows instances or objects of our class, to be used like functions. It’s weird
|
||
but powerful.
|
||
- Here’s the basic syntax. Any specific number of arguments can be used.
|
||
|
||
```cpp
|
||
class my_class_name {
|
||
public:
|
||
// ... normal class stuff ...
|
||
my_return_type operator() ( /* my list of args */ );
|
||
};
|
||
```
|
||
|
||
- Compile and run this simple functor [example](functor.cpp).
|
||
|
||
## 14.2 Why are Functors Useful?
|
||
|
||
- One example is the default 3rd argument for std::sort. We know that by default STL’s sort routines will use
|
||
the less than comparison function for the type stored inside the container. How exactly do they do that?
|
||
- First let’s define another tiny helper function:
|
||
|
||
```cpp
|
||
bool float_less(float x, float y) {
|
||
return x < y;
|
||
}
|
||
```
|
||
|
||
- And then let's define a vector called *my_data*:
|
||
|
||
```cpp
|
||
std::vector<float> my_data = {1.1, 2.2, 3.3, 4.4, 5.5};
|
||
```
|
||
|
||
- Remember how we can sort the *my_data* vector defined above using our own homemade comparison function for sorting:
|
||
|
||
```cpp
|
||
std::sort(my_data.begin(),my_data.end(),float_less);
|
||
```
|
||
|
||
If we don't specify a 3rd argument:
|
||
|
||
```cpp
|
||
std::sort(my_data.begin(),my_data.end());
|
||
```
|
||
|
||
This is what STL does by default:
|
||
|
||
```cpp
|
||
std::sort(my_data.begin(),my_data.end(),std::less<float>());
|
||
```
|
||
|
||
- What is std::less? It’s a templated class. Above we have called the default constructor to make an instance
|
||
of that class. Then, that instance/object can be used like it’s a function. Weird!
|
||
|
||
- How does it do that? std::less is a teeny tiny class that just contains the overloaded function call operator.
|
||
|
||
```
|
||
template <class T>
|
||
class less {
|
||
public:
|
||
bool operator() (const T& x, const T& y) const {
|
||
return x < y;
|
||
}
|
||
};
|
||
```
|
||
|
||
- You can use this instance/object/functor as a function that expects exactly two arguments of type T (in this
|
||
example float) that returns a bool. That’s exactly what we need for std::sort! This ultimately does the
|
||
same thing as our tiny helper homemade compare function!
|
||
|
||
## 14.3 Another Functor Example
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
|
||
// functor class
|
||
class MultiplyBy {
|
||
private:
|
||
int factor;
|
||
|
||
public:
|
||
// constructor
|
||
MultiplyBy(int factor) : factor(factor) {}
|
||
|
||
// overloaded function call operator
|
||
int operator()(int x) const {
|
||
return x * factor;
|
||
}
|
||
};
|
||
|
||
int main() {
|
||
// create an instance of the functor
|
||
MultiplyBy multiplyByTwo(2);
|
||
|
||
// use the functor as a function
|
||
// surprising: the object itself can be used like it's a function.
|
||
std::cout << "Result of multiplying 5 by 2: " << multiplyByTwo(5) << std::endl;
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
- You can compile and run this [example](multiply.cpp).
|
||
-->
|
||
|
||
## 14.1 Additional STL Container Classes: Stacks
|
||
|
||
<!--We’ve studied STL vectors, lists, maps, and sets. These data structures provide a wide range of flexibility in
|
||
terms of operations. One way to obtain computational efficiency is to consider a simplified set of operations or
|
||
functionality.-->
|
||
<!-- For example, with a hash table we give up the notion of a sorted table and gain in find, insert, & erase efficiency.-->
|
||
|
||
- A stack is a linear data structure that follows the Last-In-First-Out (LIFO) principle.
|
||
- Stacks allow access, insertion and deletion from only one end called the top.
|
||
- There is no access to values in the middle of a stack.
|
||
- Stacks may be implemented efficiently in terms of vectors and lists, although vectors are preferable.
|
||
- All stack operations are O(1).
|
||
|
||
### 14.1.1 Member functions of std::stack
|
||
|
||
- push(const T& value): Adds an element value to the top of the stack.
|
||
- pop(): Removes the top element from the stack.
|
||
- top(): Returns a reference to the top element of the stack without removing it.
|
||
- empty(): Checks if the stack is empty. Returns true if the stack is empty, false otherwise.
|
||
- size(): Returns the number of elements in the stack.
|
||
|
||
### 14.1.2 Stack Example Program
|
||
|
||
- Following is an example program, remember to include the stack library.
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
#include <stack>
|
||
|
||
int main() {
|
||
std::stack<int> myStack;
|
||
|
||
myStack.push(10);
|
||
myStack.push(20);
|
||
myStack.push(30);
|
||
myStack.push(40);
|
||
myStack.push(50);
|
||
|
||
std::cout << "Size of stack: " << myStack.size() << std::endl;
|
||
std::cout << "Top element: " << myStack.top() << std::endl;
|
||
|
||
if (!myStack.empty()) {
|
||
std::cout << "Stack is not empty" << std::endl;
|
||
} else {
|
||
std::cout << "Stack is empty" << std::endl;
|
||
}
|
||
|
||
myStack.pop();
|
||
// What is the output of this next line?
|
||
std::cout << "Top element after pop: " << myStack.top() << std::endl;
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
You can compile and run this above [program](stack.cpp).
|
||
|
||
### 14.1.3 Stack Implementation
|
||
|
||
We have the stack implementation and test code here: [stack.h](stack.h), [stack_test.cpp](stack_test.cpp).
|
||
|
||
## 14.2 Additional STL Container Classes: Queues
|
||
|
||
- A queue is a linear data structure that follows the First-In-First-Out (FIFO) principle.
|
||
- Queues allow insertion at one end, called the back and removal from the other end, called the front.
|
||
- There is no access to values in the middle of a queue.
|
||
- Queues may be implemented efficiently in terms of a list. Using vectors for queues is also possible, but requires more work to get right.
|
||
- All queue operations are O(1).
|
||
|
||
### 14.2.1 Member functions of std::queue
|
||
|
||
- push(const T& value): Adds an element value to the rear of the queue. This operation is also known as enqueue.
|
||
- pop(): Removes the front element from the queue. This operation is also known as dequeue.
|
||
- front(): Returns a reference to the front element of the queue without removing it.
|
||
- empty(): Checks if the queue is empty. Returns true if the queue is empty, false otherwise.
|
||
- size(): Returns the number of elements in the queue.
|
||
|
||
### 14.2.2 Queue Example Program
|
||
|
||
- Following is an example program, remember to include the queue library.
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
#include <queue>
|
||
|
||
int main() {
|
||
std::queue<int> myQueue;
|
||
|
||
myQueue.push(10);
|
||
myQueue.push(20);
|
||
myQueue.push(30);
|
||
myQueue.push(40);
|
||
myQueue.push(50);
|
||
|
||
std::cout << "Size of queue: " << myQueue.size() << std::endl;
|
||
std::cout << "Front element: " << myQueue.front() << std::endl;
|
||
|
||
if (!myQueue.empty()) {
|
||
std::cout << "Queue is not empty" << std::endl;
|
||
} else {
|
||
std::cout << "Queue is empty" << std::endl;
|
||
}
|
||
|
||
myQueue.pop();
|
||
// What is the output of this next line?
|
||
std::cout << "Front element after pop: " << myQueue.front() << std::endl;
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
You can compile and run this above [program](queue.cpp).
|
||
|
||
## 14.3 Stack Example Problem
|
||
|
||
Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.
|
||
|
||
An input string is valid if:
|
||
|
||
- Open brackets must be closed by the same type of brackets.
|
||
- Open brackets must be closed in the correct order.
|
||
- Every close bracket has a corresponding open bracket of the same type.
|
||
|
||
Example 1: Input: s = "()" Output: true
|
||
|
||
Example 2: Input: s = "()[]{}" Output: true
|
||
|
||
Example 3: Input: s = "(]" Output: false
|
||
|
||
Example 4: Input: s = "([])" Output: true
|
||
|
||
Constraints: 1 <= s.length <= 104; s consists of parentheses only '()[]{}'.
|
||
|
||
We have the solution here:
|
||
|
||
```cpp
|
||
bool isValid(string s) {
|
||
std::stack<char> myStack;
|
||
int len = s.length();
|
||
char c;
|
||
for(int i=0;i<len;i++){
|
||
// push all the open brackets into the stack
|
||
if(s[i]=='(' || s[i]=='{' || s[i]=='['){
|
||
myStack.push(s[i]);
|
||
}else{
|
||
// if we encounter a close bracket first, it's already invalid.
|
||
if(myStack.empty()){
|
||
return false;
|
||
}
|
||
c = myStack.top();
|
||
myStack.pop();
|
||
// for every close bracket we encounter, there must be a corresponding open bracket at the top of the stack.
|
||
if(s[i]==')' && c!='('){
|
||
return false;
|
||
}
|
||
if(s[i]=='}' && c!='{'){
|
||
return false;
|
||
}
|
||
if(s[i]==']' && c!='['){
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
// if it's empty, we are good.
|
||
if(myStack.empty()){
|
||
return true;
|
||
}else{
|
||
return false;
|
||
}
|
||
}
|
||
```
|
||
|
||
<!-- - [Leetcode problem 20: Valid Parentheses](https://leetcode.com/problems/valid-parentheses/). Solution: [p20_valid_parentheses.cpp](../../leetcode/p20_valid_parentheses.cpp) -->
|
||
|
||
- Play this [animation](https://jidongxiao.github.io/CSCI1200-DataStructures/animations/stack/index.html) to see how this program works.
|
||
|
||
## 14.4 More Leetcode Exercises
|
||
|
||
- [Leetcode problem 225: Implement Stack using Queues](https://leetcode.com/problems/implement-stack-using-queues/). Solution: [p225_stack_using_queues.cpp](../../leetcode/p225_stack_using_queues.cpp).
|
||
- [Leetcode problem 232: Implement Queue using Stacks](https://leetcode.com/problems/implement-queue-using-stacks/). Solution: [p232_queue_using_stacks.cpp](../../leetcode/p232_queue_using_stacks.cpp).
|
||
- [Leetcode problem 71: Simplify Path](https://leetcode.com/problems/simplify-path/). Solution: [p71_simplify_path.cpp](../../leetcode/p71_simplify_path.cpp)
|