Compare commits

...

6 Commits

Author SHA1 Message Date
bb359c9fe7 solve lab 9 2025-04-09 12:19:36 -04:00
Jidong Xiao
e41e5e4a0c list others as variations 2025-04-09 12:19:36 -04:00
Jidong Xiao
6bdc38ef77 switch to min heap now 2025-04-09 12:19:36 -04:00
Jidong Xiao
53824b1147 adding lab 2025-04-09 12:19:36 -04:00
Jidong Xiao
1ade7ffdde adding comments to heapify code 2025-04-09 12:19:36 -04:00
Jidong Xiao
47cbeb649f adding layer draw 2025-04-09 12:19:36 -04:00
9 changed files with 755 additions and 29 deletions

View File

@@ -559,52 +559,63 @@
break; break;
case 3: case 3:
no_rect.fill('yellow'); no_rect.fill('yellow');
layer.draw();
break; break;
case 4: case 4:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
no_rect.fill('white'); no_rect.fill('white');
find_inorder_predecessor.fill('yellow'); find_inorder_predecessor.fill('yellow');
stage.find('#node_2')[0].fill('lime'); stage.find('#node_2')[0].fill('lime');
layer.draw();
break; break;
case 5: case 5:
stage.find('#node_2')[0].fill('white'); stage.find('#node_2')[0].fill('white');
stage.find('#node_5')[0].fill('lime'); stage.find('#node_5')[0].fill('lime');
layer.draw();
break; break;
case 6: case 6:
stage.find('#node_5')[0].fill('white'); stage.find('#node_5')[0].fill('white');
stage.find('#node_7')[0].fill('lime'); stage.find('#node_7')[0].fill('lime');
layer.draw();
break; break;
case 7: case 7:
find_inorder_predecessor.fill('white'); find_inorder_predecessor.fill('white');
where_right.fill('yellow'); where_right.fill('yellow');
layer.draw();
break; break;
case 8: case 8:
null_rect.fill('yellow'); null_rect.fill('yellow');
layer.draw();
break; break;
case 9: case 9:
where_right.fill('white'); where_right.fill('white');
null_rect.fill('white'); null_rect.fill('white');
set_current.fill('yellow'); set_current.fill('yellow');
layer.draw();
break; break;
case 10: case 10:
node7to1.show(); node7to1.show();
layer.draw();
break; break;
case 11: case 11:
stage.find('#node_1')[0].fill('white'); stage.find('#node_1')[0].fill('white');
stage.find('#node_2')[0].fill('yellow'); stage.find('#node_2')[0].fill('yellow');
layer.draw();
break; break;
case 12: case 12:
set_current.fill('white'); set_current.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 13: case 13:
no_rect.fill('yellow'); no_rect.fill('yellow');
layer.draw();
break; break;
case 14: case 14:
@@ -613,355 +624,442 @@
find_inorder_predecessor.fill('yellow'); find_inorder_predecessor.fill('yellow');
stage.find('#node_7')[0].fill('white'); stage.find('#node_7')[0].fill('white');
stage.find('#node_4')[0].fill('lime'); stage.find('#node_4')[0].fill('lime');
layer.draw();
break; break;
case 15: case 15:
find_inorder_predecessor.fill('white'); find_inorder_predecessor.fill('white');
where_right.fill('yellow'); where_right.fill('yellow');
layer.draw();
break; break;
case 16: case 16:
null_rect.fill('yellow'); null_rect.fill('yellow');
layer.draw();
break; break;
case 17: case 17:
where_right.fill('white'); where_right.fill('white');
null_rect.fill('white'); null_rect.fill('white');
set_current.fill('yellow'); set_current.fill('yellow');
layer.draw();
break; break;
case 18: case 18:
node4to2.show(); node4to2.show();
layer.draw();
break; break;
case 19: case 19:
stage.find('#node_2')[0].fill('white'); stage.find('#node_2')[0].fill('white');
stage.find('#node_4')[0].fill('yellow'); stage.find('#node_4')[0].fill('yellow');
layer.draw();
break; break;
case 20: case 20:
set_current.fill('white'); set_current.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 21: case 21:
yes_rect.fill('yellow'); yes_rect.fill('yellow');
layer.draw();
break; break;
case 22: case 22:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
yes_rect.fill('white'); yes_rect.fill('white');
print_move_right.fill('yellow'); print_move_right.fill('yellow');
layer.draw();
break; break;
case 23: case 23:
console_text.text('Console:\n4'); console_text.text('Console:\n4');
layer.draw();
break; break;
case 24: case 24:
stage.find('#node_4')[0].fill('white'); stage.find('#node_4')[0].fill('white');
stage.find('#node_2')[0].fill('yellow'); stage.find('#node_2')[0].fill('yellow');
layer.draw();
break; break;
case 25: case 25:
print_move_right.fill('white'); print_move_right.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 26: case 26:
no_rect.fill('yellow'); no_rect.fill('yellow');
layer.draw();
break; break;
case 27: case 27:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
no_rect.fill('white'); no_rect.fill('white');
find_inorder_predecessor.fill('yellow'); find_inorder_predecessor.fill('yellow');
layer.draw();
break; break;
case 28: case 28:
stage.find('#node_4')[0].fill('lime'); stage.find('#node_4')[0].fill('lime');
layer.draw();
break; break;
case 29: case 29:
find_inorder_predecessor.fill('white'); find_inorder_predecessor.fill('white');
where_right.fill('yellow'); where_right.fill('yellow');
layer.draw();
break; break;
case 30: case 30:
current_rect.fill('yellow'); current_rect.fill('yellow');
layer.draw();
break; break;
case 31: case 31:
where_right.fill('white'); where_right.fill('white');
current_rect.fill('white'); current_rect.fill('white');
set_null.fill('yellow'); set_null.fill('yellow');
layer.draw();
break; break;
case 32: case 32:
node4to2.hide(); node4to2.hide();
console_text.text('Console:\n4 2'); console_text.text('Console:\n4 2');
layer.draw();
break; break;
case 33: case 33:
stage.find('#node_4')[0].fill('white'); stage.find('#node_4')[0].fill('white');
stage.find('#node_2')[0].fill('white'); stage.find('#node_2')[0].fill('white');
stage.find('#node_5')[0].fill('yellow'); stage.find('#node_5')[0].fill('yellow');
layer.draw();
break; break;
case 34: case 34:
set_null.fill('white'); set_null.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 35: case 35:
no_rect.fill('yellow'); no_rect.fill('yellow');
layer.draw();
break; break;
case 36: case 36:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
no_rect.fill('white'); no_rect.fill('white');
find_inorder_predecessor.fill('yellow'); find_inorder_predecessor.fill('yellow');
stage.find('#node_6')[0].fill('lime'); stage.find('#node_6')[0].fill('lime');
layer.draw();
break; break;
case 37: case 37:
find_inorder_predecessor.fill('white'); find_inorder_predecessor.fill('white');
where_right.fill('yellow'); where_right.fill('yellow');
layer.draw();
break; break;
case 38: case 38:
null_rect.fill('yellow'); null_rect.fill('yellow');
layer.draw();
break; break;
case 39: case 39:
where_right.fill('white'); where_right.fill('white');
null_rect.fill('white'); null_rect.fill('white');
set_current.fill('yellow'); set_current.fill('yellow');
layer.draw();
break; break;
case 40: case 40:
node6to5.show(); node6to5.show();
layer.draw();
break; break;
case 41: case 41:
stage.find('#node_5')[0].fill('white'); stage.find('#node_5')[0].fill('white');
stage.find('#node_6')[0].fill('yellow'); stage.find('#node_6')[0].fill('yellow');
layer.draw();
break; break;
case 42: case 42:
set_current.fill('white'); set_current.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 43: case 43:
yes_rect.fill('yellow'); yes_rect.fill('yellow');
layer.draw();
break; break;
case 44: case 44:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
yes_rect.fill('white'); yes_rect.fill('white');
print_move_right.fill('yellow'); print_move_right.fill('yellow');
layer.draw();
break; break;
case 45: case 45:
console_text.text('Console:\n4 2 6'); console_text.text('Console:\n4 2 6');
layer.draw();
break; break;
case 46: case 46:
stage.find('#node_6')[0].fill('white'); stage.find('#node_6')[0].fill('white');
stage.find('#node_5')[0].fill('yellow'); stage.find('#node_5')[0].fill('yellow');
layer.draw();
break; break;
case 47: case 47:
print_move_right.fill('white'); print_move_right.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 48: case 48:
no_rect.fill('yellow'); no_rect.fill('yellow');
layer.draw();
break; break;
case 49: case 49:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
no_rect.fill('white'); no_rect.fill('white');
find_inorder_predecessor.fill('yellow'); find_inorder_predecessor.fill('yellow');
layer.draw();
break; break;
case 50: case 50:
stage.find('#node_6')[0].fill('lime'); stage.find('#node_6')[0].fill('lime');
layer.draw();
break; break;
case 51: case 51:
find_inorder_predecessor.fill('white'); find_inorder_predecessor.fill('white');
where_right.fill('yellow'); where_right.fill('yellow');
layer.draw();
break; break;
case 52: case 52:
current_rect.fill('yellow'); current_rect.fill('yellow');
layer.draw();
break; break;
case 53: case 53:
where_right.fill('white'); where_right.fill('white');
current_rect.fill('white'); current_rect.fill('white');
set_null.fill('yellow'); set_null.fill('yellow');
layer.draw();
break; break;
case 54: case 54:
node6to5.hide(); node6to5.hide();
console_text.text('Console:\n4 2 6 5'); console_text.text('Console:\n4 2 6 5');
layer.draw();
break; break;
case 55: case 55:
stage.find('#node_6')[0].fill('white'); stage.find('#node_6')[0].fill('white');
stage.find('#node_5')[0].fill('white'); stage.find('#node_5')[0].fill('white');
stage.find('#node_7')[0].fill('yellow'); stage.find('#node_7')[0].fill('yellow');
layer.draw();
break; break;
case 56: case 56:
set_null.fill('white'); set_null.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 57: case 57:
yes_rect.fill('yellow'); yes_rect.fill('yellow');
layer.draw();
break; break;
case 58: case 58:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
yes_rect.fill('white'); yes_rect.fill('white');
print_move_right.fill('yellow'); print_move_right.fill('yellow');
layer.draw();
break; break;
case 59: case 59:
console_text.text('Console:\n4 2 6 5 7'); console_text.text('Console:\n4 2 6 5 7');
layer.draw();
break; break;
case 60: case 60:
stage.find('#node_7')[0].fill('white'); stage.find('#node_7')[0].fill('white');
stage.find('#node_1')[0].fill('yellow'); stage.find('#node_1')[0].fill('yellow');
layer.draw();
break; break;
case 61: case 61:
print_move_right.fill('white'); print_move_right.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 62: case 62:
no_rect.fill('yellow'); no_rect.fill('yellow');
layer.draw();
break; break;
case 63: case 63:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
no_rect.fill('white'); no_rect.fill('white');
find_inorder_predecessor.fill('yellow'); find_inorder_predecessor.fill('yellow');
layer.draw();
break; break;
case 64: case 64:
stage.find('#node_2')[0].fill('lime'); stage.find('#node_2')[0].fill('lime');
layer.draw();
break; break;
case 65: case 65:
stage.find('#node_2')[0].fill('white'); stage.find('#node_2')[0].fill('white');
stage.find('#node_5')[0].fill('lime'); stage.find('#node_5')[0].fill('lime');
layer.draw();
break; break;
case 66: case 66:
stage.find('#node_5')[0].fill('white'); stage.find('#node_5')[0].fill('white');
stage.find('#node_7')[0].fill('lime'); stage.find('#node_7')[0].fill('lime');
layer.draw();
break; break;
case 67: case 67:
find_inorder_predecessor.fill('white'); find_inorder_predecessor.fill('white');
where_right.fill('yellow'); where_right.fill('yellow');
layer.draw();
break; break;
case 68: case 68:
current_rect.fill('yellow'); current_rect.fill('yellow');
layer.draw();
case 69: case 69:
where_right.fill('white'); where_right.fill('white');
current_rect.fill('white'); current_rect.fill('white');
set_null.fill('yellow'); set_null.fill('yellow');
layer.draw();
break; break;
case 70: case 70:
console_text.text('Console:\n4 2 6 5 7 1'); console_text.text('Console:\n4 2 6 5 7 1');
node7to1.hide(); node7to1.hide();
layer.draw();
break; break;
case 71: case 71:
stage.find('#node_7')[0].fill('white'); stage.find('#node_7')[0].fill('white');
stage.find('#node_1')[0].fill('white'); stage.find('#node_1')[0].fill('white');
stage.find('#node_3')[0].fill('yellow'); stage.find('#node_3')[0].fill('yellow');
layer.draw();
break; break;
case 72: case 72:
set_null.fill('white'); set_null.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 73: case 73:
yes_rect.fill('yellow'); yes_rect.fill('yellow');
layer.draw();
break; break;
case 74: case 74:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
yes_rect.fill('white'); yes_rect.fill('white');
print_move_right.fill('yellow'); print_move_right.fill('yellow');
layer.draw();
break; break;
case 75: case 75:
console_text.text('Console:\n4 2 6 5 7 1 3'); console_text.text('Console:\n4 2 6 5 7 1 3');
layer.draw();
break; break;
case 76: case 76:
stage.find('#node_3')[0].fill('white'); stage.find('#node_3')[0].fill('white');
stage.find('#node_8')[0].fill('yellow'); stage.find('#node_8')[0].fill('yellow');
layer.draw();
break; break;
case 77: case 77:
print_move_right.fill('white'); print_move_right.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 78: case 78:
no_rect.fill('yellow'); no_rect.fill('yellow');
layer.draw();
break; break;
case 79: case 79:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
no_rect.fill('white'); no_rect.fill('white');
find_inorder_predecessor.fill('yellow'); find_inorder_predecessor.fill('yellow');
layer.draw();
break; break;
case 80: case 80:
stage.find('#node_9')[0].fill('lime'); stage.find('#node_9')[0].fill('lime');
layer.draw();
break; break;
case 81: case 81:
find_inorder_predecessor.fill('white'); find_inorder_predecessor.fill('white');
where_right.fill('yellow'); where_right.fill('yellow');
layer.draw();
break; break;
case 82: case 82:
null_rect.fill('yellow'); null_rect.fill('yellow');
layer.draw();
break; break;
case 83: case 83:
where_right.fill('white'); where_right.fill('white');
null_rect.fill('white'); null_rect.fill('white');
set_current.fill('yellow'); set_current.fill('yellow');
layer.draw();
break; break;
case 84: case 84:
node9to8.show(); node9to8.show();
layer.draw();
break; break;
case 85: case 85:
stage.find('#node_8')[0].fill('white'); stage.find('#node_8')[0].fill('white');
stage.find('#node_9')[0].fill('yellow'); stage.find('#node_9')[0].fill('yellow');
layer.draw();
break; break;
case 86: case 86:
set_current.fill('white'); set_current.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 87: case 87:
yes_rect.fill('yellow'); yes_rect.fill('yellow');
layer.draw();
break; break;
case 88: case 88:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
yes_rect.fill('white'); yes_rect.fill('white');
layer.draw();
break; break;
case 89: case 89:
print_move_right.fill('yellow'); print_move_right.fill('yellow');
layer.draw();
break; break;
case 90: case 90:
console_text.text('Console:\n4 2 6 5 7 1 3 9'); console_text.text('Console:\n4 2 6 5 7 1 3 9');
layer.draw();
break; break;
case 91: case 91:
stage.find('#node_8')[0].fill('yellow'); stage.find('#node_8')[0].fill('yellow');
stage.find('#node_9')[0].fill('white'); stage.find('#node_9')[0].fill('white');
layer.draw();
break; break;
case 92: case 92:
print_move_right.fill('white'); print_move_right.fill('white');
left_subtree_null.fill('yellow'); left_subtree_null.fill('yellow');
layer.draw();
break; break;
case 93: case 93:
no_rect.fill('yellow'); no_rect.fill('yellow');
layer.draw();
break; break;
case 94: case 94:
left_subtree_null.fill('white'); left_subtree_null.fill('white');
no_rect.fill('white'); no_rect.fill('white');
find_inorder_predecessor.fill('yellow'); find_inorder_predecessor.fill('yellow');
layer.draw();
break; break;
case 95: case 95:
stage.find('#node_9')[0].fill('lime'); stage.find('#node_9')[0].fill('lime');
layer.draw();
break; break;
case 96: case 96:
find_inorder_predecessor.fill('white'); find_inorder_predecessor.fill('white');
where_right.fill('yellow'); where_right.fill('yellow');
layer.draw();
break; break;
case 97: case 97:
current_rect.fill('yellow'); current_rect.fill('yellow');
layer.draw();
break; break;
case 98: case 98:
where_right.fill('white'); where_right.fill('white');
current_rect.fill('white'); current_rect.fill('white');
set_null.fill('yellow'); set_null.fill('yellow');
layer.draw();
break; break;
case 99: case 99:
console_text.text('Console:\n4 2 6 5 7 1 3 9 8'); console_text.text('Console:\n4 2 6 5 7 1 3 9 8');
layer.draw();
break; break;
case 100: case 100:
node9to8.hide(); node9to8.hide();
stage.find('#node_8')[0].fill('white'); stage.find('#node_8')[0].fill('white');
stage.find('#node_9')[0].fill('white'); stage.find('#node_9')[0].fill('white');
layer.draw();
break; break;
case 101: case 101:
start_from_root.fill('red'); start_from_root.fill('red');
@@ -971,6 +1069,7 @@
where_right.fill('red'); where_right.fill('red');
set_current.fill('red'); set_current.fill('red');
set_null.fill('red'); set_null.fill('red');
layer.draw();
break; break;
@@ -979,7 +1078,6 @@
}; };
//while (step < )
</script> </script>

