Computer Science Notes

Notes From CS Undergrad Courses FSU

This project is maintained by awa03

Recap of Hash Table 1 Notes

  1. The basic idea of hash table is to approximate a giant array that is indexed by the key
  2. A hash table is an array where the index of the data is computed (by the hash function) based on the key of the data

Index = hash(key) % table_size;

  1. The situation when two keys are hashed into the same index is called a conflict or collision
  2. A good hash function doesn't remove all conflicts. It statistically minimized the probability of collision across the key space

Separate Chaining

![[Firefox_Screenshot_2023-11-17T13-55-38.899Z.png]]

Search with Separate Chaining

Inserting with Separate Chaining

Delete with Separate Chaining

Separate Chaining

Implementation


template <typename HashedObj>
class HashTable
{
public:
	explicit HashTable(int size=101);
	bool contains(HashedObj& x) const;

	void makeEmpty();
	bool insert(const HashedObj& x);
	bool insert(HashedObj&& x);
	bool remove(const HashedObj& x);

private:
	vector<list<HashedObj>> theLists;
	int currSize;

	void rehash();
	size_t myhash(const HashObj& x) const;

}

Hashed Object

template <typename key>
class Hash {
public:
	size_t operator()(const key& k) const;
	
};
template <>
class Hash<string>{ // Implementation
public:
	size_t operator()(const string& key){
		// ...
	}
};

Class Example

class Employee{
public:
	const string& getName() const{
		return name;
	}
	bool operator==(const Employee& rhs) const
	{
		return getName() == rhs.getName();	
	}
	bool operator!=(const Employee& rhs)const {
		return !(*this == rhs);
	}
private:
	string name;
	double salary;
	int seniority;
	// Aditional private members 
};
template<>
class hash<Employee>{
public:
	size_t operator()(const Employee& item){
		static hash<string> hf;
		return hf(item.getName());
	}
};

Separate Chaining

// Separate Chaining 
size_t myhash(const HashedObj& x){
	static hash<HashedObj> hf;
	return hf(x) % theList.size();
}

// Separate Chaining Cont'd
// More Function Definitions
void makeEmpty(){
	for(auto& theList: theList){
		theList.clear();
	}

}

bool contains(const HashedObj& x) const{
	auto & whichList = theList[myhash(x)];
	return find(begin(whichList), end(whichList) != end(whichList));
}

bool remove(const HashObj& x){
	auto& whichList = theList[myhash(x)];
	auto itr = find(begin(whichList), end(whichList), x);

	if(itr == end(whichList)){
		return false;
	}
	whichList.erase(itr);
	--currentSize;
	return true;
}

bool insert(const HashedObj& x){
	auto& whichList = theList[myhash(x)];
	if(find(begin(whichList), end(whichList), x) != end(whichList)){
		return false;
	}
	whichList.push_back(x);

	// rehash... 
	if(++currentsize > theList.size()){
		rehash();
	}
	return true;
}

Hash Tables without Chaining

Linear Probing

Insert (assume no duplicate keys)

  1. Index = hash(key) % table_size;
  2. If table[index] is empty, put informations (key and others) in entry table[index]
  3. If table[index] is not empty then, index++; index = index % table_size; goto 2

Search (key)

  1. Index = hash(key) % table_size;
  2. If (table[index] is empty) return -1 (not found)
  3. Else if (table[index].key == key) return index;
  4. index++; index = index % table_size; goto 2;

![[Firefox_Screenshot_2023-11-17T23-08-09.550Z.png]]

Delete

Quadratic Probing

![[Firefox_Screenshot_2023-11-17T23-10-36.771Z.png]]

// Nested within Hash Table class
enum EntryType{
	ACTIVE,
	EMPTY,
	DELETED
};

struct HashEntry{
	HashedObj element;
	EntryType info;
	HashedEntry(const HashedObj& e = HashedObj{}, EntryType != EMPTY)
		: element{e}, info{|}{}
	HashEntry(HashedObj&& e, EntryType != EMPTY)
		: element{std::move(e)}, info{|}{}
};

Double Hashing