天天看點

第九節課:8.25:哈希第九節課:8.25:哈希

第九節課:8.25:哈希

一、課前回顧:用c++的方式寫了單連結清單:

#pragma once 
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

//單連結清單:
/*
1.單連結清單由結點構成:資料資訊的資料域和存儲下一結點位置的指針域組成結點
2.有頭結點,頭結點不存儲資料資訊
3.一般有頭指針和尾指針分别指向第一個或者是最後一個結點
*/

template <class T>
class CMy_LinkList
{
	struct node
	{
		T data;
		node* pnext;
	};
	node* list_head_node;
public:
	CMy_LinkList();
	~CMy_LinkList();
	void Print();
	void clear();//删除整個連結清單
	bool insert(T const& insert_data, size_t pos);
	bool find(T const& find_data);
	void init(size_t n);
	bool del(size_t pos);
	size_t getLength();//包含頭結點
};
template <class T>
void CMy_LinkList<T>::Print()
{
	node* phead = list_head_node->pnext;
	while (phead)
	{
		printf("%d ",phead->data);
		phead = phead->pnext;
	}
	printf("\n");
}

template <class T>
void CMy_LinkList<T>::init(size_t length)
{
	//先删除連結清單
	clear();

	//建立頭結點
	if (list_head_node == nullptr)
	{
		list_head_node = new node;
		if (list_head_node == nullptr)
			return;
		list_head_node->data = length;
	}

	//初始化時間種子
	srand(time(0));//time(0):從0時0分0秒開始經過了多少秒傳回去

	//定義尾指針,用于尾插
	node* prear = list_head_node;

	//尾插法:
	for (size_t i = 0; i < length - 1; i++)//除去一個頭結點
	{
		node* newnode = new node;
		prear->pnext = newnode;//建立連接配接
		prear = newnode;//尾指針移動到尾結點這裡是尾插法
		newnode->data = rand()%100 + 1;//100以内的數
	}
	
	//插入結束,尾結點的指針域的值為nullptr
	prear->pnext = nullptr;
}

template <class T>
bool CMy_LinkList<T>::find(T const& find_data)
{
	node* pfirst = list_head_node -> pnext;
	while (pfirst)
	{
		if (pfirst->data == find_data)
		{
			return true;
		}
		pfirst = pfirst->pnext;
	}
	return false;
}

/*
1.删除pos不合适,return false;
2.定義頭指針,便于周遊
2.周遊找到pos - 1位置的結點,用phead儲存
3.将要删除結點的位址儲存下來:ptemp = phead->pnext;
4.标準操作:phead->pnext = phead->next->next//指向删除結點的下一節點
5..标準操作:free(ptemp); ptemp = nullptr;
*/
template <class T>
bool CMy_LinkList<T>::del(size_t pos)
{
	//删除位置不正确
	if (pos < 1 || pos > getLength())
	{
		return false;
	}

	//定義頭指針便于周遊
	size_t j = 0;
	node* phead = list_head_node;
	while (phead && j < pos - 1)
	{
		phead = phead->pnext;
	}
	//至此為止:phead指向pos - 1的位置

	//删除結點前的保障工作
	node* pdelete = phead->pnext;//将要删除結點的位址儲存下來
	phead->pnext = pdelete->next;//指向删除結點的下一節點
	delete pdelete;
	pdelete = nullptr;
}

template <class T>
size_t CMy_LinkList<T>::getLength()//不包括頭結點
{
	node* phead = list_head_node->pnext;
	size_t count = 0;
	while (phead)
	{
		phead = phead->pnext;
		count++;
	}
	return count;
}

/*
1.插入pos不合适,return false;
2.這裡采用前插法,在pos前插入該節點
3.周遊找到pos - 1位置的結點,用pnow儲存
沒有找到該位置或者是找到尾結點結束了,
4.建立新結點newnode
5.标準操作:newnode->pnext = pnow->pnext; pnow->next = newnode;
6.插入完成
*/
template <class T>
bool CMy_LinkList<T>::insert(T const& insert_data, size_t pos)//前插法,插入pos前面
{
	//插入位置不合适
	if (pos < 1 || pos > getLength())//length不包括頭結點
	{
		return false;
	}
	//建立頭指針便于周遊
	node* phead = list_head_node;//頭指針指向頭結點
		size_t j = 0;
	while (phead && j < pos - 1)//j=0,phead指向頭結點;j=1;指向第一個結點;剛好對應起來
	{
		phead = phead->pnext;
		j++;
	}
	//至此為止:phead指向第pos - 1的結點的位置

	//建立新結點
	node* newnode = new node;
	newnode->data = insert_data;

	//标準插入操作:
	newnode->pnext = phead->pnext; 
	phead->pnext = newnode;
	return 1;
}

template <class T>
CMy_LinkList<T>::CMy_LinkList()
{
	list_head_node = nullptr;
}

template <class T>
CMy_LinkList<T>::~CMy_LinkList()
{
	clear();
}