View File

@@ -1,23 +1,143 @@
#include <iostream> #include <iostream>
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <list>
bool isGood(int n) { // Compute the sum of the squares of a number's digits.
int sumOfSquaresOfDigits(int x) {
int sum = 0;
while (x > 0) {
int digit = x % 10;
sum += digit * digit;
x /= 10;
}
return sum;
}
// std::unordered_set
bool isGood_unordered_set(int n) {
std::unordered_set<int> visited;
while (n != 1 && visited.find(n) == visited.end()) {
visited.insert(n);
n = sumOfSquaresOfDigits(n);
}
return (n == 1);
}
// std::unordered_map
bool isGood_unordered_map(int n) {
// We only need to mark that we have "seen" a number.
// The bool value is arbitrary; storing true is enough.
std::unordered_map<int, bool> visited;
while (n != 1 && visited.find(n) == visited.end()) {
visited[n] = true; // Mark n as visited
n = sumOfSquaresOfDigits(n);
}
return (n == 1);
}
class MyHashSet {
private:
// store integers in a vector of lists
std::vector< std::list<int> > table;
int num_elements;
static const int DEFAULT_SIZE = 16;
static const double LOAD_FACTOR_THRESHOLD;
// A simple hash function for int
int hashFunction(int key) const {
// take abs value
int h = key < 0 ? -key : key;
return h % (int)table.size();
}
void rehash() {
int newSize = (int)table.size() * 2; // doubling
std::vector< std::list<int> > newTable(newSize);
// re-insert all existing elements into newTable
for (auto &chain : table) {
for (int val : chain) {
int h = (val < 0 ? -val : val) % newSize;
newTable[h].push_back(val);
}
}
table.swap(newTable);
}
void rehashIfNeeded() {
// If load factor = num_elements / table.size() is too big, rehash
double load_factor = (double)num_elements / (double)table.size();
if (load_factor > LOAD_FACTOR_THRESHOLD) {
rehash();
}
}
public:
// Constructor
MyHashSet(int initialSize = DEFAULT_SIZE)
: table(initialSize), num_elements(0) {}
// Return true if key is found
bool find(int key) const {
int h = hashFunction(key);
// Search the list in bin h
for (int val : table[h]) {
if (val == key) return true;
}
return false;
}
// Insert key if not already in the hash set
void insert(int key) {
if (!find(key)) {
int h = hashFunction(key);
table[h].push_back(key);
num_elements++;
rehashIfNeeded();
}
}
};
const double MyHashSet::LOAD_FACTOR_THRESHOLD = 1.0;
// The "isGood" function that uses MyHashSet
bool isGood_custom_hash(int n) {
MyHashSet visited;
while (n != 1 && !visited.find(n)) {
visited.insert(n);
n = sumOfSquaresOfDigits(n);
}
return (n == 1);
} }
int main() { int main() {
// Test cases
// 2, 4, 5, 6, 17, 18, 20 are not good numbers.
// 1, 7, 10, 13, 19, 23, 28, 68 are good numbers.
int testCases[] = {2,4,5,6,17,18,20,1,7,10,13,19,23,28,68}; int testCases[] = {2,4,5,6,17,18,20,1,7,10,13,19,23,28,68};
std::cout << "=== Using std::unordered_set ===" << std::endl;
for (int n : testCases) { for (int n : testCases) {
if (isGood(n)) { if (isGood_unordered_set(n))
std::cout << n << " is a good number." << std::endl; std::cout << n << " is a good number." << std::endl;
} else { else
std::cout << n << " is not a good number." << std::endl;
}
std::cout << "\n=== Using std::unordered_map ===" << std::endl;
for (int n : testCases) {
if (isGood_unordered_map(n))
std::cout << n << " is a good number." << std::endl;
else
std::cout << n << " is not a good number." << std::endl;
}
std::cout << "\n=== Using custom separate-chaining HashSet ===" << std::endl;
for (int n : testCases) {
if (isGood_custom_hash(n))
std::cout << n << " is a good number." << std::endl;
else
std::cout << n << " is not a good number." << std::endl; std::cout << n << " is not a good number." << std::endl;
}
} }
return 0; return 0;
} }

