第九节课: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;
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值