//不删除頭結點
/*
1.隻有頭結點不用清空
2.建立一個指針pdelete儲存要删除的結點的位址
2.建立一個指針pnext_node儲存要删除的結點的下一個位址
3.删除該節點
4.接着往下删除,
*/
template <class T>
void CMy_LinkList<T>::clear()
{
	//隻有頭結點,不用清空
	if (list_head_node == nullptr)
		return;
	
	//要删除的節點總是頭結點後面的結點
	node* pdelete = list_head_node -> pnext;

	//儲存下一個要删除的結點的位址
	node* pnext_node;

	while (pdelete)
	{
		pnext_node = pdelete->pnext;
		delete pdelete;
		pdelete = pnext_node;//接着往下删除,如果為空,表明是尾結點,删除尾結點後,删除結束
	}

	list_head_node->pnext = nullptr;
	list_head_node->data = 0;
}

           

二、哈希

1.定義:

哈希法是一種特殊查照方法,又名散列法,希望不通過任何比較,一次存取就能夠得到元素

2.怎麼去設計哈希表:

1.确定表的空間範圍,确定哈希值域

2.構造一個合适的哈希函數;這個函數要確定表中的元素經過該函數的計算之後,函數的傳回值的範圍在值域之内

3.選擇處理沖突的方法(用鍊式結構)

比如:一個遊戲道具的編号,第一個數字代表種類:0表示武器,1表示武器,2表示飾品;

而一把刀和一把槍的編号的第一個都是0,要進行沖突處理;連結清單。

3.哈希函數:定義好哈希函數是哈希表的設計的關鍵

1.自身函數

2.數字分析法(數字疊加法,數字求餘法)

4.哈希示例(c)

#include <stdio.h>
#include <string.h>

#define size 10

//節點
struct Node
{
	int data;
	Node* pnext;
};

//哈希表
struct Hash
{
	Node* Hashtable[size];//0~9
};

//哈希函數
int GetVal(int data)
{
	return data % 10;
}
//哈希表初始化函數
Hash* CreateTable()
{
	Hash* ptable = new Hash;//40個位元組
	for (size_t i = 0; i < size; i++)
	{
		ptable->Hashtable[i] = nullptr;
	}
	return ptable;
}
//哈希插入函數
bool Table_Insert(Hash* ptable, int insert_data)
{
	//哈希表是否建立了?
	if (ptable == nullptr)
		return false;

	//插入:1.無沖2.有沖突
	//定義一個臨時的位置指針,pos,看即将要存的位置上面是否已經存有元素
	Node* pos = ptable->Hashtable[GetVal(insert_data)];

	Node* tempnode = new Node;

	//1.無沖突
	if (pos == nullptr)
	{
		tempnode->data = insert_data;
		tempnode->pnext = nullptr;
		//pos = tempnode;//函數結束後pos釋放,最終ptable->Hashtable[GetVal(insert_data)]還是nullptr
		ptable->Hashtable[GetVal(insert_data)] = tempnode;
	}

	else//2.有沖突
	{
		while (pos->pnext)
		{
			pos = pos->pnext;
		}
		pos->pnext = tempnode;
		tempnode->pnext = nullptr;
		tempnode->data = insert_data;
	}
	return true;
}
//哈希查找函數
Node* Table_Find(Hash ptable, int find_data)//将目标值的位址指針拷貝過來
{
	if (&ptable == nullptr) return nullptr;

	//找到對應的表範圍:
	Node* pos = ptable.Hashtable[GetVal(find_data)];

	if (pos == nullptr)
	{
		return nullptr;
	}
	else
	{
		while (pos)
		{
			if (pos->data == find_data)
			{
				return pos;
			}
			pos = pos->pnext;
		}
	}
	return nullptr;
}
//哈希删除單個值函數
bool Table_Del(Hash* ptable, int delete_data)
{
	//該值不在表内:
	Node* pos = Table_Find(*ptable, delete_data);
	if (pos == nullptr)
		return false;

	//隻有一個節點
	if (ptable->Hashtable[GetVal(delete_data)] == pos)
	{
		//要使執行pos的指針指派為空
		ptable->Hashtable[GetVal(delete_data)] = nullptr;
		delete[]pos;
		pos = nullptr;
	}

	//中間的節點或者使最後的節點:重新周遊查找:
	else
	{
		pos = ptable->Hashtable[GetVal(delete_data)];
		while (pos ->pnext)//該沖突表的第二個節點,第一個節點為1,第二個節點為11
		{
			if (pos->pnext->data == delete_data)
			{
				break;
			}
			pos = pos->pnext;
		}
		//tempdelete儲存要删除的節點
		Node* tempdelete = pos->pnext;
		//第一個節點指向第三個節點
		pos->pnext = tempdelete->pnext;
		//删除第二個節點
		delete[]tempdelete;
		tempdelete = nullptr;
	}
	return true;
}
//哈希删除整個哈希表
bool Table_Clear(Hash* ptable)
{
	if (ptable == nullptr) return false;
	
	//
	for (size_t i = 0; i < size; i++)
	{
		Node* phead = ptable->Hashtable[i];//頭指針
		Node* pdelete;//要删除的節點
		while (phead)
		{
			pdelete = phead;
			phead = phead->pnext;//指向下一個要删除的節點
			delete[] pdelete;
			pdelete = nullptr;
		}
		ptable->Hashtable[i] = nullptr;
	}
	return true;
}