View File

@@ -1,23 +1,113 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <list>
// A minimal separate-chaining HashSet for integers.
class MyHashSet {
private:
std::vector< std::list<int> > table;
int num_elements;
static const int DEFAULT_SIZE = 16;
static const double LOAD_FACTOR_THRESHOLD;
int hashFunction(int key) const {
int h = key < 0 ? -key : key; // handle negative
return h % (int)table.size();
}
void rehash() {
int newSize = (int)table.size() * 2;
std::vector< std::list<int> > newTable(newSize);
for (auto &chain : table) {
for (int val : chain) {
int h = (val < 0 ? -val : val) % newSize;
newTable[h].push_back(val);
}
}
table.swap(newTable);
}
void rehashIfNeeded() {
double load_factor = (double)num_elements / (double)table.size();
if (load_factor > LOAD_FACTOR_THRESHOLD) {
rehash();
}
}
public:
MyHashSet(int initialSize = DEFAULT_SIZE)
: table(initialSize), num_elements(0) {}
bool find(int key) const {
int h = hashFunction(key);
for (int val : table[h]) {
if (val == key) {
return true;
}
}
return false;
}
void insert(int key) {
if (!find(key)) {
int h = hashFunction(key);
table[h].push_back(key);
num_elements++;
rehashIfNeeded();
}
}
};
const double MyHashSet::LOAD_FACTOR_THRESHOLD = 1.0;
int longestConsecutive(std::vector<int>& nums) { int longestConsecutive(std::vector<int>& nums) {
if (nums.empty()) return 0;
// Insert all numbers into the custom hash set
MyHashSet set;
for (int n : nums) {
set.insert(n);
}
int longestStreak = 0;
// For each number, only count a sequence if it's a "start"
// i.e., if (n-1) is NOT in the set
for (int n : nums) {
if (!set.find(n - 1)) {
// n is the start of a consecutive sequence
int currentNum = n;
int currentStreak = 1;
while (set.find(currentNum + 1)) {
currentNum++;
currentStreak++;
}
if (currentStreak > longestStreak) {
longestStreak = currentStreak;
}
}
}
return longestStreak;
} }
int main() { int main() {
//std::vector<int> nums = {100, 4, 200, 1, 3, 2}; // You can change these test vectors to experiment:
std::vector<int> nums = {100, 4, 200, 1, 3, 2, 2, 2, 2, 3}; //std::vector<int> nums = {100, 4, 200, 1, 3, 2};
//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 = {0,3,7,2,5,8,4,6,0,1}; std::vector<int> nums = {100, 4, 200, 1, 3, 2, 2, 2, 2, 3};
//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();
int size = nums.size(); std::cout << "for vector {";
std::cout<< "for vector {"; for(int i = 0; i < size; i++){
for(int i=0;i<size-1;i++){ if(i < size - 1) std::cout << nums[i] << ",";
std::cout<< nums[i] << ","; else std::cout << nums[i];
} }
std::cout<< nums[size-1] << "}" <<std::endl; std::cout << "}\n";
int length = longestConsecutive(nums);
std::cout << "The length of the longest consecutive sequence is: " << length << std::endl; int length = longestConsecutive(nums);
return 0; std::cout << "The length of the longest consecutive sequence is: "
<< length << std::endl;
return 0;
} }

