re order 11 and 12
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# Lab 12 — Stacks and Queues
|
# Lab 11 — Stacks and Queues
|
||||||
|
|
||||||
In this lab, you will implement queues in different ways, and then fix memory leaks in the provided program. Start by downloading the provided program [levelOrder.cpp](levelOrder.cpp). The provided program [levelOrder.cpp](levelOrder.cpp) traverses a binary tree by level order. It prints the following message to STDOUT:
|
In this lab, you will implement queues in different ways, and then fix memory leaks in the provided program. Start by downloading the provided program [levelOrder.cpp](levelOrder.cpp). The provided program [levelOrder.cpp](levelOrder.cpp) traverses a binary tree by level order. It prints the following message to STDOUT:
|
||||||
|
|
||||||
BIN
labs/11_stacks_and_queues/a.out
Executable file
BIN
labs/11_stacks_and_queues/a.out
Executable file
Binary file not shown.
153
labs/11_stacks_and_queues/levelOrder_sol1.cpp
Normal file
153
labs/11_stacks_and_queues/levelOrder_sol1.cpp
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
|
// Definition for a binary tree node.
|
||||||
|
class TreeNode {
|
||||||
|
public:
|
||||||
|
int val;
|
||||||
|
TreeNode* left;
|
||||||
|
TreeNode* right;
|
||||||
|
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyQueue {
|
||||||
|
public:
|
||||||
|
MyQueue() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(TreeNode* x) {
|
||||||
|
int size = myStack1.size();
|
||||||
|
// move everything in stack1 to stack2
|
||||||
|
for(int i=0;i<size;i++){
|
||||||
|
myStack2.push(myStack1.top());
|
||||||
|
myStack1.pop();
|
||||||
|
}
|
||||||
|
// push x into stack1
|
||||||
|
myStack1.push(x);
|
||||||
|
// move everything in stack2 to stack1
|
||||||
|
for(int i=0;i<size;i++){
|
||||||
|
myStack1.push(myStack2.top());
|
||||||
|
myStack2.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop() {
|
||||||
|
myStack1.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode* front() {
|
||||||
|
return myStack1.top();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() {
|
||||||
|
return myStack1.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() {
|
||||||
|
return myStack1.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::stack<TreeNode*> myStack1;
|
||||||
|
std::stack<TreeNode*> myStack2;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> levelOrderTraversal(TreeNode* root) {
|
||||||
|
std::vector<std::vector<int>> result;
|
||||||
|
|
||||||
|
if (root == nullptr) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MyQueue myQueue;
|
||||||
|
myQueue.push(root);
|
||||||
|
|
||||||
|
while (!myQueue.empty()) {
|
||||||
|
int size = myQueue.size();
|
||||||
|
std::vector<int> current_level;
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
TreeNode* current = myQueue.front();
|
||||||
|
myQueue.pop();
|
||||||
|
std::cout << current->val << " ";
|
||||||
|
|
||||||
|
if (current != nullptr) {
|
||||||
|
current_level.push_back(current->val);
|
||||||
|
|
||||||
|
if (current->left != nullptr) {
|
||||||
|
myQueue.push(current->left);
|
||||||
|
}
|
||||||
|
if (current->right != nullptr) {
|
||||||
|
myQueue.push(current->right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_back(current_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
/* test case 1
|
||||||
|
* 1
|
||||||
|
* 2 3
|
||||||
|
* 4 5 6 7
|
||||||
|
*/
|
||||||
|
TreeNode* root1 = new TreeNode(1);
|
||||||
|
root1->left = new TreeNode(2);
|
||||||
|
root1->right = new TreeNode(3);
|
||||||
|
root1->left->left = new TreeNode(4);
|
||||||
|
root1->left->right = new TreeNode(5);
|
||||||
|
root1->right->left = new TreeNode(6);
|
||||||
|
root1->right->right = new TreeNode(7);
|
||||||
|
|
||||||
|
std::cout << "Level Order Traversal: ";
|
||||||
|
levelOrderTraversal(root1);
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
/* test case 2
|
||||||
|
* 1
|
||||||
|
* 2 3
|
||||||
|
* 4 5 6 7
|
||||||
|
* 8 9
|
||||||
|
*/
|
||||||
|
TreeNode* root2 = new TreeNode(1);
|
||||||
|
root2->left = new TreeNode(2);
|
||||||
|
root2->right = new TreeNode(3);
|
||||||
|
root2->left->left = new TreeNode(4);
|
||||||
|
root2->left->right = new TreeNode(5);
|
||||||
|
root2->right->left = new TreeNode(6);
|
||||||
|
root2->right->right = new TreeNode(7);
|
||||||
|
root2->right->right->left = new TreeNode(8);
|
||||||
|
root2->right->right->right = new TreeNode(9);
|
||||||
|
|
||||||
|
std::cout << "Level Order Traversal: ";
|
||||||
|
levelOrderTraversal(root2);
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
/* test case 3
|
||||||
|
* 1
|
||||||
|
* 2 3
|
||||||
|
* 4 5 6 7
|
||||||
|
* 8
|
||||||
|
*/
|
||||||
|
TreeNode* root3 = new TreeNode(1);
|
||||||
|
root3->left = new TreeNode(2);
|
||||||
|
root3->right = new TreeNode(3);
|
||||||
|
root3->left->left = new TreeNode(4);
|
||||||
|
root3->left->right = new TreeNode(5);
|
||||||
|
root3->right->left = new TreeNode(6);
|
||||||
|
root3->right->right = new TreeNode(7);
|
||||||
|
root3->left->left->left = new TreeNode(8);
|
||||||
|
|
||||||
|
std::cout << "Level Order Traversal: ";
|
||||||
|
levelOrderTraversal(root3);
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
140
labs/11_stacks_and_queues/levelOrder_sol2.cpp
Normal file
140
labs/11_stacks_and_queues/levelOrder_sol2.cpp
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
// Definition for a binary tree node.
|
||||||
|
class TreeNode {
|
||||||
|
public:
|
||||||
|
int val;
|
||||||
|
TreeNode* left;
|
||||||
|
TreeNode* right;
|
||||||
|
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MyQueue {
|
||||||
|
public:
|
||||||
|
MyQueue() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(TreeNode* node) {
|
||||||
|
myList.push_back(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop() {
|
||||||
|
myList.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode* front() {
|
||||||
|
return myList.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() {
|
||||||
|
return myList.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() {
|
||||||
|
return myList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::list<TreeNode*> myList;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> levelOrderTraversal(TreeNode* root) {
|
||||||
|
std::vector<std::vector<int>> result;
|
||||||
|
|
||||||
|
if (root == nullptr) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MyQueue myQueue;
|
||||||
|
myQueue.push(root);
|
||||||
|
|
||||||
|
while (!myQueue.empty()) {
|
||||||
|
int size = myQueue.size();
|
||||||
|
std::vector<int> current_level;
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
TreeNode* current = myQueue.front();
|
||||||
|
myQueue.pop();
|
||||||
|
std::cout << current->val << " ";
|
||||||
|
|
||||||
|
if (current != nullptr) {
|
||||||
|
current_level.push_back(current->val);
|
||||||
|
|
||||||
|
if (current->left != nullptr) {
|
||||||
|
myQueue.push(current->left);
|
||||||
|
}
|
||||||
|
if (current->right != nullptr) {
|
||||||
|
myQueue.push(current->right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_back(current_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
/* test case 1
|
||||||
|
* 1
|
||||||
|
* 2 3
|
||||||
|
* 4 5 6 7
|
||||||
|
*/
|
||||||
|
TreeNode* root1 = new TreeNode(1);
|
||||||
|
root1->left = new TreeNode(2);
|
||||||
|
root1->right = new TreeNode(3);
|
||||||
|
root1->left->left = new TreeNode(4);
|
||||||
|
root1->left->right = new TreeNode(5);
|
||||||
|
root1->right->left = new TreeNode(6);
|
||||||
|
root1->right->right = new TreeNode(7);
|
||||||
|
|
||||||
|
std::cout << "Level Order Traversal: ";
|
||||||
|
levelOrderTraversal(root1);
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
/* test case 2
|
||||||
|
* 1
|
||||||
|
* 2 3
|
||||||
|
* 4 5 6 7
|
||||||
|
* 8 9
|
||||||
|
*/
|
||||||
|
TreeNode* root2 = new TreeNode(1);
|
||||||
|
root2->left = new TreeNode(2);
|
||||||
|
root2->right = new TreeNode(3);
|
||||||
|
root2->left->left = new TreeNode(4);
|
||||||
|
root2->left->right = new TreeNode(5);
|
||||||
|
root2->right->left = new TreeNode(6);
|
||||||
|
root2->right->right = new TreeNode(7);
|
||||||
|
root2->right->right->left = new TreeNode(8);
|
||||||
|
root2->right->right->right = new TreeNode(9);
|
||||||
|
|
||||||
|
std::cout << "Level Order Traversal: ";
|
||||||
|
levelOrderTraversal(root2);
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
/* test case 3
|
||||||
|
* 1
|
||||||
|
* 2 3
|
||||||
|
* 4 5 6 7
|
||||||
|
* 8
|
||||||
|
*/
|
||||||
|
TreeNode* root3 = new TreeNode(1);
|
||||||
|
root3->left = new TreeNode(2);
|
||||||
|
root3->right = new TreeNode(3);
|
||||||
|
root3->left->left = new TreeNode(4);
|
||||||
|
root3->left->right = new TreeNode(5);
|
||||||
|
root3->right->left = new TreeNode(6);
|
||||||
|
root3->right->right = new TreeNode(7);
|
||||||
|
root3->left->left->left = new TreeNode(8);
|
||||||
|
|
||||||
|
std::cout << "Level Order Traversal: ";
|
||||||
|
levelOrderTraversal(root3);
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
126
labs/11_stacks_and_queues/levelOrder_sol3.cpp
Normal file
126
labs/11_stacks_and_queues/levelOrder_sol3.cpp
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
// Definition for a binary tree node.
|
||||||
|
class TreeNode {
|
||||||
|
public:
|
||||||
|
int val;
|
||||||
|
TreeNode* left;
|
||||||
|
TreeNode* right;
|
||||||
|
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> levelOrderTraversal(TreeNode* root) {
|
||||||
|
std::vector<std::vector<int>> result;
|
||||||
|
|
||||||
|
if (root == nullptr) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::queue<TreeNode*> myQueue;
|
||||||
|
myQueue.push(root);
|
||||||
|
|
||||||
|
while (!myQueue.empty()) {
|
||||||
|
int size = myQueue.size();
|
||||||
|
std::vector<int> current_level;
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
TreeNode* current = myQueue.front();
|
||||||
|
myQueue.pop();
|
||||||
|
std::cout << current->val << " ";
|
||||||
|
|
||||||
|
if (current != nullptr) {
|
||||||
|
current_level.push_back(current->val);
|
||||||
|
|
||||||
|
if (current->left != nullptr) {
|
||||||
|
myQueue.push(current->left);
|
||||||
|
}
|
||||||
|
if (current->right != nullptr) {
|
||||||
|
myQueue.push(current->right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_back(current_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteTree(TreeNode* root){
|
||||||
|
if(root==nullptr){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(root->left!=nullptr){
|
||||||
|
deleteTree(root->left);
|
||||||
|
}
|
||||||
|
if(root->right!=nullptr){
|
||||||
|
deleteTree(root->right);
|
||||||
|
}
|
||||||
|
delete root;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
/* test case 1
|
||||||
|
* 1
|
||||||
|
* 2 3
|
||||||
|
* 4 5 6 7
|
||||||
|
*/
|
||||||
|
TreeNode* root1 = new TreeNode(1);
|
||||||
|
root1->left = new TreeNode(2);
|
||||||
|
root1->right = new TreeNode(3);
|
||||||
|
root1->left->left = new TreeNode(4);
|
||||||
|
root1->left->right = new TreeNode(5);
|
||||||
|
root1->right->left = new TreeNode(6);
|
||||||
|
root1->right->right = new TreeNode(7);
|
||||||
|
|
||||||
|
std::cout << "Level Order Traversal: ";
|
||||||
|
levelOrderTraversal(root1);
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
/* test case 2
|
||||||
|
* 1
|
||||||
|
* 2 3
|
||||||
|
* 4 5 6 7
|
||||||
|
* 8 9
|
||||||
|
*/
|
||||||
|
TreeNode* root2 = new TreeNode(1);
|
||||||
|
root2->left = new TreeNode(2);
|
||||||
|
root2->right = new TreeNode(3);
|
||||||
|
root2->left->left = new TreeNode(4);
|
||||||
|
root2->left->right = new TreeNode(5);
|
||||||
|
root2->right->left = new TreeNode(6);
|
||||||
|
root2->right->right = new TreeNode(7);
|
||||||
|
root2->right->right->left = new TreeNode(8);
|
||||||
|
root2->right->right->right = new TreeNode(9);
|
||||||
|
|
||||||
|
std::cout << "Level Order Traversal: ";
|
||||||
|
levelOrderTraversal(root2);
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
/* test case 3
|
||||||
|
* 1
|
||||||
|
* 2 3
|
||||||
|
* 4 5 6 7
|
||||||
|
* 8
|
||||||
|
*/
|
||||||
|
TreeNode* root3 = new TreeNode(1);
|
||||||
|
root3->left = new TreeNode(2);
|
||||||
|
root3->right = new TreeNode(3);
|
||||||
|
root3->left->left = new TreeNode(4);
|
||||||
|
root3->left->right = new TreeNode(5);
|
||||||
|
root3->right->left = new TreeNode(6);
|
||||||
|
root3->right->right = new TreeNode(7);
|
||||||
|
root3->left->left->left = new TreeNode(8);
|
||||||
|
|
||||||
|
std::cout << "Level Order Traversal: ";
|
||||||
|
levelOrderTraversal(root3);
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
deleteTree(root1);
|
||||||
|
deleteTree(root2);
|
||||||
|
deleteTree(root3);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Lab 11 — Hash Tables
|
# Lab 12 — Hash Tables
|
||||||
|
|
||||||
<!--In this lab, you will first experiment with our hash table implementation of a set. The key differences between the ds_set class (based on a binary search tree) and the ds_hashset class (based on a hash table, of course), are the performance of insert/find/erase: O(log n) vs. O(1), and the order that the elements are traversed using iterators: the set was in order, while the hashset is in no apparent order.-->
|
<!--In this lab, you will first experiment with our hash table implementation of a set. The key differences between the ds_set class (based on a binary search tree) and the ds_hashset class (based on a hash table, of course), are the performance of insert/find/erase: O(log n) vs. O(1), and the order that the elements are traversed using iterators: the set was in order, while the hashset is in no apparent order.-->
|
||||||
|
|
||||||
BIN
labs/12_hash_tables/a.out
Executable file
BIN
labs/12_hash_tables/a.out
Executable file
Binary file not shown.
90
labs/12_hash_tables/find_max_average.cpp
Normal file
90
labs/12_hash_tables/find_max_average.cpp
Normal file
File diff suppressed because one or more lines are too long
95
labs/12_hash_tables/happy_number_separate_chaining_sol.cpp
Normal file
95
labs/12_hash_tables/happy_number_separate_chaining_sol.cpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
// Solve the problem using separate chaining.
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// this table can have at most 1024 keys
|
||||||
|
#define TABLE_SIZE 1024
|
||||||
|
|
||||||
|
class Node {
|
||||||
|
public:
|
||||||
|
int number;
|
||||||
|
Node* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// search the hash table and see if we can find this num.
|
||||||
|
bool identify(int num, Node** table){
|
||||||
|
int key = abs(num%TABLE_SIZE);
|
||||||
|
// search num in table[key];
|
||||||
|
Node* node = table[key];
|
||||||
|
while(node!=NULL){
|
||||||
|
if(node->number == num){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
// if not found, return false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add num into the hash table
|
||||||
|
void add(int num, Node** table){
|
||||||
|
int key = abs(num%TABLE_SIZE);
|
||||||
|
Node* node = new Node;
|
||||||
|
// insert num and index into table[key]
|
||||||
|
// if this is the first node
|
||||||
|
if(table[key]==NULL){
|
||||||
|
node->number = num;
|
||||||
|
node->next = NULL;
|
||||||
|
table[key] = node;
|
||||||
|
}else{
|
||||||
|
// if this is not the first node
|
||||||
|
node->number = num;
|
||||||
|
node->next = table[key];
|
||||||
|
table[key] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int replace(int n){
|
||||||
|
int digit;
|
||||||
|
int result=0;
|
||||||
|
while(n>0){
|
||||||
|
digit = (n%10);
|
||||||
|
result += digit * digit;
|
||||||
|
n = n/10;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isHappy(int n) {
|
||||||
|
int newN = n;
|
||||||
|
Node* hash_table[TABLE_SIZE];
|
||||||
|
for(int i=0;i<TABLE_SIZE;i++){
|
||||||
|
hash_table[i]=NULL;
|
||||||
|
}
|
||||||
|
while(1){
|
||||||
|
newN = replace(newN);
|
||||||
|
if(newN==1){
|
||||||
|
return true;
|
||||||
|
}else{
|
||||||
|
// if we can find it, this is going to be an infinite loop
|
||||||
|
if(identify(newN, hash_table)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// can't find it, push it in the map first
|
||||||
|
add(newN, hash_table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Test cases
|
||||||
|
// 2, 4, 5, 6, 17, 18, 20 are not happy numbers.
|
||||||
|
// 1, 7, 10, 13, 19, 23, 28, 68 are not happy numbers.
|
||||||
|
|
||||||
|
int testCases[] = {2,4,5,6,17,18,20,1,7,10,13,19,23,28,68};
|
||||||
|
|
||||||
|
for (int n : testCases) {
|
||||||
|
if (isHappy(n)) {
|
||||||
|
std::cout << n << " is a happy number." << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << n << " is not a happy number." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
50
labs/12_hash_tables/happy_number_set_sol.cpp
Normal file
50
labs/12_hash_tables/happy_number_set_sol.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
int replace(int n){
|
||||||
|
int digit;
|
||||||
|
int result=0;
|
||||||
|
while(n>0){
|
||||||
|
digit = (n%10);
|
||||||
|
result += digit * digit;
|
||||||
|
n = n/10;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isHappy(int n) {
|
||||||
|
int newN = n;
|
||||||
|
std::unordered_set<int> set1;
|
||||||
|
while(1){
|
||||||
|
newN = replace(newN);
|
||||||
|
if(newN==1){
|
||||||
|
return true;
|
||||||
|
}else{
|
||||||
|
// if we can find it, this is going to be an infinite loop
|
||||||
|
if(set1.find(newN)!=set1.end()){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// can't find it, insert it in the set first
|
||||||
|
set1.insert(newN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Test cases
|
||||||
|
// 2, 4, 5, 6, 17, 18, 20 are not happy numbers.
|
||||||
|
// 1, 7, 10, 13, 19, 23, 28, 68 are not happy numbers.
|
||||||
|
|
||||||
|
int testCases[] = {2,4,5,6,17,18,20,1,7,10,13,19,23,28,68};
|
||||||
|
|
||||||
|
for (int n : testCases) {
|
||||||
|
if (isHappy(n)) {
|
||||||
|
std::cout << n << " is a happy number." << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << n << " is not a happy number." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
260
labs/12_hash_tables/test/ds_hashset.h
Normal file
260
labs/12_hash_tables/test/ds_hashset.h
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
#ifndef ds_hashset_h_
|
||||||
|
#define ds_hashset_h_
|
||||||
|
// The set class as a hash table instead of a binary search tree. The
|
||||||
|
// primary external difference between ds_set and ds_hashset is that
|
||||||
|
// the iterators do not step through the hashset in any meaningful
|
||||||
|
// order. It is just the order imposed by the hash function.
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// The ds_hashset is templated over both the type of key and the type
|
||||||
|
// of the hash function, a function object.
|
||||||
|
template < class KeyType, class HashFunc >
|
||||||
|
class ds_hashset {
|
||||||
|
private:
|
||||||
|
typedef typename std::list<KeyType>::iterator hash_list_itr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// =================================================================
|
||||||
|
// THE ITERATOR CLASS
|
||||||
|
// Defined as a nested class and thus is not separately templated.
|
||||||
|
|
||||||
|
class iterator {
|
||||||
|
public:
|
||||||
|
friend class ds_hashset; // allows access to private variables
|
||||||
|
private:
|
||||||
|
|
||||||
|
// ITERATOR REPRESENTATION
|
||||||
|
ds_hashset* m_hs;
|
||||||
|
int m_index; // current index in the hash table
|
||||||
|
hash_list_itr m_list_itr; // current iterator at the current index
|
||||||
|
|
||||||
|
private:
|
||||||
|
// private constructors for use by the ds_hashset only
|
||||||
|
iterator(ds_hashset * hs) : m_hs(hs), m_index(-1) {}
|
||||||
|
iterator(ds_hashset* hs, int index, hash_list_itr loc)
|
||||||
|
: m_hs(hs), m_index(index), m_list_itr(loc) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Ordinary constructors & assignment operator
|
||||||
|
iterator() : m_hs(0), m_index(-1) {}
|
||||||
|
iterator(iterator const& itr)
|
||||||
|
: m_hs(itr.m_hs), m_index(itr.m_index), m_list_itr(itr.m_list_itr) {}
|
||||||
|
iterator& operator=(const iterator& old) {
|
||||||
|
m_hs = old.m_hs;
|
||||||
|
m_index = old.m_index;
|
||||||
|
m_list_itr = old.m_list_itr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The dereference operator need only worry about the current
|
||||||
|
// list iterator, and does not need to check the current index.
|
||||||
|
const KeyType& operator*() const { return *m_list_itr; }
|
||||||
|
|
||||||
|
// The comparison operators must account for the list iterators
|
||||||
|
// being unassigned at the end.
|
||||||
|
friend bool operator== (const iterator& lft, const iterator& rgt)
|
||||||
|
{ return lft.m_hs == rgt.m_hs && lft.m_index == rgt.m_index &&
|
||||||
|
(lft.m_index == -1 || lft.m_list_itr == rgt.m_list_itr); }
|
||||||
|
friend bool operator!= (const iterator& lft, const iterator& rgt)
|
||||||
|
{ return lft.m_hs != rgt.m_hs || lft.m_index != rgt.m_index ||
|
||||||
|
(lft.m_index != -1 && lft.m_list_itr != rgt.m_list_itr); }
|
||||||
|
// increment and decrement
|
||||||
|
iterator& operator++() {
|
||||||
|
this->next();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
iterator operator++(int) {
|
||||||
|
iterator temp(*this);
|
||||||
|
this->next();
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
iterator & operator--() {
|
||||||
|
this->prev();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
iterator operator--(int) {
|
||||||
|
iterator temp(*this);
|
||||||
|
this->prev();
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Find the next entry in the table
|
||||||
|
void next() {
|
||||||
|
++ m_list_itr; // next item in the list
|
||||||
|
|
||||||
|
// If we are at the end of this list
|
||||||
|
if (m_list_itr == m_hs->m_table[m_index].end()) {
|
||||||
|
// Find the next non-empty list in the table
|
||||||
|
for (++m_index;
|
||||||
|
m_index < int(m_hs->m_table.size()) && m_hs->m_table[m_index].empty();
|
||||||
|
++m_index) {}
|
||||||
|
|
||||||
|
// If one is found, assign the m_list_itr to the start
|
||||||
|
if (m_index != int(m_hs->m_table.size()))
|
||||||
|
m_list_itr = m_hs->m_table[m_index].begin();
|
||||||
|
|
||||||
|
// Otherwise, we are at the end
|
||||||
|
else
|
||||||
|
m_index = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the previous entry in the table
|
||||||
|
void prev() {
|
||||||
|
// If we aren't at the start of the current list, just decrement
|
||||||
|
// the list iterator
|
||||||
|
if (m_list_itr != m_hs->m_table[m_index].begin())
|
||||||
|
m_list_itr -- ;
|
||||||
|
|
||||||
|
else {
|
||||||
|
// Otherwise, back down the table until the previous
|
||||||
|
// non-empty list in the table is found
|
||||||
|
for (--m_index; m_index >= 0 && m_hs->m_table[m_index].empty(); --m_index) {}
|
||||||
|
|
||||||
|
// Go to the last entry in the list.
|
||||||
|
m_list_itr = m_hs->m_table[m_index].begin();
|
||||||
|
hash_list_itr p = m_list_itr; ++p;
|
||||||
|
for (; p != m_hs->m_table[m_index].end(); ++p, ++m_list_itr) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// end of ITERATOR CLASS
|
||||||
|
// =================================================================
|
||||||
|
private:
|
||||||
|
// =================================================================
|
||||||
|
// HASH SET REPRESENTATION
|
||||||
|
std::vector< std::list<KeyType> > m_table; // actual table
|
||||||
|
HashFunc m_hash; // hash function
|
||||||
|
unsigned int m_size; // number of keys
|
||||||
|
|
||||||
|
public:
|
||||||
|
// =================================================================
|
||||||
|
// HASH SET IMPLEMENTATION
|
||||||
|
|
||||||
|
// Constructor for the table accepts the size of the table. Default
|
||||||
|
// constructor for the hash function object is implicitly used.
|
||||||
|
ds_hashset(unsigned int init_size = 10) : m_table(init_size), m_size(0) {}
|
||||||
|
|
||||||
|
// Copy constructor just uses the member function copy constructors.
|
||||||
|
ds_hashset(const ds_hashset<KeyType, HashFunc>& old)
|
||||||
|
: m_table(old.m_table), m_size(old.m_size) {}
|
||||||
|
|
||||||
|
~ds_hashset() {}
|
||||||
|
|
||||||
|
ds_hashset& operator=(const ds_hashset<KeyType,HashFunc>& old) {
|
||||||
|
if (&old != this) {
|
||||||
|
this->m_table = old.m_table;
|
||||||
|
this->m_size = old.m_size;
|
||||||
|
this->m_hash = old.m_hash;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int size() const { return m_size; }
|
||||||
|
|
||||||
|
|
||||||
|
// Insert the key if it is not already there.
|
||||||
|
std::pair< iterator, bool > insert(KeyType const& key) {
|
||||||
|
const float LOAD_FRACTION_FOR_RESIZE = 1.25;
|
||||||
|
|
||||||
|
if (m_size >= LOAD_FRACTION_FOR_RESIZE * m_table.size())
|
||||||
|
this->resize_table(2*m_table.size()+1);
|
||||||
|
|
||||||
|
// Implement this function for Lab 11, Checkpoint 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the key, using hash function, indexing and list find
|
||||||
|
iterator find(const KeyType& key) {
|
||||||
|
unsigned int hash_value = m_hash(key);
|
||||||
|
unsigned int index = hash_value % m_table.size();
|
||||||
|
hash_list_itr p = std::find(m_table[index].begin(),
|
||||||
|
m_table[index].end(), key);
|
||||||
|
if (p == m_table[index].end())
|
||||||
|
return this->end();
|
||||||
|
else
|
||||||
|
return iterator(this, index, p);
|
||||||
|
}
|
||||||
|
// Erase the key
|
||||||
|
int erase(const KeyType& key) {
|
||||||
|
// Find the key and use the erase iterator function.
|
||||||
|
iterator p = find(key);
|
||||||
|
if (p == end())
|
||||||
|
return 0;
|
||||||
|
else {
|
||||||
|
erase(p);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase at the iterator
|
||||||
|
void erase(iterator p) {
|
||||||
|
m_table[ p.m_index ].erase(p.m_list_itr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the first entry in the table and create an associated iterator
|
||||||
|
iterator begin() {
|
||||||
|
|
||||||
|
// Implement this function for Lab 11, Checkpoint 2, Part 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an end iterator.
|
||||||
|
iterator end() {
|
||||||
|
iterator p(this);
|
||||||
|
p.m_index = -1;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A public print utility.
|
||||||
|
void print(std::ostream & ostr) {
|
||||||
|
for (unsigned int i=0; i<m_table.size(); ++i) {
|
||||||
|
ostr << i << ": ";
|
||||||
|
for (hash_list_itr p = m_table[i].begin(); p != m_table[i].end(); ++p)
|
||||||
|
ostr << ' ' << *p;
|
||||||
|
ostr << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// resize the table with the same values but twice as many buckets
|
||||||
|
void resize_table(unsigned int new_size) {
|
||||||
|
|
||||||
|
// Implement this function for Lab 11, Checkpoint 2, Part 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
155
labs/12_hash_tables/test/test_ds_hashset.cpp
Normal file
155
labs/12_hash_tables/test/test_ds_hashset.cpp
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include "ds_hashset.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Wrapping a class around a function turns a function into a functor
|
||||||
|
// (We'll talk about this more in Lecture 21. You can just ignore
|
||||||
|
// this wrapper part for now.)
|
||||||
|
class hash_string_obj {
|
||||||
|
public:
|
||||||
|
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
// EXPERIMENT WITH THE HASH FUNCTION FOR CHECKPOINT 1, PART 2
|
||||||
|
|
||||||
|
unsigned int operator() ( const std::string& key ) const {
|
||||||
|
// This implementation comes from
|
||||||
|
// http://www.partow.net/programming/hashfunctions/
|
||||||
|
//
|
||||||
|
// This is a general-purpose, very good hash function for strings.
|
||||||
|
unsigned int hash = 1315423911;
|
||||||
|
for(unsigned int i = 0; i < key.length(); i++)
|
||||||
|
hash ^= ((hash << 5) + key[i] + (hash >> 2));
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef ds_hashset<std::string, hash_string_obj> ds_hashset_type;
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
|
// CODE TO TEST CHECKPOINT 1, PART 1
|
||||||
|
ds_hashset_type a;
|
||||||
|
ds_hashset_type set1;
|
||||||
|
std::pair< ds_hashset_type::iterator, bool > insert_result;
|
||||||
|
|
||||||
|
std::string to_insert = std::string("hello");
|
||||||
|
insert_result = set1.insert( to_insert );
|
||||||
|
assert( insert_result.second );
|
||||||
|
|
||||||
|
insert_result = set1.insert( std::string("good-bye") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
|
||||||
|
insert_result = set1.insert( std::string("friend") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
|
||||||
|
insert_result = set1.insert( std::string("abc") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
|
||||||
|
insert_result = set1.insert( std::string("puppy") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
|
||||||
|
insert_result = set1.insert( std::string("zebra") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
|
||||||
|
insert_result = set1.insert( std::string("daddy") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
|
||||||
|
insert_result = set1.insert( std::string("puppy") );
|
||||||
|
assert( !insert_result.second && * insert_result.first == std::string("puppy") );
|
||||||
|
|
||||||
|
std::cout << "The set size is " << set1.size() << '\n'
|
||||||
|
<< "Here is the table: \n";
|
||||||
|
set1.print( std::cout );
|
||||||
|
|
||||||
|
ds_hashset_type::iterator p;
|
||||||
|
p = set1.find( "foo" );
|
||||||
|
if ( p == set1.end() )
|
||||||
|
std::cout << "\"foo\" is not in the set\n";
|
||||||
|
else
|
||||||
|
std::cout << "\"foo\" is in the set\n"
|
||||||
|
<< "The iterator points to " << *p << std::endl;
|
||||||
|
|
||||||
|
p = set1.find("puppy");
|
||||||
|
if ( p == set1.end() )
|
||||||
|
std::cout << "\"puppy\" is not in the set\n";
|
||||||
|
else
|
||||||
|
std::cout << "\"puppy\" is in the set\n"
|
||||||
|
<< "The iterator points to " << *p << std::endl;
|
||||||
|
|
||||||
|
p = set1.find("daddy");
|
||||||
|
if ( p == set1.end() )
|
||||||
|
std::cout << "\"daddy\" is not in the set\n";
|
||||||
|
else
|
||||||
|
std::cout << "\"daddy\" is in the set\n"
|
||||||
|
<< "The iterator points to " << *p << std::endl;
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
|
// CODE TO TEST CHECKPOINT 2, PART 1
|
||||||
|
/*
|
||||||
|
p = set1.begin();
|
||||||
|
std::cout << "\nHere is the result of iterating: \n";
|
||||||
|
for ( p = set1.begin(); p != set1.end(); ++p )
|
||||||
|
std::cout << *p << '\n';
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------
|
||||||
|
// CODE TO TEST CHECKPOINT 2, PART 2
|
||||||
|
/*
|
||||||
|
ds_hashset_type set2( set1 );
|
||||||
|
std::cout << "set1.size() = " << set1.size() << ", set2.size() = " << set2.size() << std::endl;
|
||||||
|
|
||||||
|
// Now add more stuff to set2. This should trigger a resize given the default settings.
|
||||||
|
insert_result = set2.insert( std::string("ardvark") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
insert_result = set2.insert( std::string("baseball") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
insert_result = set2.insert( std::string("football") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
insert_result = set2.insert( std::string("gymnastics") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
insert_result = set2.insert( std::string("dance") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
insert_result = set2.insert( std::string("swimming") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
insert_result = set2.insert( std::string("track") );
|
||||||
|
assert( insert_result.second );
|
||||||
|
|
||||||
|
std::cout << "\nAfter seven more inserts:\n"
|
||||||
|
<< "set1.size() = " << set1.size() << ", set2.size() = " << set2.size() << "\n"
|
||||||
|
<< "\nThe contents of set2:" << std::endl;
|
||||||
|
set2.print(std::cout);
|
||||||
|
std::cout << "The results of iterating:\n";
|
||||||
|
for ( p = set2.begin(); p != set2.end(); ++p )
|
||||||
|
std::cout << *p << '\n';
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// OTHER TEST CODE
|
||||||
|
/*
|
||||||
|
// Now test erase
|
||||||
|
int num = set2.erase( std::string("hello") );
|
||||||
|
std::cout << "Tried erase \"hello\" and got num (should be 1) = " << num << std::endl;
|
||||||
|
num = set2.erase( std::string("abc") );
|
||||||
|
std::cout << "Tried erase \"abc\" and got num (should be 1) = " << num << std::endl;
|
||||||
|
num = set2.erase( std::string("hello") );
|
||||||
|
std::cout << "Tried erase \"hello\" and got num (should be 0) = " << num << std::endl;
|
||||||
|
num = set2.erase( std::string("football") );
|
||||||
|
std::cout << "Tried erase \"football\" and got num (should be 1) = " << num << std::endl;
|
||||||
|
num = set2.erase( std::string("friend") );
|
||||||
|
std::cout << "Tried erase \"friend\" and got num (should be 1) = " << num
|
||||||
|
<< "\nHere are the final contents of set2:" << std::endl;
|
||||||
|
set2.print(std::cout);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
labs/12_hash_tables/test2/a.out
Executable file
BIN
labs/12_hash_tables/test2/a.out
Executable file
Binary file not shown.
95
labs/12_hash_tables/test2/happy_number_separate_chaining.cpp
Normal file
95
labs/12_hash_tables/test2/happy_number_separate_chaining.cpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
// Solve the problem using separate chaining.
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// this table can have at most 1024 keys
|
||||||
|
#define TABLE_SIZE 1024
|
||||||
|
|
||||||
|
class Node {
|
||||||
|
public:
|
||||||
|
int number;
|
||||||
|
Node* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// search the hash table and see if we can find this num.
|
||||||
|
bool identify(int num, Node** table){
|
||||||
|
int key = abs(num%TABLE_SIZE);
|
||||||
|
// search num in table[key];
|
||||||
|
Node* node = table[key];
|
||||||
|
while(node!=NULL){
|
||||||
|
if(node->number == num){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
// if not found, return false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add num into the hash table
|
||||||
|
void add(int num, Node** table){
|
||||||
|
int key = abs(num%TABLE_SIZE);
|
||||||
|
Node* node = new Node;
|
||||||
|
// insert num and index into table[key]
|
||||||
|
// if this is the first node
|
||||||
|
if(table[key]==NULL){
|
||||||
|
node->number = num;
|
||||||
|
node->next = NULL;
|
||||||
|
table[key] = node;
|
||||||
|
}else{
|
||||||
|
// if this is not the first node
|
||||||
|
node->number = num;
|
||||||
|
node->next = table[key];
|
||||||
|
table[key] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int replace(int n){
|
||||||
|
int digit;
|
||||||
|
int result=0;
|
||||||
|
while(n>0){
|
||||||
|
digit = (n%10);
|
||||||
|
result += digit * digit;
|
||||||
|
n = n/10;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isHappy(int n) {
|
||||||
|
int newN = n;
|
||||||
|
Node* hash_table[TABLE_SIZE];
|
||||||
|
for(int i=0;i<TABLE_SIZE;i++){
|
||||||
|
hash_table[i]=NULL;
|
||||||
|
}
|
||||||
|
while(1){
|
||||||
|
newN = replace(newN);
|
||||||
|
if(newN==1){
|
||||||
|
return true;
|
||||||
|
}else{
|
||||||
|
// if we can find it, this is going to be an infinite loop
|
||||||
|
if(identify(newN, hash_table)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// can't find it, push it in the map first
|
||||||
|
add(newN, hash_table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Test cases
|
||||||
|
// 2, 4, 5, 6, 17, 18, 20 are not happy numbers.
|
||||||
|
// 1, 7, 10, 13, 19, 23, 28, 68 are not happy numbers.
|
||||||
|
|
||||||
|
int testCases[] = {2,4,5,6,17,18,20,1,7,10,13,19,23,28,68};
|
||||||
|
|
||||||
|
for (int n : testCases) {
|
||||||
|
if (isHappy(n)) {
|
||||||
|
std::cout << n << " is a happy number." << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << n << " is not a happy number." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
labs/12_hash_tables/test3/a.out
Executable file
BIN
labs/12_hash_tables/test3/a.out
Executable file
Binary file not shown.
50
labs/12_hash_tables/test3/happy_number_set_sol.cpp
Normal file
50
labs/12_hash_tables/test3/happy_number_set_sol.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
int replace(int n){
|
||||||
|
int digit;
|
||||||
|
int result=0;
|
||||||
|
while(n>0){
|
||||||
|
digit = (n%10);
|
||||||
|
result += digit * digit;
|
||||||
|
n = n/10;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isHappy(int n) {
|
||||||
|
int newN = n;
|
||||||
|
std::unordered_set<int> set1;
|
||||||
|
while(1){
|
||||||
|
newN = replace(newN);
|
||||||
|
if(newN==1){
|
||||||
|
return true;
|
||||||
|
}else{
|
||||||
|
// if we can find it, this is going to be an infinite loop
|
||||||
|
if(set1.find(newN)!=set1.end()){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// can't find it, insert it in the set first
|
||||||
|
set1.insert(newN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Test cases
|
||||||
|
// 2, 4, 5, 6, 17, 18, 20 are not happy numbers.
|
||||||
|
// 1, 7, 10, 13, 19, 23, 28, 68 are not happy numbers.
|
||||||
|
|
||||||
|
int testCases[] = {2,4,5,6,17,18,20,1,7,10,13,19,23,28,68};
|
||||||
|
|
||||||
|
for (int n : testCases) {
|
||||||
|
if (isHappy(n)) {
|
||||||
|
std::cout << n << " is a happy number." << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << n << " is not a happy number." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
BIN
labs/12_hash_tables/test4/a.out
Executable file
BIN
labs/12_hash_tables/test4/a.out
Executable file
Binary file not shown.
117
labs/12_hash_tables/test4/test_longest_consecutive_sequence.cpp
Normal file
117
labs/12_hash_tables/test4/test_longest_consecutive_sequence.cpp
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define TABLE_SIZE 1024
|
||||||
|
|
||||||
|
class Node{
|
||||||
|
public:
|
||||||
|
int number;
|
||||||
|
Node* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// insert num into table
|
||||||
|
void insert(int num, Node** table){
|
||||||
|
int key;
|
||||||
|
key = abs(num%TABLE_SIZE); // key will be something in between 0 and (TABLE_SIZE-1); num can be negative
|
||||||
|
if(table[key] == nullptr){
|
||||||
|
// create the first node for this linked list
|
||||||
|
Node* node;
|
||||||
|
node = new Node;
|
||||||
|
node->number = num;
|
||||||
|
node->next = nullptr;
|
||||||
|
table[key] = node;
|
||||||
|
}else{
|
||||||
|
// insert a node to the beginning of this linked list
|
||||||
|
Node* node;
|
||||||
|
node = new Node;
|
||||||
|
node->number = num;
|
||||||
|
node->next = table[key];
|
||||||
|
table[key] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// search the hash table and see if we can find this num.
|
||||||
|
bool identify(int num, Node** table){
|
||||||
|
int key = abs(num%TABLE_SIZE);
|
||||||
|
// search num in table[key];
|
||||||
|
Node* node = table[key];
|
||||||
|
while(node != nullptr){
|
||||||
|
if(node->number == num){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
// if not found, return false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Question: why is this an O(n) solution when we have a nested loop? Because the inner while loop will only be used if *itr1 is the beginning of the sequence, which means each element will only be visited 2 or 3 times.
|
||||||
|
int longestConsecutive(std::vector<int>& nums) {
|
||||||
|
int len=0;
|
||||||
|
Node* hash_table[TABLE_SIZE];
|
||||||
|
// initialize the table
|
||||||
|
for(int i=0;i<TABLE_SIZE;i++){
|
||||||
|
hash_table[i] = nullptr;
|
||||||
|
}
|
||||||
|
int size = nums.size();
|
||||||
|
if(size == 0){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// store unique elements in nums in set1
|
||||||
|
for(int i=0;i<nums.size();i++){
|
||||||
|
insert(nums[i], hash_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i=0;
|
||||||
|
Node* current = hash_table[i];
|
||||||
|
// if we reach here, then there is at least one Node in the hash table.
|
||||||
|
// find the first non-NULL Node.
|
||||||
|
while(current == nullptr){
|
||||||
|
i++;
|
||||||
|
current = hash_table[i];
|
||||||
|
}
|
||||||
|
// traverse the hash table
|
||||||
|
while(current!=nullptr){
|
||||||
|
// if (current->num-1) can't be found
|
||||||
|
if(identify(current->number - 1, hash_table)){
|
||||||
|
int x = current->number + 1;
|
||||||
|
// now that current->num is the beginning of a sequence, how about current->num + 1?
|
||||||
|
while(identify(x, hash_table)){
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
// when we get out of the above while loop, it's time to update len, if needed.
|
||||||
|
if( (x - current->number) > len){
|
||||||
|
len = x - current->number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
// we still need a while here, rather than an if.
|
||||||
|
// so that we can find the next non-empty bucket.
|
||||||
|
while(current == nullptr){
|
||||||
|
i++;
|
||||||
|
if(i<TABLE_SIZE){
|
||||||
|
// move to the next bucket
|
||||||
|
current = hash_table[i];
|
||||||
|
}else{
|
||||||
|
// this means we have visited every element in the whole hash table.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
//std::vector<int> nums = {100, 4, 200, 1, 3, 2};
|
||||||
|
std::vector<int> nums = {0,3,7,2,5,8,4,6,0,1};
|
||||||
|
//std::vector<int> nums = {100, 4, 200, 201, 202, 203, 205, 204, 1, 3, 2};
|
||||||
|
int size = nums.size();
|
||||||
|
std::cout<< "for vector {";
|
||||||
|
for(int i=0;i<size-1;i++){
|
||||||
|
std::cout<< nums[i] << ",";
|
||||||
|
}
|
||||||
|
std::cout<< nums[size-1] << "}" <<std::endl;
|
||||||
|
int length = longestConsecutive(nums);
|
||||||
|
std::cout << "The length of the longest consecutive sequence is: " << length << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
// Question: why is this an O(n) solution when we have a nested loop? Because the inner while loop will only be used if *itr1 is the beginning of the sequence, which means each element will only be visited 2 or 3 times.
|
||||||
|
int longestConsecutive(std::vector<int>& nums) {
|
||||||
|
int len=0;
|
||||||
|
std::unordered_set<int> set1;
|
||||||
|
int size = nums.size();
|
||||||
|
// store unique elements in nums in set1
|
||||||
|
for(int i=0;i<nums.size();i++){
|
||||||
|
set1.insert(nums[i]);
|
||||||
|
}
|
||||||
|
std::unordered_set<int>::iterator itr1 = set1.begin();
|
||||||
|
while(itr1!=set1.end()){
|
||||||
|
// clearly *itr1 is in the set, because that's the meaning of iteration; and if *itr1-1 is not in the set, then we know *itr1 is the beginning of a sequence.
|
||||||
|
if(!set1.count(*itr1-1)){
|
||||||
|
int x = *itr1+1;
|
||||||
|
// now that *itr1 is the beginning of a sequence, how about *itr1+1?
|
||||||
|
while(set1.count(x)){
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
if(x-*itr1>len){
|
||||||
|
len = x-*itr1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itr1++;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
//std::vector<int> nums = {100, 4, 200, 1, 3, 2};
|
||||||
|
std::vector<int> nums = {0,3,7,2,5,8,4,6,0,1};
|
||||||
|
//std::vector<int> nums = {100, 4, 200, 201, 202, 203, 205, 204, 1, 3, 2};
|
||||||
|
int size = nums.size();
|
||||||
|
std::cout<< "for vector {";
|
||||||
|
for(int i=0;i<size-1;i++){
|
||||||
|
std::cout<< nums[i] << ",";
|
||||||
|
}
|
||||||
|
std::cout<< nums[size-1] << "}" <<std::endl;
|
||||||
|
int length = longestConsecutive(nums);
|
||||||
|
std::cout << "The length of the longest consecutive sequence is: " << length << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define TABLE_SIZE 1024
|
||||||
|
|
||||||
|
class Node{
|
||||||
|
public:
|
||||||
|
int number;
|
||||||
|
Node* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// insert num into table
|
||||||
|
void insert(int num, Node** table){
|
||||||
|
int key;
|
||||||
|
key = abs(num%TABLE_SIZE); // key will be something in between 0 and (TABLE_SIZE-1); num can be negative
|
||||||
|
if(table[key] == nullptr){
|
||||||
|
// create the first node for this linked list
|
||||||
|
Node* node;
|
||||||
|
node = new Node;
|
||||||
|
node->number = num;
|
||||||
|
node->next = nullptr;
|
||||||
|
table[key] = node;
|
||||||
|
}else{
|
||||||
|
// insert a node to the beginning of this linked list
|
||||||
|
Node* node;
|
||||||
|
node = new Node;
|
||||||
|
node->number = num;
|
||||||
|
node->next = table[key];
|
||||||
|
table[key] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// search the hash table and see if we can find this num.
|
||||||
|
bool identify(int num, Node** table){
|
||||||
|
int key = abs(num%TABLE_SIZE);
|
||||||
|
// search num in table[key];
|
||||||
|
Node* node = table[key];
|
||||||
|
while(node != nullptr){
|
||||||
|
if(node->number == num){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
// if not found, return false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Question: why is this an O(n) solution when we have a nested loop? Because the inner while loop will only be used if *itr1 is the beginning of the sequence, which means each element will only be visited 2 or 3 times.
|
||||||
|
int longestConsecutive(std::vector<int>& nums) {
|
||||||
|
int len=0;
|
||||||
|
Node* hash_table[TABLE_SIZE];
|
||||||
|
// initialize the table
|
||||||
|
for(int i=0;i<TABLE_SIZE;i++){
|
||||||
|
hash_table[i] = nullptr;
|
||||||
|
}
|
||||||
|
int size = nums.size();
|
||||||
|
if(size == 0){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// store unique elements in nums in set1
|
||||||
|
for(int i=0;i<nums.size();i++){
|
||||||
|
insert(nums[i], hash_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i=0;
|
||||||
|
Node* current = hash_table[i];
|
||||||
|
// if we reach here, then there is at least one Node in the hash table.
|
||||||
|
// find the first non-NULL Node.
|
||||||
|
while(current == nullptr){
|
||||||
|
i++;
|
||||||
|
current = hash_table[i];
|
||||||
|
}
|
||||||
|
// traverse the hash table
|
||||||
|
while(current!=nullptr){
|
||||||
|
// if (current->num-1) can't be found
|
||||||
|
if(!identify(current->number - 1, hash_table)){
|
||||||
|
int x = current->number + 1;
|
||||||
|
// now that current->num is the beginning of a sequence, how about current->num + 1?
|
||||||
|
while(identify(x, hash_table)){
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
// when we get out of the above while loop, it's time to update len, if needed.
|
||||||
|
if( (x - current->number) > len){
|
||||||
|
len = x - current->number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
// we still need a while here, rather than an if.
|
||||||
|
// so that we can find the next non-empty bucket.
|
||||||
|
while(current == nullptr){
|
||||||
|
i++;
|
||||||
|
if(i<TABLE_SIZE){
|
||||||
|
// move to the next bucket
|
||||||
|
current = hash_table[i];
|
||||||
|
}else{
|
||||||
|
// this means we have visited every element in the whole hash table.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
//std::vector<int> nums = {100, 4, 200, 1, 3, 2};
|
||||||
|
std::vector<int> nums = {100, 4, 200, 1, 3, 2, 2, 2, 2, 3};
|
||||||
|
//std::vector<int> nums = {100, 4, 200, 1, 3, 2, 5, 6};
|
||||||
|
//std::vector<int> nums = {0,3,7,2,5,8,4,6,0,1};
|
||||||
|
//std::vector<int> nums = {100, 4, 200, 201, 202, 203, 205, 204, 1, 3, 2};
|
||||||
|
int size = nums.size();
|
||||||
|
std::cout<< "for vector {";
|
||||||
|
for(int i=0;i<size-1;i++){
|
||||||
|
std::cout<< nums[i] << ",";
|
||||||
|
}
|
||||||
|
std::cout<< nums[size-1] << "}" <<std::endl;
|
||||||
|
int length = longestConsecutive(nums);
|
||||||
|
std::cout << "The length of the longest consecutive sequence is: " << length << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
120
labs/12_hash_tables/test_longest_consecutive_sequence_sol.cpp
Normal file
120
labs/12_hash_tables/test_longest_consecutive_sequence_sol.cpp
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define TABLE_SIZE 1024
|
||||||
|
|
||||||
|
class Node{
|
||||||
|
public:
|
||||||
|
int number;
|
||||||
|
Node* next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// insert num into table
|
||||||
|
void insert(int num, Node** table){
|
||||||
|
int key;
|
||||||
|
key = abs(num%TABLE_SIZE); // key will be something in between 0 and (TABLE_SIZE-1); num can be negative
|
||||||
|
if(table[key] == nullptr){
|
||||||
|
// create the first node for this linked list
|
||||||
|
Node* node;
|
||||||
|
node = new Node;
|
||||||
|
node->number = num;
|
||||||
|
node->next = nullptr;
|
||||||
|
table[key] = node;
|
||||||
|
}else{
|
||||||
|
// insert a node to the beginning of this linked list
|
||||||
|
Node* node;
|
||||||
|
node = new Node;
|
||||||
|
node->number = num;
|
||||||
|
node->next = table[key];
|
||||||
|
table[key] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// search the hash table and see if we can find this num.
|
||||||
|
bool identify(int num, Node** table){
|
||||||
|
int key = abs(num%TABLE_SIZE);
|
||||||
|
// search num in table[key];
|
||||||
|
Node* node = table[key];
|
||||||
|
while(node != nullptr){
|
||||||
|
if(node->number == num){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
// if not found, return false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Question: why is this an O(n) solution when we have a nested loop? Because the inner while loop will only be used if *itr1 is the beginning of the sequence, which means each element will only be visited 2 or 3 times.
|
||||||
|
int longestConsecutive(std::vector<int>& nums) {
|
||||||
|
int len=0;
|
||||||
|
Node* hash_table[TABLE_SIZE];
|
||||||
|
// initialize the table
|
||||||
|
for(int i=0;i<TABLE_SIZE;i++){
|
||||||
|
hash_table[i] = nullptr;
|
||||||
|
}
|
||||||
|
int size = nums.size();
|
||||||
|
if(size == 0){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// store unique elements in nums in set1
|
||||||
|
for(int i=0;i<nums.size();i++){
|
||||||
|
insert(nums[i], hash_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i=0;
|
||||||
|
Node* current = hash_table[i];
|
||||||
|
// if we reach here, then there is at least one Node in the hash table.
|
||||||
|
// find the first non-NULL Node.
|
||||||
|
while(current == nullptr){
|
||||||
|
i++;
|
||||||
|
current = hash_table[i];
|
||||||
|
}
|
||||||
|
// traverse the hash table
|
||||||
|
while(current!=nullptr){
|
||||||
|
// if (current->num-1) can't be found
|
||||||
|
if(!identify(current->number - 1, hash_table)){
|
||||||
|
int x = current->number + 1;
|
||||||
|
// now that current->num is the beginning of a sequence, how about current->num + 1?
|
||||||
|
while(identify(x, hash_table)){
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
// when we get out of the above while loop, it's time to update len, if needed.
|
||||||
|
if( (x - current->number) > len){
|
||||||
|
len = x - current->number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
// we still need a while here, rather than an if.
|
||||||
|
// so that we can find the next non-empty bucket.
|
||||||
|
while(current == nullptr){
|
||||||
|
i++;
|
||||||
|
if(i<TABLE_SIZE){
|
||||||
|
// move to the next bucket
|
||||||
|
current = hash_table[i];
|
||||||
|
}else{
|
||||||
|
// this means we have visited every element in the whole hash table.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
//std::vector<int> nums = {100, 4, 200, 1, 3, 2};
|
||||||
|
//std::vector<int> nums = {100, 4, 200, 1, 3, 2, 2, 2, 2, 3};
|
||||||
|
//std::vector<int> nums = {100, 4, 200, 1, 3, 2, 5, 6};
|
||||||
|
//std::vector<int> nums = {0,3,7,2,5,8,4,6,0,1};
|
||||||
|
//std::vector<int> nums = {100, 4, 200, 201, 202, 203, 205, 204, 1, 3, 2};
|
||||||
|
std::vector<int> nums = {-3,0,1,2,3,-2,-1,-5};
|
||||||
|
int size = nums.size();
|
||||||
|
std::cout<< "for vector {";
|
||||||
|
for(int i=0;i<size-1;i++){
|
||||||
|
std::cout<< nums[i] << ",";
|
||||||
|
}
|
||||||
|
std::cout<< nums[size-1] << "}" <<std::endl;
|
||||||
|
int length = longestConsecutive(nums);
|
||||||
|
std::cout << "The length of the longest consecutive sequence is: " << length << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user