int main()
{
	//Hash* table = CreateTable(table);//這裡傳參數時候會進行拷貝,但是table還沒有初始化
	Hash* table = CreateTable();
	printf("insert success = %d\n", Table_Insert(table, 1));
	printf("insert success = %d\n", Table_Insert(table, 11));
	printf("insert success = %d\n", Table_Insert(table, 111));
	printf("%x\n",Table_Find(*table,2));
	printf("delete success = %d\n", Table_Del(table, 11));
	printf("clear success = %d\n", Table_Clear(table));
	int a = 0;
	return 0;
}
           

哈希示例:(c++)

#pragma once
#include <stdio.h>

/*
1.确定哈希表的範圍
2.設計好哈希函數,(數學分析法)使表中的資料用過計算之後能夠在值域範圍之内
也就是說,函數的傳回值在值域範圍内
3.處理好沖突(連結清單)
*/
class CMy_hash
{
	struct Hash
	{
		int data;
		Hash* pnext;
	};
	Hash* hash_arr[10];//這裡設計為0~9

public:
	CMy_hash();
	~CMy_hash();
	void clear();
public:
	void store(int arr[], int length);//存儲函數
	bool find(int find_data);
	void insert(int  insert_data);//插入單個元素
	bool del(int delete_data);

private:
	int hash(int data);//哈希函數
	Hash* _find(int find_data);//以後根據這個函數來做變化
};

           
#include "CMy_hash.h"

bool CMy_hash::del(int delete_data)//注意:自己的了解:如果删除操作較多的話,建議以空間換取時間,用雙向連結清單就可以免去找上一節點的時間
{
	Hash* pos = _find(delete_data);//找到删除的節點
	if (pos == nullptr) return false;
	else
	{
		if (pos->pnext == nullptr)
		{
			delete pos;
			pos = nullptr;
		}
		else
		{
			Hash* temp = hash_arr[hash(delete_data)];
			while (temp->pnext != pos) temp = temp->pnext;//找到上一個節點
			temp->pnext = pos->pnext;
			delete pos;
			pos = nullptr;
		}
	}
}

void CMy_hash::insert(int  insert_data)
{
	int index = 0;
	//當沒有沖突時:
	index = hash(insert_data);
	//建立一個節點
	Hash* tempnode = new Hash;
	tempnode->data = insert_data;
	tempnode->pnext = nullptr;

	if (hash_arr[index] == nullptr)//空哈希表
	{
		hash_arr[index] = tempnode;
	}
	else
	{
		//找到最後一個節點開始存儲
		Hash* temp = hash_arr[index];//第一個節點
		while (temp->pnext) temp = temp->pnext;//找到temp為最後一個節點
		//建立連接配接
		temp->pnext = tempnode;
		temp = nullptr;
	}
}

bool CMy_hash::find(int find_data)
{
	if (_find(find_data) == nullptr) return false;
	else return true;
}

CMy_hash::Hash* CMy_hash::_find(int find_data)
{
	int index = 0;
	for (size_t i = 0; i < 10; i++)
	{
		index = hash(find_data);
		if (hash_arr[index]->data == find_data)
		{
			return hash_arr[index];
		}
		else if (hash_arr[index]->pnext != nullptr)
		{
			Hash* temp = hash_arr[index]->pnext;
			while (temp)
			{
				if (temp->data == find_data)
				{
					return temp;
				}
				temp = temp->pnext;
			}
		}
		return nullptr;
	}
}

void CMy_hash::store(int arr[], int length)
{
	for (size_t i = 0; i < length; i++)
		insert(arr[i]);
}

/*
哈希函數:傳回元素的值的個位數,按個位數大小找标号
*/
int CMy_hash::hash(int data)
{
	return (data % 10);
}

CMy_hash::CMy_hash()
{
	for (size_t i = 0; i < 10; i++)
	{
		hash_arr[i] = nullptr;
	}
}

CMy_hash::~CMy_hash()
{
	clear();
}

void CMy_hash::clear()
{
	for (size_t i = 0; i < 10; i++)
	{
		if (hash_arr[i] != nullptr)
		{
			//從最後一個節點開始删除
			Hash* temp_delete = hash_arr[i];//頭指針後面的第一個節點
			Hash* temp_pnext;
			while (temp_delete)
			{
				temp_pnext = temp_delete->pnext;
				delete temp_delete;
				temp_delete = temp_pnext;
			}
		}
	}
}
           

繼續閱讀