View File

@@ -0,0 +1,25 @@
# Lab 10 — Priority Queues
In this lab, you will use binary heaps to implement the priority queue container. Start by downloading the files:
[priority_queue.h](priority_queue.h) [test_pq.cpp](test_pq.cpp)
The code provided in these files is straightforward. test_pq.cpp is a driver and test program, while priority_queue.h is a skeleton implementation. Please take a careful look. You will complete the implementation and add to the main program in lab. In your implementation, be careful when subtracting 1 from an unsigned int whose value is 0; it is not -1!
## Checkpoint 1:
Implement and test the push (a.k.a. insert) and the check_heap functions. Recall that push depends on the percolate_up functionality. check_heap, which works either with the heap member variable or with a vector provided from the outside, determines if the vector is properly a heap, meaning that each value is less than or equal to the values of both of its children (if they exist).
**To complete this checkpoint**: Show a TA or mentor your debugged implementation and discuss the running time of both insert and check_heap.
## Checkpoint 2:
Implement and test the pop (a.k.a. delete_min) function and the constructor that builds a valid heap from a vector of values that is in no particular order. Both of these depend on proper implementation of the percolate_down function.
**To complete this checkpoint**: Show a TA or mentor these additions and the test output.
## Checkpoint 3:
Finally, write and test the non-member function that sorts a vector by doing a heap sort. This code should sort the data “in place” and not require a large scratch space (e.g., another vector or list or another priority queue) to store a copy of the data as it is being sorted.
**To complete this checkpoint**: Show a TA or mentor your completed and debugged heap sort code.

