文章目录
1. 前言
HashMap(5)中主要介绍了我的一个数据结构实验课设,而这个课设是实现一个HashMap,政整个HashMap的实现是以java JDK1.8的HashMap为参考实现的,其实说是参考,大部分其中的方法是和java源码一样的,在红黑树的方面则是自己设计的。对于这个课设,也有一些和HashMap不一样的地方,比如Key和Value都是使用int类型的,数组下标不再存放无头单链表,而是存放有头单链表,这其实都是其中一些坑,后来慢慢改成这样的。对于HashMap的学习,我一直都是认为尽管看了10遍都不如自己动手敲1遍,好记性不如烂笔头是真的。敲完一遍之后自己对底层的一些设计是比较有清晰的一个认识的,尤其是扩容处理和红黑树树化处理这块,也是做了一个HashMap课设之后才知道原来在树化的时候不仅仅维护了树,还维护了双向链表。以及其中一些常量的设计,都会有一个全新的认识。
第一篇文章:源码解析系列:HashMap(1)
第二篇文章:源码解析系列:HashMap(2)
第三篇文章:源码解析系列:HashMap(3)
第四篇文章:源码解析系列:HashMap(4)
第五篇文章:源码解析系列:HashMap(5)
2. 开始C语言之旅
话不多说了,直接上代码,代码中都有很详细的解析了,不需要再多讲。使用工具:VS2019,把代码复制到VS里直接运行即可。
1、常量头文件Common.h
#define _CRT_SECURE_NO_WARNINGS
#ifndef aaa
#define aaa
#endif // ! aaa
//常用头文件
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include <time.h>
#include<float.h>
#include<math.h>
//常用常量
#define OK 1
#define ERROR 0
#define MYOVERFLOW -1
#define ARGS_ERROR -2
#define TRUE 1
#define FALSE -0
//默认初始化容量
#define DEFAULT_INITIAL_CAPACITY 1<<4 //16
//字符串大小
#define STR_MAX_SIZE 1024
//最大的容量
#define MAXIMUM_CAPACITY 1<<30 //1073741824
//int最大容量
#define INT_MAX 2147483647
//不存在,专门对于那些不存在的结点使用的
#define UN_EXIT -2147483648
//负载因子
#define DEFAULT_LOAD_FACTOR 0.75
//树化条件
#define TREEIFY_THRESHOLD 8
//红色
#define RED 1
//黑色
#define BLACK 0
//最小扩容树的长度 64
//测试
#define MIN_TREEIFY_CAPACITY 6
//去树化的最大值
#define UNTREEIFY_THRESHOLD 6
//常用类型定义
typedef int Status;
typedef int key;
typedef int Key;
typedef int Value;
//typedef int rcdType;#pragma once
2、map头文件,包含方法以及结构体的定义
#include "Common.h"
typedef struct {
Key key; //键
Value value; //值
int hashCode;//hashCode
int hash; //hash
}DataType; //对基本数据类型进行封装,类似泛型
//结点
typedef struct hashnode{
DataType data;
int isRed; //红色还是黑色
struct hashnode* next, *prev; //key冲突时,通过next指针进行连接, prev:树化的时候使用
struct hashnode* lchild, *rchild, *parent; //树化的时候通过这个连接
}HashNode, *hashNode;
typedef struct {
//负载因子
float loadFactor;
//键值对大小
int size;
//修改次数
int modCount;
//扩容阈值: 容量(桶数量)* 负载因子
int threshold;
//桶的长度
int length;
//桶
HashNode* table;
}HashMap, *hashmap;
//菜单
void meun();
/*
*工具方法
*/
//计算hashCode
int hashCode(char* a);
//计算char的长度
int findLength(char* c);
//求初始容量:2的n次方
int numberOfLeadingZeros(unsigned int i);
int tableSizeFor(int cap);
//计算hash
int hash(hashNode node);
//通过key计算hash
int hashByKey(Key key);
//判断浮点数合不合理
const int show_classification(float x);
//根据key和value封装node
hashNode package(Key key, Value value);
//判断两个key是不是相同的
Status equals(Key var1, Key var2);
//比较两个key大小
Status compare(Key var1, Key var2);
//两个结点赋值
void initByOther(HashNode& node1, hashNode& node2);
//对链表进行清除
void clear(hashNode table);
/*
开始构建hashMap,三种构造方法
*/
int init1(hashmap& hashMap);
int init2(hashmap& hashMap,int capacity);
int init3(hashmap& hashMap, int initialCapacity, float loadFactor);
//判断hashmap是不是空
int isEmpty(hashmap hashMap);
//获取键值对的数量
int size(hashmap hashMap);
//根据键key获取value
Value get(Key key, hashmap hashMap);
//根据hash和key获取结点
hashNode getNode(int hash, Key key, hashmap hashMap);
//put加入结点,可以进行替换
Status put(Key key, Value value, hashmap& hashMap);
//put加入结点,不可以进行替换
Status putNotReplace(Key key, Value value, hashmap& hashMap);
//统一加入的结点
/**
hash:hash值
key:键
value:值
onlyIfAbsent:当这个参数为1的时候,表示同key情况下不可被覆盖
evict:用于linkHashmap的,这里不要求
*/
Status putVal(int hash, Key key, Value value, int onlyIfAbsent,
int evict, hashmap& hashMap);
//分配空间
hashNode resize(hashmap& hashMap);
//清空hashMap
void clear(hashmap &hashMap);
//hashMap是否包含某个key
Status containsKey(Key key, hashmap& hasmMap);
//hashMap是否包含某个Value
Status containsValue(Value value, hashmap hashMap);
//真正的remove方法
Status removeNode(int hash, Key key, Value value, int matchValue, int movable, hashmap &hashMap);
//通过key和value进行移除
Status removeByKeyValue(Key key, Value value, hashmap& hashMap);
//通过key移除
Value removeByKey(Key key, hashmap& hashMap);
//替换
Status replaceByKeyValue(Key key, Value oldValue, Value newValue, hashmap& hashMap);
//根据key来进行匹配
Status replaceByKey(Key key, Value newValue, hashmap& hashMap);
//初始树化操作
hashNode treeifBin(hashNode table, int hash, hashmap hashMap);
//树化
hashNode treeif(hashNode node, hashNode table, int hash, hashmap hashMap);
//重新对root根节点赋值
void moveRootToFront(hashNode& table, hashNode root, hashmap hashMap);
//检测红黑树是否规范
void checkInvariants(hashNode root);
//对红黑树结点进行分流
void split(hashNode& node, hashmap& hashmap, hashNode& tab, int index, int bit, int oldCap);
//去树化
hashNode untreeify(hashmap hashMap, hashNode node);
//树结点转化成链表节点
hashNode beTreeToNode(hashNode p);
3、树的头文件,包含方法
/*
红黑树接口
*/
#include "map.h"
//求父节点
hashNode parentOf(hashNode node);
//判断结点是不是红色的
int RedOrNot(hashNode node);
int isBlack(hashNode node);
//中序打印
void inOrderPrint(hashNode root);
void inOrderPrint_1(hashNode root);
//获取根节点
hashNode getRoot(hashNode root);
//左旋
void leftRotate(hashNode& root, hashNode x);
//右旋
void rightRotate(hashNode& root, hashNode x);
//插入方法
void insert(Key key, Value value, hashNode& root, hashmap& hashMap);
//插入
void insertReal(hashNode node, hashNode& root, hashmap& hashMap);
//插入修复
void insertFixUp(hashNode& node, hashNode& root);
//删除叶子结点
void deleteNode(hashmap& hashmap, int hash, Key key, Value value, hashNode& root, hashNode node);
//真正的删除
void deleteNodeReal(hashNode node, hashNode& root, hashmap& hashMap);
//删除修复
void deleteFixUp(hashNode x, hashNode parent, hashNode& root);
//删除的辅助方法
//1. 子树替换
void transplate(hashNode u, hashNode v, hashNode& root);
//2.查找最小的元素
hashNode searchNode(Value key, hashNode root);
//3. 最小关键字元素
hashNode searchMin(hashNode root);
/**
* 画树函数
*/
void draw_level(hashNode* node, bool left, char* str); // 画分支
void draw(hashNode* root); // 画根节点
void drawLine(hashNode root); // 链表的打印
void print(hashNode table, hashmap hashMap); // hashmap的打印
4、main.h,主函数.h
#include "RBTree.h"
int main(void) {
hashmap hashMap = NULL;
meun();
char want;
while (1) {
printf("请输入选择(输入g显示菜单):");
while ((want = getchar()) != '\n' && want != EOF) {
switch (want) {
case 'a': {
if (init1(hashMap) != OK) {
printf("初始化失败(已初始化或者异常)\n");
break;
}
printf("初始化成功\n");
break;
}
case 'b': {
int capacity = 0;
printf("请输入扩容阈值:");
scanf("%d", &capacity);
if (capacity < 0 || init2(hashMap, capacity) != OK) {
printf("初始化失败(已初始化或者异常)\n");
break;
}
printf("初始化成功\n");
break;
}
case 'c': {
int capacity = 0;
printf("请输入扩容阈值:");
scanf("%d", &capacity);
float loadFactor = 0;
printf("请输入负载因子:");
scanf("%f", &loadFactor);
if (capacity < 0 || loadFactor < 0.0 || init3(hashMap, capacity, loadFactor) != OK) {
printf("初始化失败(已初始化或者异常)\n");
break;
}
printf("初始化成功\n");
break;
}
case 'd': {
Key key = 0;
printf("请输入key:");
scanf("%d", &key);
Value value = 0;
printf("请输入value:");
scanf("%d", &value);
Status result = put(key, value, hashMap);
if (result == ERROR) {
printf("插入失败\n");
break;
}
printf("插入成功\n");
break;
}
case 'e': {
Key key = 0;
printf("请输入key:");
scanf("%d", &key);
Value value = 0;
printf("请输入value:");
scanf("%d", &value);
Status result = putNotReplace(key, value, hashMap);
if (result == ERROR) {
printf("插入失败\n");
break;
}
printf("插入成功\n");
break;
}
case 'f': {
Key key = 0;
printf("请输入key:");
scanf("%d", &key);
Status result = containsKey(key, hashMap);
if (result == OK) {
printf("包含该key\n");
break;
}
else {
printf("不包含该key\n");
break;
}
}
case 'g': {
meun();
break;
}
case 'h': {
if (hashMap == NULL || hashMap->table == NULL) {
printf("没有结点\n");
break;
}
print(hashMap->table, hashMap);
break;
}
case 'i': {
Key key = 0;
printf("请输入key:");
scanf("%d", &key);
Value oldValue = 0;
printf("请输入新的value值:");
scanf("%d", &oldValue);
if (replaceByKey(key, oldValue, hashMap) == ERROR) {
printf("替换失败\n");
break;
}
printf("替换成功\n");
break;
break;
}
case 'j': {
Key key = 0;
printf("请输入key:");
scanf("%d", &key);
Value value = get(key, hashMap);
if (value == UN_EXIT) {
printf("没有找到\n");
break;
}
printf("找到了对应得value,value=%d\n", value);
break;
}
case 'k': {
Key key = 0;
printf("请输入key:");
scanf("%d", &key);
Value oldValue = 0;
printf("请输入value值:");
scanf("%d", &oldValue);
Value newValue = 0;
printf("请输入替换后的value值:");
scanf("%d", &newValue);
if (replaceByKeyValue(key, oldValue, newValue, hashMap) == ERROR) {
printf("替换失败\n");
break;
}
printf("替换成功\n");
break;
}
case 'l': {
Key key = 0;
printf("请输入key:");
scanf("%d", &key);
Value value = removeByKey(key, hashMap);
if (value == ERROR) {
printf("删除失败\n");
break;
}
printf("删除成功\n");
break;
}
case 'm': {
Key key = 0;
printf("请输入key:");
scanf("%d", &key);
Value value = 0;
printf("请输入value值:");
scanf("%d", &value);
Status result = removeByKeyValue(key, value, hashMap);
if (result == ERROR) {
printf("删除失败\n");
break;
}
printf("删除成功\n");
break;
}
case 't': {
for (int i = 11; i <= 99; i += 11) {
put(i, i, hashMap);
}
break;
}
case 'u': {
for (int i = 99; i >= 66; i -= 11) {
removeByKey(i, hashMap);
}
break;
}
default:break;
}
}
}
return 0;
}
5、map.cpp,实现map.h头文件里面的函数
#include "RBTree.h"
/*
工具方法
*/
int hashCode(char* a) {
if (a == NULL) {
return 0;
}
else {
int result = 1;
char* var1 = a;
int var2 = findLength(a);
for (int var3 = 0; var3 < var2; ++var3) {
char element = var1[var3];
result = 31 * result + element;
}
return result;
}
}
//查找长度
int findLength(char* c) {
int sum = 0;
for (sum; c[sum] != '\0'; sum++) {
sum++;
}
return sum;
}
//求有多少个0
int numberOfLeadingZeros(unsigned int i) {
if (i <= 0) {
return i == 0 ? 32 : 0;
}
else {
int n = 31;
if (i >= 65536) {
n -= 16;
i >>= 16;
}
if (i >= 256) {
n -= 8;
i >>= 8;
}
if (i >= 16) {
n -= 4;
i >>= 4;
}
if (i >= 4) {
n -= 2;
i >>= 2;
}
return n - (i >> 1);
}
}
//求容量大小
int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
//求结点的左子树
hashNode getLeft(hashNode node) {
return node->lchild;
}
//求结点的右子树
hashNode getRight(hashNode node) {
return node->rchild;
}
//判断颜色,红色:1,黑色:0
int isRed(hashNode node) {
return node->isRed;
}
//判断两个key是不是相同的
Status equals(Key var1, Key var2) {
return var1 == var2 ? OK : ERROR;
}
//比较两个key的大小
//-1,第2个大,0,相同,1,第1个大
Status compare(Key var1, Key var2) {
if (var1 > var2) {
return 1;
}
else if (var1 == var2) {
return 0;
}
else {
return -1;
}
}
//封装key value
hashNode package(Key key, Value value) {
char temp[STR_MAX_SIZE] = {0};
//把一个数转化成10进制字符串求hashCode
_itoa(key, temp, 10);
hashNode node = (hashNode)malloc(sizeof(HashNode));
//key一定不为NULL
if (node == NULL || key == NULL) {
return NULL;
}
node->lchild = NULL;
node->rchild = NULL;
node->next = NULL;
node->prev = NULL;
node->parent = NULL;
//加入的结点一定为红色结点
node->isRed = RED;
node->data.key = key;
node->data.value = value;
//加入hashCode
node->data.hashCode = hashCode(temp);
//加入hash值
node->data.hash = hash(node);
return node;
}
//求hash
int hash(hashNode node) {
unsigned int h;
return (node == NULL) ? 0 : (h = node->data.hashCode) ^ (h >> 16);
}
//通过key计算hash
int hashByKey(Key key) {
char temp[STR_MAX_SIZE] = {0};
//把一个数转化成10进制字符串求hashCode
_itoa(key, temp, 10);
unsigned int h;
return (key == NULL) ? 0 : (h = hashCode(temp)) ^ (h >> 16);
}
//判断一个浮点数是不是合法的
const int show_classification(float x)
{
switch (fpclassify(x))
{
case FP_INFINITE:
case FP_NAN:
case FP_NORMAL:return OK;
case FP_SUBNORMAL:
case FP_ZERO:
return ARGS_ERROR;
default:
return -1;
break;
}
}
/*
开始构建hashMap
*/
//默认参数
int init1(hashmap& hashMap){
if (hashMap != NULL) {
return ERROR;
}
hashMap = (hashmap)malloc(sizeof(HashMap));
if (hashMap == NULL) {
return MYOVERFLOW;
}
//初始化默认的16和0.75
hashMap->loadFactor = DEFAULT_LOAD_FACTOR;
hashMap->length = 0;
hashMap->modCount = 0;
hashMap->size = 0;
//懒加载
hashMap->table = NULL;
hashMap->threshold = 0;
return OK;
}
//自定义容量
int init2(hashmap& hashMap, int capacity) {
if (hashMap != NULL || capacity <= 0) {
return ERROR;
}
return init3(hashMap, capacity, DEFAULT_LOAD_FACTOR);
}
//自定义容量和负载因子
int init3(hashmap& hashMap, int initialCapacity, float loadFactor) {
if (hashMap != NULL || initialCapacity <= 0 || loadFactor <= 0.0) {
return ERROR;
}
//参数判断
hashMap = (hashmap)malloc(sizeof(HashMap));
if (hashMap == NULL) {
return MYOVERFLOW;
}
//初始容量不合理
if (initialCapacity < 0 || initialCapacity > MAXIMUM_CAPACITY) {
return ARGS_ERROR;
}
//负载因子不合理
if (loadFactor <= 0 || show_classification(loadFactor) == ARGS_ERROR) {
return ARGS_ERROR;
}
hashMap->loadFactor = loadFactor;
//2的n次方
hashMap->threshold = tableSizeFor(initialCapacity);
hashMap->length = 0;
hashMap->modCount = 0;
hashMap->table = NULL;
hashMap->size = 0;
return OK;
}
//map是不是空
int isEmpty(hashmap hashMap) {
return hashMap->size == 0 ? 1 : 0;
}
//获取键值对的数量
int size(hashmap hashMap) {
return hashMap->size;
}
//根据key获取value
Value get(Key key, hashmap hashMap) {
if (key == UN_EXIT || hashMap == NULL) {
return UN_EXIT;
}
hashNode node;
hashNode temp = package(key, NULL);
return (node = getNode(hash(temp), key, hashMap)) == NULL ? UN_EXIT : node->data.value;
}
//获取结点
hashNode getNode(int hash, Key key, hashmap hashMap) {
if (key == UN_EXIT && hashMap == NULL) {
return NULL;
}
//先获取所有的桶
hashNode table = NULL;
hashNode first, e;
int length = 0;
Key k = NULL;
if ((table = hashMap->table) != NULL && ((length = hashMap->length) != 0)
&& ((first = &(table[(length - 1) & hash]))) != NULL
&& first->data.key != UN_EXIT) {
//带头的链表
first = first->next;
//证明有结点,并且根据key找到了本来在哪个下标
if (first->data.hash == hash && equals(key, first->data.key) == OK) {
return first;
}
//如果是树
if (first->lchild != NULL || first->rchild != NULL) {
//左子树或者右子树不是空,证明此时是树,使用树的查找
return searchNode(key, first);
}
//否则证明是链表
//不是第一个结点
if ((e = first->next) != NULL) {
//存在下一个结点,遍历查找
do {
if (e->data.hash == hash &&
((k = e->data.key) == key || (key != NULL && equals(key, k)))) {
return e;
}
} while ((e = e->next) != NULL);
}
}
return NULL;
}
//put加入结点,可以进行替换
Status put(Key key, Value value, hashmap &hashMap) {
if (hashMap == NULL || key == UN_EXIT || value == UN_EXIT) {
return NULL;
}
hashNode node = getNode(hashByKey(key), key, hashMap);
//如果存在,就直接替换
if (node != NULL) {
node->data.value = value;
return OK;
}
return putVal(hashByKey(key), key, value, FALSE, FALSE, hashMap);
}
Status putNotReplace(Key key, Value value, hashmap& hashMap) {
if (hashMap == NULL || key == UN_EXIT || value == UN_EXIT) {
return NULL;
}
hashNode node = getNode(hashByKey(key), key, hashMap);
//如果存在,就返回错误
if (node != NULL) {
return ERROR;
}
return putVal(hashByKey(key), key, value, TRUE, FALSE, hashMap);
}
//统一加入的结点
/**
hash:hash值
key:键
value:值
onlyIfAbsent:当这个参数为1的时候,表示同key情况下不可被覆盖
evict:用于linkHashmap的,这里不要求
*/
Status putVal(int hash, Key key, Value value, int onlyIfAbsent,
int evict, hashmap &hashMap) {
Value oldValue = NULL;
hashNode tab;
//tableLength:表的长度
//i:数组下标
hashNode first; int tableLength, i;
//判断hashMap是不是空,如果是空,证明是第一次进入的
if ((tab = hashMap->table) == NULL || (tableLength = hashMap->length) == 0) {
//开始初始化hashMap的table
tab = resize(hashMap);
tableLength = hashMap->length;
hashMap->table = tab;
}
//i:下标
i = (tableLength - 1) & hash;
hashNode root = hashMap->table[i].next;
//把tab[i]的开头赋值给first
if ((first = &tab[i])->data.key == UN_EXIT) {
//如果是空,证明该下标没有数据
tab[i].next = package(key, value);
//改成0,防止下次还进来
tab[i].data.key = 0;
}
else {
//证明有数据
//有头单链表
first = first->next;
hashNode e = NULL;
Key key1 = UN_EXIT;
//hash相同,key又相同,key赋值
if (first->data.hash == hash && equals(first->data.key, key)) {
e = first;
}
else if (first->lchild != NULL || first->rchild != NULL) {
//树
insert(key, value, root, hashMap);
}
else {
//链表
for (int binCount = 0; ; binCount++) {
e = first->next;
if (e == NULL) {
first->next = package(key, value);
//如果大于8,就开始树化
if (binCount >= TREEIFY_THRESHOLD - 1) {
// 树化
tab = treeifBin(tab, hash, hashMap);
}
break;
}
//如果找到一个相同的,就直接break替换
if (e->data.hash == hash && equals(key, first->data.key)) {
break;
}
first = e;
}
}
//到了这里,e不为空,证明中途找到了一模一样的key,进行替换
if (e != NULL) {
Value oldValue = e->data.value;
//onlyIfAbsent为0的时候或者这个值是空的才可以替换
if (!onlyIfAbsent || oldValue == NULL){
//进行替换
e->data.value = value;
return OK;
}
}
}
//添加修改次数
hashMap->modCount++;
//kv个数+1
hashMap->size++;
//超出界限
if (hashMap->size > hashMap->threshold) {
//重新分配空间
tab = resize(hashMap);
}
hashMap->table = tab;
//重新指向新的table
return OK;
}
//重新分配空间,改变存储结构
hashNode resize(hashmap &hashMap) {
//旧数组
hashNode oldTab = hashMap->table;
//旧的数组长度
int oldCap = hashMap->length;
//旧的扩容阈值
int oldThr = hashMap->threshold;
//新的数组长度和新的容量
int newCap, newThr = 0;
//正常扩容,数组中有长度了
if (oldCap > 0) {
//旧的容量(数组长度)大于0,证明已经初始化了
if (oldCap > MAXIMUM_CAPACITY) {
//如果容量已经大于最大的了,阈值直接设置成最大的,不再扩容了
hashMap->threshold = INT_MAX;
return oldTab;
}
//判断如果新的长度大小为旧的扩展一倍,并且小于最大的容量值,16是默认的初始容量,也可以自定义容量,使用init1时可以这样扩容
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) {
newThr = oldThr << 1;
}
}
//oldCap = 0, oldThr不等于0,else if和else都是初始化
//容量为0但是阈值大于0对应java的有默认值得构造函数
//1. new HashMap(initCap, loadFactory)
//2. new HashMap(initCap)
else if (oldThr > 0) {
//新的长度大小=旧的阈值
newCap = oldThr;
}
//init1()
else {
//两个都是0,证明是刚刚初始化
newCap = 16;
newThr = 12;
}
//对应HashMap中的以下情况
//1. new HashMap(initCap, loadFactory)
//2. new HashMap(initCap)
//3. new HashMap(map) 并且这个map有数据
//4. oldCap < 16
//newThr为0时,通过newCap和负载因子计算出一个newThr
if (newThr == 0) {
float ft = (float)newCap * hashMap->loadFactor;
//大部分都是相乘取整了
newThr = newCap < 1073741824 && ft < 1.07374182E9F ? (int)ft : 2147483647;
if (newThr == 0) {
//防止相乘得到的结果是空的情况
newThr = 1;
}
}
//赋值给新的扩容的阈值以及新的容量
hashMap->threshold = newThr;
//开始扩容
//经过计算,当16->32之后,原来16上面一部分会分到32上面
//创建
hashNode newTab = (hashNode)malloc(newCap * sizeof(HashNode));
if (newTab == NULL) {
return NULL;
}
//防止野指针
for (int i = 0; i < newCap; i++) {
newTab[i].data.hash = 0;
newTab[i].data.hashCode = 0;
newTab[i].isRed = RED;
newTab[i].data.key = UN_EXIT;
newTab[i].data.value = UN_EXIT;
newTab[i].next = NULL;
newTab[i].prev = NULL;
newTab[i].lchild = NULL;
newTab[i].rchild = NULL;
newTab[i].parent = NULL;
}
//复制长度
hashMap->length = newCap;
//旧的不为空,证明原来的旧表有数据
if (oldTab != NULL) {
for (int j = 0; j < oldCap; j++) {
hashNode node;
//有数据
if ((node = &(oldTab[j])) != NULL && node->data.key != UN_EXIT) {
//证明有头单链表有数据
node = node->next;
if (node->next == NULL) {
//证明只有一个头
//node->data.hash & (newCap - 1)计算出新节点的地址
//由于newCap = oldCap*2,所以其余位不变,比如16->32,多了一个第五位,所以第五位是1,新下标+16,是0,就保留在旧下标
newTab[node->data.hash & (newCap - 1)].next = node;
//标志位设置为0
newTab[node->data.hash & (newCap - 1)].data.key = 0;
}
else if (node->lchild != NULL || node->rchild != NULL) {
//此时是树,调用树的更新,低位到高位
split(node, hashMap, newTab, j, newCap, oldCap);
}else{
//链表
//1. 低位链表头和尾
//低位链表:存放在扩容之后的数组的下标和原来的数组的下标位置一样
hashNode loHead = NULL, loTail = NULL;
//2. 高位链表头和尾
//高位链表:存放在扩容之后的数组的下标为当前数组下标位置+扩容前数
hashNode hiHead = NULL, hiTail = NULL;
hashNode next = NULL;
while (node != NULL) {
next = node->next;
//判断:oldCap一定是2的n次方,所以只有一个1
//oldCap例子:0b 10000
//hash 0b....11111
//hash 0b....01111
if ((node->data.hash & oldCap) == 0) {
//证明高位是0,应该放在低位,也就是01111的情况
//如果尾部是null,证明第一次进来,放在头部
if (loTail == NULL) {
loHead = node;
}
else {
loTail->next = node;
}
loTail = node;
}
else {
//高位是1,应该放在高位
if (hiTail == NULL) {
hiHead = node;
}
else {
hiTail->next = node;
}
hiTail = node;
}
node = next;
}
if (loTail != NULL) {
//低位有数据
loTail->next = NULL;
newTab[j].next = loHead;
//标志位设置为0证明有数据
newTab[j].data.key = 0;
}
if (hiTail != NULL) {
hiTail->next = NULL;
//标志位设置为0证明有数据
newTab[j + oldCap].next = hiHead;
newTab[j + oldCap].data.key = 0;
}
}
}
}
//赋值给hashMap
hashMap->table = newTab;
//释放掉旧的hash表
free(oldTab);
}
return newTab;
}
//树的分配
/*
1. hashmap
2. table表
3. table表的下标
4. 旧的数组
*/
void split(hashNode& node, hashmap& hashmap, hashNode& tab, int index, int bit, int oldCap) {
hashNode b = node;
//低位头和尾和高位头和尾
hashNode loHead = NULL, loTail = NULL;
hashNode hiHead = NULL, hiTail = NULL;
//分开后低位的链表(树)和高位的链表(树)
int lcount = 0;
int hcount = 0;
//next:当前节点的下一个
hashNode e = b, next = NULL;
for (e; e != NULL; e = next) {
next = e->next;
e->next = NULL;
//应该在低位
if ((e->data.hash & oldCap) == 0) {
//第一次进入e的前一个一定是NULL
e->prev = loTail;
//e的前一个是空,证明e是头
if (e->prev == NULL) {
loHead = e;
}
//e不是头
else {
loTail->next = e;
}
loTail = e;
lcount++;
}
else {
//应该在高位
//第一次进入一定是NULL
e->prev = hiTail;
//e的前一个是空,证明e是头
if (e->prev == NULL) {
hiHead = e;
}
//e不是头
else {
hiTail->next = e;
}
hiTail = e;
hcount++;
}
}
//到此为止双向链表创建完成了
//判断低位头有没有空,如果空了,证明所有的结点都到高位去了
if (loHead == NULL) {
//设置低位为没有数据
tab[index].data.key = UN_EXIT;
}
//链表赋值完成之后开始下面的树化和去树化操作
if (loHead != NULL) {
if (lcount < UNTREEIFY_THRESHOLD) {
//低位链表数目小于6个,证明可以开始去树化了
tab[index].next = untreeify(hashmap, loHead);
tab[index].next->prev = &tab[index];
tab[index].data.key = 0;
}
else {
//不小于6个,树化
tab[index].next = loHead;
tab[index].next->prev = &tab[index];
tab[index].data.key = 0;
//如果高位首节点不为空,说明原来的红黑树已经被拆分成两个链表了
if (hiHead != NULL)
//开始树化
treeif(loHead, hashmap->table, loHead->data.hash, hashmap);
}
}
if (hiHead != NULL) {
if (hcount < UNTREEIFY_THRESHOLD) {
//低位链表数目小于6个,证明可以开始去树化了
tab[index + oldCap].next = untreeify(hashmap, hiHead);
tab[index + oldCap].data.key = 0;
tab[index + oldCap].next->prev = &tab[index + oldCap];
}
else {
//不小于6个,树化
tab[index + oldCap].next = hiHead;
tab[index + oldCap].data.key = 0;
tab[index + oldCap].next->prev = &tab[index + oldCap];
//如果低位首节点不为空,说明原来的红黑树已经被拆分成两个链表了
if (loHead != NULL)
//开始树化
treeif(hiHead, hashmap->table, hiHead->data.hash, hashmap);
}
}
}
//去树化
hashNode untreeify(hashmap hashMap, hashNode node) {
//辅助节点
hashNode hd = NULL, t1 = NULL;
//p:用于遍历树
hashNode q = node;
for (q; q != NULL; q = q->next) {
//对每一个节点从树节点转化为链表节点,双向链表也去掉
//获取普通链表节点
hashNode p = beTreeToNode(q);
if (t1 == NULL) {
hd = p;
}
else {
t1->next = p;
}
t1 = p;
}
return hd;
}
hashNode beTreeToNode(hashNode p){
return package(p->data.key, p->data.value);
}
//初步树化
hashNode treeifBin(hashNode table, int hash, hashmap hashMap) {
int length = hashMap->length;
int index = (length - 1) & hash;
hashNode e = NULL;
//数组长度小于64的时候不可以树化
if (table == NULL || hashMap->size < MIN_TREEIFY_CAPACITY) {
return resize(hashMap);
}
//找到的下标的首个结点不是NULL
else if ((e = &table[index]) != NULL) {
//首先把单向链表转化为双向链表
while (e != NULL && e->next != NULL) {
e->next->prev = e;
e = e->next;
}
//真正树化
return treeif(&table[index], table, hash, hashMap);
}
return NULL;
}
//红黑树树化,在维护数的同时也要维护双向链表
hashNode treeif(hashNode node ,hashNode table, int hash, hashmap hashMap) {
//根节点
hashNode root = NULL;
//临时结点
hashNode x = node->next;
hashNode next = NULL;
//遍历链表
for (x; x != NULL; x = next) {
next = x->next;
x->lchild = x->rchild = NULL;
//根结点
if (root == NULL) {
//把头设置成root
root = x;
root->isRed = BLACK;
}
//不是根结点
else {
Key key = x->data.key;
int hash = x->data.hash;
hashNode temp = root;
while(1) {
//根结点的hash
int tempHash = temp->data.hash;
//-1. 往左找 1:往右找 0:相等,不会出现,因为key是一定不相等的
int result = 0;
//根结点hash>当前结点的hash,往左找位置插入
if (tempHash > x->data.hash) {
result = -1;
}
//这里使用hash值作为比较来查找红黑树
else if (tempHash < x->data.hash) {
result = 1;
}
else {
if (compare(temp->data.key, x->data.key)) {
result = -1;
}
else if (compare(temp->data.key, x->data.key) < 0) {
result = 1;
}
else {
result = 0;
}
}
//记录temp,作为父节点赋值
hashNode tp = temp;
temp = result < 0 ? temp->lchild : temp->rchild;
//查找到叶子结点了
if (temp == NULL) {
x->parent = tp;
//判断此时x是在tp的左边还是右边
if (result < 0) {
tp->lchild = x;
}
else if (result > 0) {
tp->rchild = x;
}
//插入后要调整平衡
insertFixUp(x, root);
break;
}
}
}
}
//重新对root赋值
moveRootToFront(table, root, hashMap);
return table;
}
//重新对root赋值
//确保输入的root作为table的第一个
void moveRootToFront(hashNode& table, hashNode root, hashmap hashMap) {
int length = 0;
if (table == NULL || root == NULL) {
return;
}
length = hashMap->length;
if (table != NULL && root != NULL && length != 0) {
int index = (length - 1) & root->data.hash;
//比如11-22-33-44-55-66-77
//11是first,44是root
//调用完成之后的结果是:44-11-22-33-55-66-77
//这里用一个node,不用指针是怕造成死循环44-44-44-44
hashNode first = table[index].next;
if (first == root) {
//不必要调整
return;
}
if (root->next != NULL) {
root->prev->next = root->next;
root->next->prev = root->prev;
}
root->next = table[index].next;
table[index].next->prev = root;
table[index].next = root;
root->prev = &table[index];
//检测红黑树的结点是不是规范,如果不是,程序直接结束
checkInvariants(table[index].next);
}
}
//检测红黑树是否规范
void checkInvariants(hashNode root) {
//父节点
hashNode parent = root->parent;
//左孩子结点
hashNode lchild = root->lchild;
//右孩子结点
hashNode rchild = root->rchild;
//前一个
hashNode prev = root->prev;
//后一个
hashNode next = root->next;
//判断
//1. 和上一个结点不连接
if (prev != NULL && prev->next != root) {
exit(0);
}
//2. 和下一个结点没有连起来
if (next != NULL && next->prev != root) {
exit(0);
}
//3. 和左孩子结点或者右孩子结点没有连起来
if (parent != NULL && parent->lchild != root && parent->rchild != root) {
exit(0);
}
//4. 左孩子的hash不对
if (lchild != NULL && (lchild->parent != root || lchild->data.hash > root->data.hash)) {
exit(0);
}
//5. 右孩子的hash不对
if (rchild != NULL && (rchild->parent != root || rchild->data.hash < root->data.hash)) {
exit(0);
}
//当前结点是红色,孩子结点也是红色
if (root->isRed && ((lchild != NULL && lchild->isRed) || (rchild != NULL && rchild->isRed))) {
exit(0);
}
//检查左孩子和右孩子
if (lchild != NULL) {
checkInvariants(lchild);
}
if (rchild != NULL) {
checkInvariants(rchild);
}
}
//清空hashMap
void clear(hashmap& hashMap) {
hashNode node = hashMap->table;
hashMap->modCount++;
hashMap->size = 0;
free(node);
hashMap->table = NULL;
}
//hashMap是否包含某个key
Status containsKey(Key key, hashmap &hashMap) {
if (key == UN_EXIT || hashMap == NULL) {
return ERROR;
}
return getNode(hashByKey(key), key, hashMap) == NULL ? ERROR : OK;
}
//hashMap是否包含某个Value
Status containsValue(Value value, hashmap hashMap) {
hashNode node = hashMap->table;
if (node != NULL && hashMap->size > 0) {
for (int i = 0; i < hashMap->length; i++) {
if (node[i].lchild != NULL || node[i].rchild != NULL) {
//树的查找
}
else if (equals(node[i].data.value, value)) {
return OK;
}
else {
//调用链表的查找
hashNode temp = &node[i];
while (temp != NULL) {
if (equals(temp->data.value, value)) {
return OK;
}
temp = temp->next;
}
}
}
}
return ERROR;
}
//remove方法,两个,一个通过key删除,一个通过key和value删除
Status removeByKeyValue(Key key, Value value, hashmap& hashMap) {
if (key == UN_EXIT || value == UN_EXIT || hashMap == NULL || hashMap->table == NULL) {
return UN_EXIT;
}
return removeNode(hashByKey(key), key, value, true, true, hashMap);
}
Status removeByKey(Key key, hashmap& hashMap) {
if (key == UN_EXIT || hashMap == NULL || hashMap->table == NULL) {
return UN_EXIT;
}
return removeNode(hashByKey(key), key, NULL, false, true, hashMap);
}
//真正的remove方法
Status removeNode(int hash, Key key, Value value, int matchValue, int movable, hashmap& hashMap) {
hashNode tab = hashMap->table;
hashNode temp = NULL;
int length = hashMap->length;
int index = (length - 1) & hash;
temp = &(hashMap->table[index]);
hashNode root = temp;
//判断条件,看头节点的key判断有没有数据
if (tab != NULL && length > 0 && temp->data.key != UN_EXIT) {
temp = temp->next;
root = temp;
hashNode node = NULL, e = NULL;
Value value1 = NULL;
//第一个结点就是,但是只有一个结点
if (temp->data.hash == hash && key != UN_EXIT && equals(temp->data.key, key) && temp->next == NULL) {
node = temp;
}
//是第一个结点,但是有可能是链表
else if (temp->next != NULL && temp->parent == NULL && temp->lchild == NULL && temp->rchild == NULL) {
e = temp->next;
//判断如果是第一个结点的情况
if (temp == tab[index].next && equals(temp->data.key, key)) {
tab[index].next = e;
free(temp);
return OK;
}
//链表的查找
while (e != NULL) {
if (e->data.hash == hash && key != UN_EXIT && equals(e->data.key, key)) {
node = e;
break;
}
temp = e;
e = e->next;
}
}
//不是链表,有可能是红黑树
else if (temp->lchild != NULL || temp->rchild != NULL) {
//红黑树的查找
node = searchNode(key, temp);
}
//判断
if (node != NULL && (matchValue == 0 || (matchValue == 1 && equals(value1 = node->data.value, value)))) {
//证明找到了
if (node->lchild != NULL || node->rchild != NULL || node->parent != NULL) {
//树的remove
deleteNode(hashMap, node->data.hash, node->data.key, node->data.value, root, node);
}
//链表的情况:中间或者队尾
else if (node->next != NULL || (node->next == NULL && tab[index].next != node)) {
//链表
temp->next = node->next;
free(node);
}
//只有一个结点
else if(node == tab[index].next && node->next == NULL){
tab[index].next = node->next;
tab[index].data.key = UN_EXIT;
free(node);
}
hashMap->modCount++;
hashMap->size--;
return OK;
}
return ERROR;
}
else {
return ERROR;
}
}
//replace方法
//根据key和value来进行匹配
Status replaceByKeyValue(Key key, Value oldValue, Value newValue, hashmap& hashMap) {
hashNode e = NULL;
e = getNode(hashByKey(key), key, hashMap);
if (e != NULL && equals(e->data.value,oldValue)) {
e->data.value = newValue;
return OK;
}
return FALSE;
}
//根据key来进行匹配
Status replaceByKey(Key key, Value newValue, hashmap& hashMap) {
hashNode e = NULL;
e = getNode(hashByKey(key), key, hashMap);
if (e != NULL) {
e->data.value = newValue;
return OK;
}
return FALSE;
}
//初始化
void initByOther(HashNode& node1, hashNode& node2) {
node1.data = node2->data;
node1.isRed = node2->isRed;
node1.lchild = node2->lchild;
node1.next = node2->next;
node1.rchild = node2->rchild;
node1.parent = node2->parent;
node1.prev = node2->prev;
}
void meun() {
printf("********a. 初始化(默认) b. 初始化(自定义阈值)\n");
printf("********c. 初始化(自定义阈值和负载因子) d. 加入结点(可以进行替换)\n");
printf("********e. 加入结点(不可进行替换) f. 查找是否包含key\n");
printf("********g. 菜单 h. 打印 \n");
printf("********i. 根据key替换 j. 查找key对应得value\n");
printf("********k. 根据key和value替换 l. 根据key移除结点\n");
printf("********m. 根据key和value移除结点\n");
}
//对链表进行清除
void clear(hashNode table) {
while (table != NULL) {
hashNode temp = table;
table = table->next;
free(temp);
}
}
6、RBTree.cpp:实现红黑树的一些操作
#include "RBTree.h"
//方法
hashNode parentOf(hashNode node) {
//一个树结点
if (node != NULL && node->parent != NULL) {
return node->parent;
}
return NULL;
}
/**
* 结点是否为红色
*/
int RedOrNot(hashNode node) {
if (node != NULL) {
return node->isRed == RED;
}
return false;
}
/**
* 结点是否为黑色
*/
int isBlack(hashNode node) {
if (node != NULL) {
return node->isRed == BLACK;
}
return false;
}
/**
* 中序打印
*/
void inOrderPrint(hashNode root) {
inOrderPrint_1(root);
}
/**
* 中序打印二叉树
*/
void inOrderPrint_1(hashNode root) {
if (root != NULL) {
inOrderPrint(root->lchild);
printf("key: %d -> value: %d", root->data.key, root->data.value);
inOrderPrint(root->rchild);
}
}
hashNode getRoot(hashNode root) {
hashNode node = root, p = NULL;
for ( ; ; ) {
p = root->parent;
if (p == NULL) {
return root;
}
else {
root = p;
}
}
}
/*
左旋
B D
/ \ / \
A D -> B E
/ \ / \
C E A C
*/
void leftRotate(hashNode& root, hashNode x) {
//假设x是B,y是D
hashNode y = x->rchild;
//1. 将y的左子节点的父节点更新为x,让C的父节点设置为B
x->rchild = y->lchild;
if (y->lchild != NULL) {
y->lchild->parent = x;
}
//2. 当x的父节点不为空,更新y的父节点为x的父节点,并将x的父节点指定子树指定为y
if (x->parent != NULL) {
y->parent = x->parent;
if (x == x->parent->lchild) {
//x是左节点
x->parent->lchild = y;
}
else {
//x的父节点为空,也就是说x是根节点,此时需要更新y结点
//x是右节点
x->parent->rchild = y;
}
}
else {
//x的父节点为空,也就是说x是根节点,此时需要更新y结点为根结点
root = y;
y->parent = NULL;
}
//将x的父节点更新为y,将y的左子结点更新为x
x->parent = y;
y->lchild = x;
}
/*
右旋
C A
/ \ / \
A E -> B C
/ \ / \
B D D E
*/
void rightRotate(hashNode& root, hashNode y) {
//y=C, x=A
hashNode x = y->lchild;
//1. 将y的左子节点指向x的右子结点,并且更新x的右子节点的父节点为y
y->lchild = x->rchild;
if (x->rchild != NULL) {
x->rchild->parent = y;
}
//2. 当y的父节点不为空。更新x的父节点为y的父节点,y的父节点指定子节点(y当前位置)为x
if (y->parent != NULL) {
x->parent = y->parent;
if (y->parent->lchild == y) {
y->parent->lchild = x;
}
else {
y->parent->rchild = x;
}
}
else {
root = x;
root->parent = NULL;
}
//3. 更新y的父节点为x,更新x的右子结点为y
y->parent = x;
x->rchild = y;
}
/**
* 公开的插入方法
* @param key
* @param value
*/
void insert(Key key, Value value, hashNode& root, hashmap& hashMap) {
hashNode node = package(key, value);
insertReal(node, root, hashMap);
}
void insertReal(hashNode node, hashNode& root, hashmap& hashMap) {
hashNode parent = NULL;
hashNode x = root;
while (x != NULL) {
parent = x;
//cmp>0,说明node的key大于x的key,到右子树查找
//cmp=0,说明node的key等于x的key,进行替换
//cmp<0,说明node的key小于x的key,需要到x的左子树查找
int cmp = compare(node->data.hash, x->data.hash);
if (cmp > 0) {
//往右找
x = x->rchild;
}
else if(cmp == 0) {
//替换
x->data.value = node->data.value;
return;
}
else {
//往左找
x = x->lchild;
}
}
//这一步找到了x为null
node->parent = parent;
//判断node和parent的key谁大
if (parent != NULL) {
int cmp = compare(node->data.key, parent->data.key);
if (cmp > 0) {
//当前node的key比parent的key大,需要把node放入parent的右子结点
parent->rchild = node;
}
else if(cmp < 0){
//当前node的key比parent的key小,需要把node放入parent的左子结点
parent->rchild = node;
}
}
else {
//第一次插入,根是空的
root = node;
}
//调整双向链表的位置
node->next = parent->next;
if (parent->next != NULL) {
parent->next->prev = node;
}
parent->next = node;
node->prev = parent;
//有可能不平衡,修复红黑树
insertFixUp(node, root);
//调整树的根部
moveRootToFront(hashMap->table, root, hashMap);
}
/**
* 修复红黑树
* 1. 红黑树为空树,将根结点染色为黑色
* 2. 插入结点的key已经存在,不需要存在
* 3. 插入的父节点为黑色,因为所插入的路径黑色结点数目不变,所以不需要处理
*
* 4. 需要我们去处理,插入的结点的父节点为红色
* 4.1 叔叔结点存在,并且为红色(父-叔 双红),将爸爸和叔叔染色为黑色。将爷爷染色为红色
* 并且以爷爷结点为当前结点进行下一轮处理
* 4.2 叔叔节点不存在或者为黑色,父节点为爷爷结点的左子树
* 4.2.1 插入的结点为其父节点的左子节点(LL), 将爸爸染色为黑色,然后将爷爷染色为红色,
* 然后以爷爷结点右旋
* 4.2.2 插入的结点为其父节点的左子节点(LR), 以爸爸结点进行一次左旋得到LL双红的情况,
* 然后指定爸爸结点为当前结点进行下一轮处理(4.2.1)
* 4.3 叔叔节点不存在或者为黑色,父节点为爷爷结点的右子树
* 4.3.1 插入的结点为其父节点的右子节点(RR), 将爸爸染色为黑色,然后将爷爷染色为红色,
* 然后以爷爷结点左旋
* 4.3.2 插入的结点为其父节点的左子节点(RL), 以爸爸结点进行一次右旋得到RR双红的情况,
* 然后指定爸爸结点为当前结点进行下一轮处理(4.3.1)
*
*/
void insertFixUp(hashNode& node, hashNode& root) {
//确保根节点是黑色的,第一次插入内
hashNode temp = node;
hashNode parent = parentOf(node);
hashNode grandParent = parentOf(parent);
//判断根结点并赋值为BLACK
//1. 自身是根结点
if (temp != NULL && temp->parent == NULL) {
temp->isRed = BLACK;
}
//2. 父节点是根结点
if (parent != NULL && parent->parent == NULL) {
parent->isRed = BLACK;
}
//祖父结点是根结点
if (grandParent != NULL && grandParent->parent == NULL) {
grandParent->isRed = BLACK;
}
//情景1.2.3,我们都不需要调整
//情景4:插入结点父节点为红色
if (parent != NULL && RedOrNot(parent)) {
hashNode uncle = NULL;
//父节点为爷爷结点的左子结点
if (parent == grandParent->lchild) {
uncle = grandParent->rchild;
//4.1 叔叔结点存在,并且为红色(父-叔 双红)
if (uncle != NULL && RedOrNot(uncle)) {
//将爸爸和叔叔染色为黑色。将爷爷染色为红色,并且以爷爷结点为当前结点进行下一轮处理
parent->isRed = BLACK;
uncle->isRed = BLACK;
grandParent->isRed = RED;
insertFixUp(grandParent, root);
return;
}
//4.2 叔叔节点不存在或者为黑色
if (uncle == NULL || isBlack(uncle)) {
//4.2.1插入的结点为其父节点的左子节点(LL)
if (node == parent->lchild) {
parent->isRed = BLACK;
grandParent->isRed = RED;
rightRotate(root, grandParent);
return;
}
else {
//4.2.2 插入的结点为其父节点的左子节点(LR)
if (node == parent->rchild) {
leftRotate(root, parent);
insertFixUp(parent, root);
return;
}
}
}
}
else {
//父节点为爷爷结点的右子树
uncle = grandParent->lchild;
//4.1 叔叔结点存在,并且为红色(父-叔 双红)
if (uncle != NULL && RedOrNot(uncle)) {
//将爸爸和叔叔染色为黑色。将爷爷染色为红色,并且以爷爷结点为当前结点进行下一轮处理
parent->isRed = BLACK;
uncle->isRed = BLACK;
grandParent->isRed = RED;
insertFixUp(grandParent, root);
return;
}
//4.3 叔叔节点不存在或者为黑色,父节点为爷爷结点的右子树
if (uncle == NULL || isBlack(uncle)) {
//4.3.1 插入的结点为其父节点的右子节点(RR)
if (node == parent->rchild) {
parent->isRed = BLACK;
grandParent->isRed = RED;
leftRotate(root, grandParent);
return;
}
//4.3.2插入的结点为其父节点的左子节点(RL)
if (node == parent->lchild) {
rightRotate(root, parent);
insertFixUp(parent, root);
return;
}
}
}
}
}
//删除叶子结点
void deleteNode(hashmap& hashmap, int hash, Key key, Value value, hashNode& root, hashNode node) {
if (hashmap == NULL || hashmap->table == NULL || hashmap->length == 0) {
return;
}
//获取下标
int index = (hashmap->length - 1) & hash;
hashNode table = hashmap->table;
//首个节点
hashNode first = table[index].next;
hashNode realRoot = first;
//succ-调用这个方法的节点(待删除节点)的后驱节点
hashNode succ = node->next;
//prev - 调用这个方法的节点(待删除节点)的前驱节点
hashNode pred = node->prev;
/**
* 维护双向链表(map在红黑树数据存储的过程中,除了维护红黑树之外还对双向链表进行了维护)
* 从链表中将该节点删除
* 如果前驱节点为空,说明删除节点是头节点,删除之后,头节点直接指向了删除节点的后继节点
*/
if (pred->data.value == UN_EXIT) {
table[index].next = first = succ;
}
else {
//删除的不是头节点
pred->next = succ;
}
if (succ != NULL) {
succ->prev = pred;
}
// 如果头节点(即根节点)为空,说明该节点删除后,红黑树为空,直接返回
if (first == NULL) {
return;
}
// 如果父节点不为空,说明删除后,调用root方法重新获取当前树的根节点
if (first->parent != NULL) {
realRoot = getRoot(first);
}
/**
* 当以下三个条件任一满足时,当满足红黑树条件时,说明该位置元素的长度少于6(UNTREEIFY_THRESHOLD),需要对该位置元素链表化
* 1、root == NULL:根节点为空,树节点数量为0
* 2、root.rchild == NULL:右孩子为空,树节点数量最多为2
* 3、(rl = root.left) == null || rl.left == null):
* (rl = root.left) == null:左孩子为空,树节点数最多为2
* rl.left == null:左孩子的左孩子为NULL,树节点数最多为6
*/
if (hashmap->size <= UNTREEIFY_THRESHOLD) {
table[index].next = untreeify(hashmap, first);
hashmap->table = table;
return;
}
hashNode var = package(key, value);
//否则还是树,调用树的删除
deleteNodeReal(var, root, hashmap);
//重新赋值table
hashmap->table = table;
//重新赋值根节点
if (root->parent != NULL) {
root = realRoot;
}
}
//真正的删除
void deleteNodeReal(hashNode node, hashNode& root, hashmap& hashMap) {
hashNode x = NULL; // x 指向 y 的唯一子节点或指向 NULL
hashNode z = searchNode(node->data.key, root);
hashNode parent = NULL;
hashNode y = z; //y 为从树中删除的节点或者移至树内的节点
int y_color = -1; //保存y的颜色
if (z == NULL) {
//没找到
return;
}
//y的颜色
y_color = y->isRed;
if (z->lchild == NULL) {
//情况1:被删除的z左节点是空,右孩子无限制,使用右孩子替换
x = z->rchild;
//使用z的右孩子替换
transplate(z, z->rchild, root);
parent = z->parent;
}
else if (z->rchild == NULL) {
//情况2:右孩子为空,使用左孩子替换
x = z->lchild;
//使用z的右孩子替换
transplate(z, z->lchild , root);
parent = z->parent;
}
else {
//z有两个结点
/*
①、找到结点D的后继结点S
②、将结点S的key值赋给结点D;
③、再将结点S从树中删除,并用结点S的右孩子替代结点S的位置;[注:从前面的描述可以看出,其实被删的是结点D的后继结点S]
④、如果被删结点S为红色,则红黑树性质未被破坏,因此不需做其他调整;
⑤、如果被删结点S为黑色,则需进一步做调整处理。
*/
//y是找到的后继结点,y的左孩子一定是NULL
y = searchMin(z->rchild);
//记录被删除的颜色
y_color = y->isRed;
x = y->rchild;
if (y->parent == z) {
//z的后继节点是y,证明y是z的右孩子结点,并且y没有左孩子结点
if (x == NULL) {
y->parent->rchild = NULL;
}
else {
x->parent = y;
}
//y取代z
transplate(z, y, root);
y->lchild = z->lchild;
y->lchild->parent = y;
y->isRed = z->isRed;
//这里的parent是因为直接取到z的孩子结点
parent = y;
}
else {
//y取代z
hashNode yRchild = y->rchild;
parent = y->parent;
//如果右结点为null,那么直接赋值
if (yRchild == NULL) {
y->parent->lchild = NULL;
}
else {
//否则就替换2次
transplate(y, yRchild, root);
}
transplate(z, y, root);
//y结点要替换到z结点的位置,赋值右子树
y->lchild = z->lchild;
y->lchild->parent = y;
y->isRed = z->isRed;
y->rchild = z->rchild;
y->rchild->parent = y;
}
}
//判断如果删除的结点是黑结点,进行修复
//对x进行修复,因为x是填充到y位置的
if (y_color == BLACK) {
deleteFixUp(x, parent, root);
}
//删除之后要调整头部
moveRootToFront(hashMap->table, root, hashMap);
//释放删除的结点z
free(z);
z = NULL;
return;
}
//删除修复
//如果删除的结点时红色结点,不用调整平衡,如果删除的结点时黑节点,就要调整
/*
前提1:参照结点N为父结点P的左孩子
情况1:参照结点N的兄弟B是红色的
处理过程:
①、将父结点P的颜色改为红色,兄弟结点的颜色改为黑色;
②、以父结点P为支点进行左旋处理;
③、情况1转变为情况2或3、4,后续需要依次判断处理。
情况2:参照结点N的兄弟B是黑色的,且B的两个孩子都是黑色的
处理过程:
①、如果parent此时为红,则把brother的黑色转移到parent上
②、如果此时parent为黑,即此时全黑了,则把brother涂红,导致brother分支少一个黑,使整个分支都少了一个黑,需要对parent又进行一轮调整
情况3:参照结点N的兄弟B是黑色的,且B的左孩子是红色的,右孩子是黑色的
处理过程:
①、将兄弟结点的左孩子调整为父颜色,父颜色调整为黑色
②、以兄弟结点开始右旋
③、以父节点左旋
情况4:参照结点N的兄弟B是黑色的,且B的左孩子是黑色的,右孩子是红色的
处理过程:
①、将父结点P的颜色拷贝给兄弟结点B,再将父结点P和兄弟结点的右孩子BR的颜色改为黑色;
②、以父结点P为支点,进行左旋处理;
③、将该结点改为树的根结点,也意味着调整结束。
前提2:参照结点N为父结点P的右孩子
情况5:参照结点N的兄弟B是红色的
处理过程:
①、将父结点P的颜色改为红色,兄弟结点的颜色改为黑色;
②、以父结点P为支点进行右旋处理;
③、情况5转变为情况6或7、8,后续需要依次判断处理。
情况6:参照结点N的兄弟B是黑色的,且B的两个孩子都是黑色的
处理过程:
①、如果parent此时为红,则把brother的黑色转移到parent上
②、如果此时parent为黑,即此时全黑了,则把brother涂红,导致brother分支少一个黑,使整个分支都少了一个黑,需要对parent又进行一轮调整
情况7:参照结点N的兄弟B是黑色的,且B的右孩子是红色的,左孩子是黑色的
处理过程:
①、将兄弟结点的右孩子调整为父颜色,父颜色调整为黑色
②、以兄弟结点开始左旋
③、以父节点右旋
情况8:参照结点N的兄弟B是黑色的,且B的右孩子是黑色的,左孩子是红色的
处理过程:
①、将父结点P的颜色拷贝给兄弟结点B,再将父结点P和兄弟结点的右孩子BR的颜色改为黑色;
②、以父结点P为支点,进行右旋处理;
③、将该结点改为树的根结点,也意味着调整结束。
*/
void deleteFixUp(hashNode x, hashNode parent, hashNode& root) {
hashNode brother = NULL;
//如果要修复的树是根,由于没有兄弟姐弟啊,那么把x的颜色改成黑色就好了
while ((x == NULL || x->isRed == BLACK) && x != root) {
// 前提1:参照结点x为父结点P的左孩子
hashNode p = parent;
if (x == p->lchild) {
//兄弟是右结点
brother = p->rchild;
//情况1:参照结点N的兄弟B是红色的
/*
①、将父结点P的颜色改为红色,兄弟结点的颜色改为黑色;
②、以父结点P为支点进行左旋处理;
③、情况1转变为情况2或3、4,后续需要依次判断处理。
*/
if (brother != NULL && brother->isRed == RED) {
brother->isRed = BLACK;
p->isRed = RED;
//以父节点左旋
leftRotate(root, p);
//完成之后兄弟要重新更新进行下一步操作
brother = p->rchild;
}
// 情况2:参照结点N的兄弟B是黑色的,且B的两个孩子都是黑色的
/*
①、将兄弟结点B的颜色改为红色
②、情况2处理完成后,不必再进行情况3、4的判断,但需重新循环判断前提1、2。
*/
if (brother != NULL && brother->isRed == BLACK && (brother->lchild == NULL || isBlack(brother->lchild)) && (brother->rchild == NULL || isBlack(brother->rchild))) {
brother->isRed = RED;
// 如果parent此时为红,则把brother的黑色转移到parent上
if (p->isRed = RED) {
p->isRed = BLACK;
brother->isRed = RED;
break;
}
else {
// 如果此时parent为黑,即此时全黑了,则把brother涂红,导致brother分支少一个黑,使整个分支都少了一个黑,需要对parent又进行一轮调整
brother->isRed = RED;
x = parent;
parent = x->parent;
}
}
else {
// 情况3:参照结点N的兄弟B是黑色的,且B的左孩子是红色的
/*
①、将兄弟结点的左孩子调整为父颜色,父颜色调整为黑色
②、以兄弟结点开始右旋
③、以父节点左旋
*/
if (brother != NULL && brother->isRed == BLACK && RedOrNot(brother->lchild)) {
brother->lchild->isRed = p->isRed;
p->isRed = BLACK;
rightRotate(root, brother);
leftRotate(root, parent);
}
//情况4:参照结点N的兄弟B是黑色的,且B的左孩子是黑色的,右孩子是红色的
/*
①、将父结点P的颜色拷贝给兄弟结点B,再将父结点P和兄弟结点的右孩子BR的颜色改为黑色;
②、以父结点P为支点,进行左旋处理;
③、将该结点改为树的根结点,也意味着调整结束。
*/
else if (brother != NULL && brother->isRed == BLACK && RedOrNot(brother->rchild)) {
brother->isRed = p->isRed;
p->isRed = BLACK;
brother->rchild->isRed = BLACK;
leftRotate(root, p);
}
break;
}
}
// 前提2:参照结点x为父结点P的左孩子
else {
//兄弟是左结点
brother = p->lchild;
//情况5:参照结点N的兄弟B是红色的
/*
①、将父结点P的颜色改为红色,兄弟结点的颜色改为黑色;
②、以父结点P为支点进行右旋处理;
③、情况5转变为情况6或7、8,后续需要依次判断处理。
*/
if (brother->isRed == RED) {
p->isRed = RED;
brother->isRed = BLACK;
rightRotate(root, p);
brother = p ->lchild;
}
//情况6:参照结点N的兄弟B是黑色的,且B的两个孩子都是黑色的
/*
①、将兄弟结点B的颜色改为红色;
②、情况6处理完成后,不必再进行情况7、8的判断,但需要重新循环判断前提1、2。
*/
if ((brother->lchild == NULL || brother->lchild->isRed == BLACK)
&& (brother->rchild == NULL || brother->rchild->isRed == BLACK)) {
// 如果parent此时为红,则把brother的黑色转移到parent上
if (p->isRed == RED) {
p->isRed = BLACK;
brother->isRed = RED;
break;
}
// 如果此时parent为黑,即此时全黑了,则把brother涂红,导致brother分支少一个黑,使整个分支都少了一个黑,需要对parent又进行一轮调整
else {
brother->isRed = RED;
x = parent;
parent = x->parent;
}
}
else {
//左右孩子必有一个是红色
// 情况7:参照结点N的兄弟B是黑色的,且B的右孩子是红色的
/*
①、将兄弟结点的右孩子调整为父颜色,父颜色调整为黑色
②、以兄弟结点开始左旋
③、以父节点右旋
*/
if (brother->isRed == BLACK && brother->rchild != NULL && brother->rchild->isRed == RED) {
brother->rchild->isRed = p->isRed;
p->isRed = BLACK;
leftRotate(root, brother);
rightRotate(root, p);
}
//左右孩子必有一个是红色
// 情况8:参照结点N的兄弟B是黑色的,且B的左孩子是红色的
/*
①、将父结点P的颜色拷贝给兄弟结点B,再将父结点P和兄弟结点的右孩子BR的颜色改为黑色;
②、以父结点P为支点,进行右旋处理;
③、将该结点改为树的根结点,也意味着调整结束。
*/
else if (brother != NULL && brother->isRed == BLACK && brother->lchild != NULL && RedOrNot(brother->lchild)) {
brother->isRed = p->isRed;
p->isRed = BLACK;
brother->lchild->isRed = BLACK;
//以父节点进行旋转
rightRotate(root, p);
}
}
}
}
//修改x颜色为黑色
if (x != NULL) {
x->isRed = BLACK;
}
return;
}
//1. 子树替换
//v代替u
void transplate(hashNode u, hashNode v, hashNode& root) {
if (u->parent == NULL) {
root = v;
}
else if (u == u->parent->lchild) {
//左孩子
u->parent->lchild = v;
}
else {
//右孩子
u->parent->rchild = v;
}
if (v != NULL) {
v->parent = u->parent;
}
}
//2.查找元素
hashNode searchNode(Value key, hashNode root) {
hashNode node = NULL;
if (key == NULL || root == NULL) {
return NULL;
}
if ((root->data.hash == hashByKey(key)) && (equals(root->data.key, key) == OK)) {
return root;
}
if ((node = searchNode(key, root->lchild)) != NULL) {
return node;
}
return searchNode(key, root->rchild);
}
/* 最小关键字元素 */
hashNode searchMin(hashNode root){
hashNode temp = root;
while (temp != NULL && temp->lchild != NULL) {
temp = temp->lchild;
}
return temp;
}
/*****************************************************************************
* @brief 水平画树
* @param node 二叉树节点
* @param left 判断左右
* @param str 可变字符串
*****************************************************************************/
void draw_level(hashNode node, bool left, char* str) {
if (node->rchild) {
draw_level(node->rchild, false, strcat(str, (left ? "| " : " ")));
}
printf("\t\t\t");
printf("%s", str);
//printf("\t\t");
printf("%c", (left ? '\\' : '/'));
printf("-----");
printf("(%d:%d)%c\n", node->data.key, node->data.value, node->isRed ? 'R' : 'B');
if (node->lchild) {
draw_level(node->lchild, true, strcat(str, (left ? " " : "| ")));
}
// " " : "| " 长度为 6
str[strlen(str) - 6] = '\0';
}
/*****************************************************************************
* @brief 根节点画树
* @param root 二叉树根节点
*****************************************************************************/
void draw(hashNode root) {
char str[STR_MAX_SIZE];
memset(str, '\0', STR_MAX_SIZE);
/**
* 1. 在 windows 下,下面是可执行的
* 2. 在 Linux 下,执行会报 Segmentation fault
* 需要使用中间变量
*/
if (root->rchild) {
draw_level(root->rchild, false, str);
}
printf("\t\t\t");
printf("(%d:%d)%c\n", root->data.key, root->data.value, root->isRed?'R':'B');
if (root->lchild) {
draw_level(root->lchild, true, str);
}
}
//链表打印
void drawLine(hashNode root) {
while (root != NULL) {
printf("(%d:%d)", root->data.key, root->data.value);
root = root->next;
if (root != NULL) {
printf("------>");
}
}
}
//总的打印
void print(hashNode table, hashmap hashMap) {
hashNode temp = table;
int count = 0;
//移动
printf("\n\n\n\n");
printf("\t\t");
//遍历table一个一个打印
for (int i = 0; i < hashMap->length; i++) {
printf("[下标%d] --> ", i);
if (table[i].data.key != UN_EXIT && (table[i].next->lchild != NULL || table[i].next->rchild != NULL || table[i].next->parent != NULL)) {
printf("\n\n\n\n");
draw(table[i].next);
}
else if(table[i].data.key != UN_EXIT){
drawLine(table[i].next);
}
printf("\n\n\n\n");
printf("\t\t");
}
printf("\n");
}
如有错误,欢迎指出
============================================
2023.5.8更新,插入节点比较多的时候会异常,是扩容的时候某些下标没初始化(或者有些地方free了),但是具体在哪出问题还没有仔细测过