re order 11 and 12

This commit is contained in:
Jidong Xiao
2024-04-02 11:32:30 -04:00
parent b91738d769
commit 2eab6a9f3d
24 changed files with 1617 additions and 2 deletions

View File

@@ -0,0 +1,89 @@
# 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 practice using std::unordered_set, std::unordered_map, and construct your own separate-chaining-based hash table.
<!--Provided code for checkpoint 1 and checkpoint 2: [ds_hashset.h](ds_hashset.h) and [test_ds_hashset.cpp](test_ds_hashset.cpp).-->
## Checkpoint 1: Using std::unordered_set and std::unordered_map
*estimate: 15-30 minutes*
Complete the *isHappy* function in this [program](happy_number.cpp). This function determines if a number n is happy or not. You can assume 1&lt;=n&lt;50000. You must write two versions of the function, one version uses std::unordered_set, the other version uses std::unordered_map.
A happy number is a number defined by the following process:
- Starting with any positive integer, replace the number by the sum of the squares of its digits.
- Repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.
- Those numbers for which this process ends in 1 are happy.
Return true if n is a happy number, and false if not. Here are some examples:
```console
Example 1:
Input: n = 19
Output: true
Explanation:
1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1
```
```console
Example 2:
Input: n = 2
Output: false
```
<!--For the first part of this checkpoint, implement and test the *insert* function for the hashset. The *insert* function must first determine in which bin the new element belongs (using the hash function), and then insert the element into that bin but only if it isnt there already. The *insert* function returns a pair containing an iterator pointing at the element, and a bool indicating whether it was successfully inserted (true) or already there (false).
For the second part of this checkpoint, experiment with the hash function. In the provided code we include the implementation of a good hash function for strings. Are there any collisions for the small example? Now write some alternative hash functions. First, create a trivial hash function that is guaranteed to have many, many collisions. Then, create a hash function that is not terrible, but will unfortunately always place anagrams (words with the same letters, but rearranged) in the same bin. Test your alternate functions and be prepared to show the results to your TA.
**To complete this checkpoint**: Show a TA your debugged implementation of *insert* and your experimentation with alternative hash functions.-->
**To complete this checkpoint**: Show a TA the two versions of your program and the test results.
## Checkpoint 2: Separate Chaining Hash Table
*estimate: 30-40 minutes*
<!--Next, implement and test the *begin* function, which initializes the iteration through a hashset. Confirm that the elements in the set are visited in the same order they appear with the *print* function (which we have implemented for debugging purposes only).
Finally, implement and test the *resize* function. This function is automatically called from the *insert* function when the set gets “too full”. This function should make a new top level vector structure of the requested size and copy all the data from the old structure to the new structure. Note that the elements will likely be shuffled around from the old structure to the new structure.-->
Complete the *isHappy* function using separate chaining. Do not use any of these: std::unordered_map, std::unordered_set, std::map, std::set.
**To complete this checkpoint**: Show a TA these additions and the test output.
## Checkpoint 3,4,5,6: Separate Chaining Hash Table (Yes, it's Checkpoint 3,4,5,6, as there are 3 extra credits for making this program work.)
*estimate: 30-40 minutes*
Form a team of two members. Complete the *longestConsecutive* function in this [program](test_longest_consecutive_sequence.cpp). You must use a separate-chaining-based hash table. Do not use any of these: std::unordered_map, std::unordered_set, std::map, std::set.
The *longestConsecutive* function takes an unsorted std::vector of integers, and returns the length of the longest consecutive elements sequence.
```console
Example 1:
Input: nums = [100,4,200,1,3,2]
Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.
```
```console
Example 2:
Input: nums = [0,3,7,2,5,8,4,6,0,1]
Output: 9
Explanation: The longest consecutive elements sequence is [0, 1, 2, 3, 4, 5, 6, 7, 8]. Therefore its length is 9.
```
**To complete checkpoint (3,4,5,6)**: Show a TA your program and the test results.
**Note**: checkpoint (3,4,5,6) is very challenging, if you can't get it work during your lab period, make sure you understand it completely after the lab period, because one of the four problems on Test 3, will be about separate-chaining-based hash tables. It's 100% certain.

BIN
labs/12_hash_tables/a.out Executable file

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
#include <iostream>
bool isHappy(int n) {
}
int main() {
// Test cases
// 2, 4, 5, 6, 17, 18, 20 are not happy numbers.
// 1, 7, 10, 13, 19, 23, 28, 68 are 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;
}

View 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;
}

View 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;
}

View 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

View 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

Binary file not shown.

View 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

Binary file not shown.

View 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

Binary file not shown.

View 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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,23 @@
#include <iostream>
#include <vector>
int longestConsecutive(std::vector<int>& nums) {
}
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;
}

View 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;
}