View File

@@ -0,0 +1,68 @@
#ifndef priority_queue_h_
#define priority_queue_h_
#include <iostream>
#include <vector>
#include <cassert>
template <class T>
class priority_queue {
private:
std::vector<T> m_heap;
public:
priority_queue() {}
priority_queue( std::vector<T> const& values )
{
}
const T& top() const
{
assert( !m_heap.empty() );
return m_heap[0];
}
void push( const T& entry )
{
}
void pop()
{
assert( !m_heap.empty() );
}
int size() { return m_heap.size(); }
bool empty() { return m_heap.empty(); }
// The following three functions are used for debugging.
// Check to see that internally the heap property is realized.
bool check_heap( )
{
return this->check_heap( this->m_heap );
}
// Check an external vector to see that the heap property is realized.
bool check_heap( const std::vector<T>& heap )
{
}
// A utility to print the contents of the heap. Use it for debugging.
void print_heap( std::ostream & ostr )
{
for ( unsigned int i=0; i<m_heap.size(); ++i )
ostr << i << ": " << m_heap[i] << std::endl;
}
};
template <class T>
void heap_sort( std::vector<T> & v )
{
}
#endif

View File

@@ -0,0 +1,104 @@
#include <iostream>
#include <vector>
#include "priority_queue.h"
int main() {
priority_queue<int> pq_int;
std::cout << "****************\n"
<< "* Checkpoint 1 *\n"
<< "****************" << std::endl;
std::vector<int> heap_vector, empty_vector, one_vector;
one_vector.push_back( 4 );
heap_vector.push_back( 2); heap_vector.push_back(11); heap_vector.push_back( 7);
heap_vector.push_back(13); heap_vector.push_back(29); heap_vector.push_back( 8);
heap_vector.push_back( 9); heap_vector.push_back(27); heap_vector.push_back(21);
heap_vector.push_back(37); heap_vector.push_back(40); heap_vector.push_back(16);
std::vector<int> non_heap_vector( heap_vector );
non_heap_vector[5] = 23; // less than its parent
std::cout << "Testing if check_heap works." << std::endl
<< "An empty vector is a heap. check_heap = " << pq_int.check_heap(empty_vector) << std::endl
<< "A one-element vector is also a heap. check_heap = "
<< pq_int.check_heap(one_vector) << std::endl
<< "A manually constructed heap. check_heap = " << pq_int.check_heap(heap_vector) << std::endl
<< "A change at one location creates a non-heap. check_heap = "
<< pq_int.check_heap(non_heap_vector) << std::endl;
pq_int.push( 5 );
pq_int.push( 13 );
pq_int.push( 3 );
pq_int.push( 11 );
pq_int.push( 6 );
pq_int.push( 23 );
pq_int.push( 8 );
pq_int.push( 12 );
std::cout << "After 8 push operations the size is " << pq_int.size() << std::endl
<< "The top, which should be 3, is " << pq_int.top() << std::endl
<< "The call to pq.int.empty() returns " << pq_int.empty() << std::endl
<< "pq_int.check_heap() = " << pq_int.check_heap() << std::endl;
std::cout << "\nHere is the actual heap. Values should be 3, 6, 5, 12, 11, 23, 8, 13\n";
pq_int.print_heap( std::cout );
/*
std::cout << "\n\n"
<< "****************\n"
<< "* Checkpoint 2 *\n"
<< "****************" << std::endl;
pq_int.pop();
std::cout << "After one pop, the size should be 7. It is " << pq_int.size() << std::endl
<< "and the top value should be 5. It is " << pq_int.top() << std::endl;
pq_int.pop(); pq_int.pop();
std::cout << "After two more pops, the size should be 5. It is " << pq_int.size() << std::endl
<< "and the top value should be 8. It is " << pq_int.top() << std::endl;
pq_int.push( 1 );
std::cout << "Another push should make the top value 1. It is " << pq_int.top() << std::endl;
pq_int.pop(); pq_int.pop(); pq_int.pop(); pq_int.pop(); pq_int.pop(); pq_int.pop();
std::cout << "After six more pops, the pq should be empty. pq_int.empty() = " << pq_int.empty() << std::endl;
std::vector<float> v;
priority_queue<float> pq_f(v);
std::cout << '\n' << "Now we are trying construction from a vector.\n"
<< "When the vector is empty, here is the value of pq_f.empty() = " << pq_f.empty() << std::endl;
v.push_back(13.5); v.push_back(11.1); v.push_back(11.1); v.push_back(-4);
v.push_back(14.3); v.push_back(4.5); v.push_back(8.5); v.push_back(27.6);
v.push_back(2.3); v.push_back(15.2); v.push_back(23.1); v.push_back(12.5);
priority_queue<float> pq_float(v);
std::cout << "After constructing from a 12-value vector, the size is " << pq_float.size() << std::endl
<< "the top, which should be -4 is " << pq_float.top() << std::endl
<< "and check_heap returns " << pq_float.check_heap() << std::endl;
*/
/*
std::cout << "\n\n"
<< "****************\n"
<< "* Checkpoint 3 *\n"
<< "****************" << std::endl;
heap_sort( v );
int order_errors = 0;
for ( unsigned int i=0; i<v.size()-1; ++i )
if ( v[i] > v[i+1] ) order_errors ++ ;
std::cout << "After running heap sort, the size of the vector, which still should be 12 is " << v.size()
<< "\nand there are " << order_errors << " ordering errors\n"
<< "The resulting vector is\n";
for ( unsigned int i=0; i<v.size(); ++i )
std::cout << i << ": " << v[i] << '\n';
std::cout << "\nRunning heap sort on an empty vector... " << std::endl;
v.clear();
heap_sort(v);
std::cout << "produces a vector that is " << (v.empty() ? "empty" : "NOT empty") << std::endl;
*/
return 0;
}

