第九节课: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;
}
}
}
}