View File

@@ -112,7 +112,102 @@ organized heap data, but incur a O(n log n) cost. Why?
- Heap Sort is a simple algorithm to sort a vector of values: Build a heap and then run n consecutive pop operations, storing each “popped” value in a new vector. - Heap Sort is a simple algorithm to sort a vector of values: Build a heap and then run n consecutive pop operations, storing each “popped” value in a new vector.
- It is straightforward to show that this requires O(n log n) time. - It is straightforward to show that this requires O(n log n) time.
- Exercise: Implement an in-place heap sort. An in-place algorithm uses only the memory holding the input data a separate large temporary vector is not needed. - Heap sort is an in-place sort. An in-place algorithm uses only the memory holding the input data a separate large temporary vector is not needed.
- The following is the sort algorithm with a main function to test it; the code makes a min heap.
```cpp
/* The heapify function is designed to ensure that a subtree rooted at a given index i
* in an array representation of a min heap maintains the heap property.
*
* Why Not Just Heapify Once? A single call to heapify on the entire array
* wouldn't suffice because heapify is designed to correct violations of
* the heap property starting from a specific node, assuming its subtrees are already heaps.
* Initially, the array doesn't have this structure, so multiple calls are necessary to build the initial min-heap.
* Similarly, during the sorting phase, each extraction disrupts the heap structure, necessitating a call to heapify to restore order.
*/
void heapify(std::vector<int>& nums, int n, int i){
int smallest = i; // assuming i is the smallest
int left = 2*i+1; // i's left child is at this location
int right = 2*i+2; // i's right child is at this location
if(left<n && nums[left]<nums[smallest]){
smallest = left;
}
if(right<n && nums[right]<nums[smallest]){
smallest = right;
}
// after the above, smallest basically will either stay the same, or will be either left or right, depending on nums[left] is larger or nums[right] is larger. largest stays the same if it is already larger than its two children.
// if largest is changed, then we do need to swap.
if(smallest != i){
std::swap(nums[i], nums[smallest]);
heapify(nums, n, smallest);
}
}
// heap sort: O(nlogn)
std::vector<int> sortArray(std::vector<int>& nums) {
int n = nums.size();
// build the heap, starting from the last non-leaf node.
// why we start from the last non-leaf node? because leaf nodes inherently satisfy the heap property, as they have no children.
// By beginning the heapify process from the last non-leaf node and moving upwards:
// We ensure that when we heapify a node, its children are already heapified.
// This bottom-up approach guarantees that each subtree satisfies the heap property before moving to the next node.
for(int i=n/2-1; i>=0; i--){
// heapify the subtree whose root is at i
// i.e., build a min heap, with i being the root; and this heap contains nodes from i to n-1;
heapify(nums, n, i);
}
// now the first one is the largest, swap it to the back
// do this n-1 times.
for(int i=0; i<(n-1); i++){
// build the min heap again, with 0 being the root.
// but only consider n-1-i elements, as the others are already in the right place.
heapify(nums, n-1-i, 0);
}
return nums;
}
// Assuming the heapify and sortArray functions are defined above or included from another file
int main() {
// Sample data to be sorted
std::vector<int> nums = {42, 12, 13, 65, 98, 45, 97, 85, 76, 90};
// Output the original array
std::cout << "Original array:\n";
for (int num : nums) {
std::cout << num << " ";
}
std::cout << std::endl;
// Sort the array using your sortArray function
std::vector<int> sortedNums = sortArray(nums);
// Output the sorted array
std::cout << "\nSorted array:\n";
for (int num : sortedNums) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
```
The above program prints the following:
```console
$ g++ heap_sort.cpp
$ ./a.out
Original array:
42 12 13 65 98 45 97 85 76 90
Sorted array:
12 42 13 65 90 45 97 85 76 98
```
## 24.9 Summary Notes about Vector-Based Priority Queues ## 24.9 Summary Notes about Vector-Based Priority Queues
@@ -122,8 +217,10 @@ organized heap data, but incur a O(n log n) cost. Why?
## 24.10 Leetcode Exercises ## 24.10 Leetcode Exercises
In addition to the above solution, there are also other variations of the heap sort:
- [Leetcode problem 912: Sort an Array](https://leetcode.com/problems/sort-an-array/). - [Leetcode problem 912: Sort an Array](https://leetcode.com/problems/sort-an-array/).
- Solution: [p912_heapsort_array.cpp](../../leetcode/p912_heapsort_array.cpp). - Solution: [p912_heapsort_array.cpp](../../leetcode/p912_heapsort_array.cpp).
- Solution 2: [p912_heapsort_array_heapify.cpp](../../leetcode/p912_heapsort_array_heapify.cpp). - Solution 2: - max heap: [p912_heapsort_array_heapify.cpp](../../leetcode/p912_heapsort_array_heapify.cpp).
- Solution 3: - min heap: [p912_heapsort_array_min_heap.cpp](../../leetcode/p912_heapsort_array_min_heap.cpp). - Solution 3: - min heap: [p912_heapsort_array_min_heap.cpp](../../leetcode/p912_heapsort_array_min_heap.cpp).
- Solution 4: - min heap, with functor: [p912_heapsort_array_functor.cpp](../../leetcode/p912_heapsort_array_functor.cpp). - Solution 4: - min heap, with functor: [p912_heapsort_array_functor.cpp](../../leetcode/p912_heapsort_array_functor.cpp).

View File

@@ -0,0 +1,101 @@
#include <iostream>
#include <vector>
/* The heapify function is designed to ensure that a subtree rooted at a given index i
* in an array representation of a heap maintains the heap property.
* While the function doesn't have an explicit base case like some recursive functions,
* it inherently terminates due to the following conditions:
*
* Leaf Node Condition: If the node at index i is a leaf node (i.e., it has no children),
* the function reaches a point where both left and right indices are greater than or equal to n (the size of the heap).
* In this scenario, the conditions left < n and right < n in the if statements evaluating the children will both be false,
* preventing further recursive calls.
*
* Heap Property Satisfaction: If the node at index i is greater than or equal to its children (or if it has no children),
* the heap property is already satisfied. Consequently, the variable largest remains equal to i,
* and the condition largest != i evaluates to false.
* This prevents the swap operation and the subsequent recursive call, leading to termination.
* In essence, the function will return when:
* The node is a leaf node.
* The node's value is greater than or equal to its children's values, maintaining the heap property.
* These implicit conditions ensure that the recursion does not continue indefinitely.
*
* Why Not Just Heapify Once? A single call to heapify on the entire array
* wouldn't suffice because heapify is designed to correct violations of
* the heap property starting from a specific node, assuming its subtrees are already heaps.
* Initially, the array doesn't have this structure, so multiple calls are necessary to build the initial max-heap.
* Similarly, during the sorting phase, each extraction disrupts the heap structure, necessitating a call to heapify to restore order.
* */
void heapify(std::vector<int>& nums, int n, int i){
int smallest = i; // assuming i is the smallest
int left = 2*i+1; // i's left child is at this location
int right = 2*i+2; // i's right child is at this location
if(left<n && nums[left]<nums[smallest]){
smallest = left;
}
if(right<n && nums[right]<nums[smallest]){
smallest = right;
}
// after the above, smallest basically will either stay the same, or will be either left or right, depending on nums[left] is larger or nums[right] is larger. largest stays the same if it is already larger than its two children.
// if largest is changed, then we do need to swap.
if(smallest != i){
std::swap(nums[i], nums[smallest]);
heapify(nums, n, smallest);
}
}
// heap sort: O(nlogn)
std::vector<int> sortArray(std::vector<int>& nums) {
int n = nums.size();
// build the heap, starting from the last non-leaf node.
// why we start from the last non-leaf node? because leaf nodes inherently satisfy the heap property, as they have no children.
// By beginning the heapify process from the last non-leaf node and moving upwards:
// We ensure that when we heapify a node, its children are already heapified.
// This bottom-up approach guarantees that each subtree satisfies the heap property before moving to the next node.
for(int i=n/2-1; i>=0; i--){
// heapify the subtree whose root is at i
// i.e., build a max heap, with i being the root; and this heap contains nodes from i to n-1;
heapify(nums, n, i);
}
// now the first one is the largest, swap it to the back
// do this n-1 times.
for(int i=0; i<(n-1); i++){
// nums[0] is always the largest one
std::swap(nums[0], nums[n-1-i]);
// build the max heap again, with 0 being the root.
// but only consider n-1-i elements, as the others are already in the right place.
heapify(nums, n-1-i, 0);
}
return nums;
}
// Assuming the heapify and sortArray functions are defined above or included from another file
int main() {
// Sample data to be sorted
std::vector<int> nums = {42, 12, 13, 65, 98, 45, 97, 85, 76, 90};
// Output the original array
std::cout << "Original array:\n";
for (int num : nums) {
std::cout << num << " ";
}
std::cout << std::endl;
// Sort the array using your sortArray function
std::vector<int> sortedNums = sortArray(nums);
// Output the sorted array
std::cout << "\nSorted array:\n";
for (int num : sortedNums) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}

View File

@@ -1,5 +1,24 @@
class Solution { class Solution {
public: public:
/* The heapify function is designed to ensure that a subtree rooted at a given index i
* in an array representation of a heap maintains the heap property.
* While the function doesn't have an explicit base case like some recursive functions,
* it inherently terminates due to the following conditions:
*
* Leaf Node Condition: If the node at index i is a leaf node (i.e., it has no children),
* the function reaches a point where both left and right indices are greater than or equal to n (the size of the heap).
* In this scenario, the conditions left < n and right < n in the if statements evaluating the children will both be false,
* preventing further recursive calls.
*
* Heap Property Satisfaction: If the node at index i is greater than or equal to its children (or if it has no children),
* the heap property is already satisfied. Consequently, the variable largest remains equal to i,
* and the condition largest != i evaluates to false.
* This prevents the swap operation and the subsequent recursive call, leading to termination.
* In essence, the function will return when:
* The node is a leaf node.
* The node's value is greater than or equal to its children's values, maintaining the heap property.
* These implicit conditions ensure that the recursion does not continue indefinitely.
* */
void heapify(vector<int>& nums, int n, int i){ void heapify(vector<int>& nums, int n, int i){
int largest = i; // assuming i is the largest int largest = i; // assuming i is the largest
int left = 2*i+1; // i's left child is at this location int left = 2*i+1; // i's left child is at this location
@@ -24,10 +43,14 @@ public:
// heap sort: O(nlogn) // heap sort: O(nlogn)
vector<int> sortArray(vector<int>& nums) { vector<int> sortArray(vector<int>& nums) {
int n = nums.size(); int n = nums.size();
// build the heap, starting from the last non-leaf node // build the heap, starting from the last non-leaf node.
// why we start from the last non-leaf node? because leaf nodes inherently satisfy the heap property, as they have no children.
// By beginning the heapify process from the last non-leaf node and moving upwards:
// We ensure that when we heapify a node, its children are already heapified.
// This bottom-up approach guarantees that each subtree satisfies the heap property before moving to the next node.
for(int i=n/2-1; i>=0; i--){ for(int i=n/2-1; i>=0; i--){
// heapify the subtree whose root is at i // heapify the subtree whose root is at i
// i.e., build a max heap, with i being the root. // i.e., build a max heap, with i being the root; and this heap contains nodes from i to n-1;
heapify(nums, n, i); heapify(nums, n, i);
} }