diff --git a/List.c b/List.c new file mode 100644 index 0000000..bee06b2 --- /dev/null +++ b/List.c @@ -0,0 +1,33 @@ +#pragma once +#include"List.h" +#include +#include +#include +int InitList(LinkList *L) +{ + *L = (LinkList)malloc(sizeof(Node_List)); /* 产生头结点,并使L指向此头结点 */ + if (!(*L)) /* 存储分配失败 */ + return 0; + (*L)->next = NULL; /* 指针域为空 */ + + return 1; +} +int ListInsert(LinkList *L, int i, int e) +{ + int j; + LinkList p, s; + p = *L; + j = 1; + while (p && j < i) /* 寻找第i个结点 */ + { + p = p->next; + ++j; + } + if (!p || j > i) + return 0; /* 第i个元素不存在 */ + s = (LinkList)malloc(sizeof(Node_List)); /* 生成新结点(C语言标准函数) */ + s->data = e; + s->next = p->next; /* 将p的后继结点赋值给s的后继 */ + p->next = s; /* 将s赋值给p的后继 */ + return 1; +} \ No newline at end of file diff --git a/List.h b/List.h new file mode 100644 index 0000000..fc32d28 --- /dev/null +++ b/List.h @@ -0,0 +1,9 @@ +#pragma once +typedef struct Node_List +{ + int data; + struct Node_List *next; +}Node_List; +typedef struct Node_List *LinkList; //定义LinkList +int InitList(LinkList *L); +int ListInsert(LinkList *L, int i, int e); \ No newline at end of file diff --git a/hashTable.c b/hashTable.c new file mode 100644 index 0000000..9be8344 --- /dev/null +++ b/hashTable.c @@ -0,0 +1,115 @@ +#pragma once +#include "hashTable.h" +#define MAX_PRIME_LESS_THAN_HASH_LEN 7993 +char* strDup(const char* str) +{ + int len; + char* ret; + if (str == NULL)return NULL; + + len = strlen(str); + ret = (char*)malloc(len + 1); + if (ret != NULL) { + memcpy(ret, str, len); + ret[len] = '\0'; + } + return ret; +} +unsigned int hashfunc(char *str, int len) +{ + register unsigned int sum = 0; + register unsigned int h = 0; + register char *p = str; + while (p - str < len) + { + register unsigned short a = *(p++); + sum ^= a * (p - str); + h ^= a / (p - str); + } + return ((sum << 16) | h) % MAX_PRIME_LESS_THAN_HASH_LEN; +} + + +void initHashTable(hashTable* t) +{ + int i; + if (t == NULL)return; + for (i = 0; i < BUCKETCOUNT; ++i) { + t->bucket[i].key = NULL; + t->bucket[i].value = NULL; + t->bucket[i].next = NULL; + } +} +void freeHashTable(hashTable* t) +{ + int i; + entry* e, *ep; + if (t == NULL)return; + for (i = 0; ibucket[i]); + while (e->next != NULL) { + ep = e->next; + e->next = ep->next; + free(ep->key); + free(ep->value); + free(ep); + } + } +} +int insertEntry(hashTable* t, char *key, RBRoot *value) +{ + int index, vlen1, vlen2; + entry* e, *ep; + + if (t == NULL || key == NULL || value == NULL) { + return -1; + } + index = hashfunc(key, strlen(key)); + + if (t->bucket[index].key == NULL) { + t->bucket[index].key = (char *)malloc(strlen(key)*sizeof(char)); + t->bucket[index].key = key; + t->bucket[index].value = (RBRoot *)malloc(sizeof(RBRoot)); + t->bucket[index].value = value; + } + else { + e = ep = &t->bucket[index]; + while (e != NULL) { + if (strcmp(e->key, key) == 0) + { + memcpy(e->value, value, sizeof(RBRoot)); + return index; + } + ep = e; + e = e->next; + } + e = (entry*)malloc(sizeof(entry)); + e->key = strDup(key); + e->value= (RBRoot *)malloc(sizeof(RBRoot)); + e->value = value; + e->next = NULL; + ep->next = e; + } + return index; +} +RBRoot* findValue(hashTable*t, char *key) +{ + int index; + entry* e; + if (t == NULL || key == NULL) + { + return NULL; + } + index = hashfunc(key,strlen(key)); + e = &(t->bucket[index]); + if (e->key == NULL)return NULL; + while (e != NULL) + { + if (strcmp(key, e->key)==0) + { + return e->value; + } + e = e->next; + } + return NULL; +} \ No newline at end of file diff --git a/hashTable.h b/hashTable.h new file mode 100644 index 0000000..85b97c3 --- /dev/null +++ b/hashTable.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include +#include +#include "rbtree.h" +#define BUCKETCOUNT 9000 + struct _hash_Entry + { + const char* key;//键 + RBRoot* value;//值 + struct _hash_Entry* next;//下一个结点 + };//结点的内容 + typedef struct _hash_Entry entry; + typedef struct _Table + { + entry bucket[BUCKETCOUNT]; //定义桶 + }hashTable; + char* strDup(const char* str); + unsigned int hashfunc(char*key, int len);//哈希值的计算 + void initHashTable(hashTable* t);//初始化哈希表 + void freeHashTable(hashTable* t);//释放哈希表 + int insertEntry(hashTable* t, char *key, RBRoot *value);//插入数据 + RBRoot* findValue(hashTable*t, char *key);//查找 + //entry* removeTable(hashTable*t, char key);//删除,暂时不用 \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..7977778 --- /dev/null +++ b/main.cpp @@ -0,0 +1,259 @@ +extern "C" { +#include"hashTable.h" +#include"rbtree.h" +#include"List.h" +} + +#include +#include +#include +#include +#include +#define TRUE 1 +#define FALSE 0 +using namespace std; +vector stopword; //stopword向量(有一些词出现次数太多,得不偿失,意义不大如 a the) +void Insertword(string voc, string filename, int address, hashTable * Index);//建立索引时要插入元素 +void BuildIndex(hashTable *Index);// 建立索引 +void collect_stopword();//把stopword加入向量 +bool isstopword(string W);//判断stopword +string Filename(int N, int M);//用这种方式获取一个文件名 +bool isword(char ch);//判断这个char是否合法 + +void collect_stopword() +{ + ifstream fp; + string stop; + char ch; + fp.open("stopword.txt", ios_base::in | ios_base::binary); + if (!fp) + { + cout << "读取屏蔽词出错!" << endl; + return; + } + + fp.get(ch); + while (fp) + { + stop = ""; + + while (!isword(ch) && !fp.eof()) + { + fp.get(ch); + } + + if (!fp) + break; + + while (isword(ch) && !fp.eof()) + { + stop += ch; + fp.get(ch); + } + transform(stop.begin(), stop.end(), stop.begin(), tolower); + + stopword.push_back(stop); + } +} + +//判断是不是stopword——从上一个函数中取得了所有stopword拼成的字符串,判断是否能在里面找到就行 +bool isstopword(string W) +{ + if (find(stopword.begin(), stopword.end(), W) == stopword.end()) + return 0; + else + return 1; +} +//判断是否是单词 +bool isword(char ch) +{ + //标准:传入ch在A-Z或者a-z中间或者是\(配合n吧)就算 + int D = ((ch >= 'a' && ch <= 'z') || (ch >= 'A'&& ch <= 'Z') || (ch == '\'')); + return ((D == 1) ? TRUE : FALSE); +} + +//构造文件名的函数(因为示范文件是10xx x的格式所以这么构造) +string Filename(int N, int M) +{ + string res; + char file[15]; + char chap[10]; + _itoa_s(N, file, 10); + _itoa_s(M, chap, 10); + strcat_s(file, 15, " "); + strcat_s(file, 15, chap); + strcat_s(file, 15, ".txt"); + res = file; + return res; +} +int hashfile(char*p) +{ + int i = strlen(p); + int hash=0; + int j; + for (j = 0; j < i; j++) + { + if (p[j] >= '0'&&p[j] <= '9') + { + hash = hash * 10 + p[j] - '0'; + } + } + return hash; +} +//整理扫描到的当前单词信息(在哪个文件的哪里),再插入 , address标志了单词位置 +void Insertword(string voc, string filename, int address, hashTable* t) +{ + char p[50]; + strcpy(p, voc.c_str()); + char *fname = (char*)filename.c_str(); + if (findValue(t,p)==NULL)//假如索引里头没有这个词我们就把他加进去 + { + LinkList L; + InitList(&L); + ListInsert(&L, 1, address); + RBRoot *root; + root = create_rbtree(); + RBleaf *leaf = (RBleaf*)malloc(sizeof(RBleaf)); + strcpy(leaf->filename, filename.c_str()); + leaf->hashfile = hashfile(fname); + leaf->index = L; + insert_rbtree(root, *leaf); + insertEntry(t, p, root); + //cout << "haha" << endl; + //inorder_rbtree(t->bucket[hashfunc(p,strlen(p))].value); + } + else//有的话就得更新它的数据 + { + RBRoot *tempRB=findValue(t, p); + //如果小向量红黑树sour中没有找到当前文件名(意思是现在找到的词是本文件中第一次出现) + if (rbtree_search(tempRB,hashfile(fname))==NULL) + { + LinkList L; + InitList(&L); + ListInsert(&L, 1, address); + RBleaf *leaf = (RBleaf*)malloc(sizeof(RBleaf)); + strcpy(leaf->filename, filename.c_str()); + leaf->hashfile = hashfile(fname); + leaf->index = L; + insert_rbtree(tempRB, *leaf); + //inorder_rbtree(t->bucket[hashfunc(p, strlen(p))].value); + //insertEntry(t, p, tempRB); + } + //如果小向量sour中找到了当前文件名(意思是现在找到的词在本文件中出现过,NA向量已经建立过了,直接加就行) + else + { + RBleaf*templeaf = rbtree_search(tempRB, hashfile(fname)); + ListInsert(&templeaf->index, 1, address); + //inorder_rbtree(t->bucket[hashfunc(p, strlen(p))].value); + } + } +} + +void BuildIndex(hashTable *Index) +{ + int cou; + int num; + + string filename = ""; + ifstream fp; + string voc = ""; + int address; + char ch; + + //这个双层循环是配合Filename函数(自己写的)完成->读所有文件的任务 + for (cou = 1001; cou <= 1042; cou++) + { + + for (num = 1; num <= 153; num++) + { + filename = Filename(cou, num); + fp.open("Shakespeare/" + filename, ios_base::in | ios_base::binary); + if (!fp.is_open()) + continue; + cout << filename << endl; + address = 0; + fp.get(ch); + while (fp)//以前学过的逐个单词读 + { + voc = ""; + + + //不是单词且没到结尾就继续 + while (!isword(ch) && !fp.eof()) + { + fp.get(ch); + } + + //到结尾就退出(去下一个文件) + if (!fp) + break; + + //是单词的操作就在String vocabulary中连缀上它 + while (isword(ch) && !fp.eof()) + { + voc += ch; + fp.get(ch); + } + transform(voc.begin(), voc.end(), voc.begin(), tolower); //转化成小写字母 + + //下标移动(数在第几个位置嘛) + address++; + + //假如不是屏蔽词汇,就整理单词信息再插入索引(用之前的Insert函数) + if (!isstopword(voc)) + { + Insertword(voc, filename, address, Index); + //getchar(); + } + } + fp.close(); + } + } + +} +void test() //测试功能专用 +{ + LinkList L; + InitList(&L); + ListInsert(&L, 1, 10); + ListInsert(&L, 1, 30); + hashTable t; + initHashTable(&t); + RBRoot *root = NULL; + root = create_rbtree(); + RBleaf *leaf = (RBleaf*)malloc(sizeof(RBleaf)); + //leaf->filename = "hahah"; + leaf->hashfile = 20; + leaf->index = L; + insert_rbtree(root, *leaf); + leaf = (RBleaf*)malloc(sizeof(RBleaf)); + //leaf->filename = "hahaha"; + leaf->hashfile = 10; + leaf->index = L; + insert_rbtree(root, *leaf); + insertEntry(&t, "son", root); + insertEntry(&t, "on", root); + RBRoot *b = findValue(&t, "son"); + inorder_rbtree(b); + getchar(); +} +int main(){ + //test(); + hashTable t; + initHashTable(&t); + cout << "屏蔽词载入中..." << endl; + collect_stopword(); + cout << "屏蔽词库建立完成" << endl; + cout << "倒排索引建立中..." << endl; + BuildIndex(&t); + cout << "倒排索引建立完成" << endl; + while (1) { + string s; + cin >> s; + char *p = (char *)s.c_str(); + inorder_rbtree(t.bucket[hashfunc(p, strlen(p))].value); + } + //inorder_rbtree((RBRoot*)t.bucket[hashfunc(p,strlen(p))].value); + getchar(); + getchar(); +} \ No newline at end of file diff --git a/rbtree.c b/rbtree.c new file mode 100644 index 0000000..5f44378 --- /dev/null +++ b/rbtree.c @@ -0,0 +1,668 @@ +/** +* C语言实现的红黑树(Red Black Tree) +* +* @author skywang +* @date 2013/11/18 +*/ +#pragma once +#include +#include +#include "rbtree.h" +#define rb_parent(r) ((r)->parent) +#define rb_color(r) ((r)->color) +#define rb_is_red(r) ((r)->color==RED) +#define rb_is_black(r) ((r)->color==BLACK) +#define rb_set_black(r) do { (r)->color = BLACK; } while (0) +#define rb_set_red(r) do { (r)->color = RED; } while (0) +#define rb_set_parent(r,p) do { (r)->parent = (p); } while (0) +#define rb_set_color(r,c) do { (r)->color = (c); } while (0) + +/* +* 创建红黑树,返回"红黑树的根"! +*/ +RBRoot* create_rbtree() +{ + RBRoot *root = (RBRoot *)malloc(sizeof(RBRoot)); + root->node = NULL; + + return root; +} + + +/* +* 中序遍历"红黑树" +*/ +int ListTraverse(LinkList L) +{ + LinkList p = L->next; + while (p) + { + printf("%d ", p->data); + p = p->next; + } + printf("\n"); + return 1; +} +static void inorder(RBTree tree) +{ + if (tree != NULL) + { + inorder(tree->left); + printf("%s:", tree->key.filename); + ListTraverse(tree->key.index); + inorder(tree->right); + } +} +void inorder_rbtree(RBRoot *root) +{ + if (root) + inorder(root->node); +} + + +/* +* (递归实现)查找"红黑树x"中键值为key的节点 +*/ +static Node* search(RBTree x, int key) +{ + if (x == NULL || x->key.hashfile == key) + return x; + + if (key < x->key.hashfile) + return search(x->left, key); + else + return search(x->right, key); +} +RBleaf* rbtree_search(RBRoot *root, int key) +{ + if (root) { + Node* temp = search(root->node, key); + if ( temp!= NULL) + { + return &temp->key; + } + } + return NULL; +} + +/* +* 找结点(x)的后继结点。即,查找"红黑树中数据值大于该结点"的"最小结点"。 +*/ +static Node* rbtree_successor(RBTree x) +{ + // 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。 + if (x->right != NULL) + return minimum(x->right); + + // 如果x没有右孩子。则x有以下两种可能: + // (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。 + // (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。 + Node* y = x->parent; + while ((y != NULL) && (x == y->right)) + { + x = y; + y = y->parent; + } + + return y; +} + +/* +* 找结点(x)的前驱结点。即,查找"红黑树中数据值小于该结点"的"最大结点"。 +*/ +static Node* rbtree_predecessor(RBTree x) +{ + // 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。 + if (x->left != NULL) + return maximum(x->left); + + // 如果x没有左孩子。则x有以下两种可能: + // (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。 + // (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。 + Node* y = x->parent; + while ((y != NULL) && (x == y->left)) + { + x = y; + y = y->parent; + } + + return y; +} + +/* +* 对红黑树的节点(x)进行左旋转 +* +* 左旋示意图(对节点x进行左旋): +* px px +* / / +* x y +* / \ --(左旋)--> / \ # +* lx y x ry +* / \ / \ +* ly ry lx ly +* +* +*/ +static void rbtree_left_rotate(RBRoot *root, Node *x) +{ + // 设置x的右孩子为y + Node *y = x->right; + + // 将 “y的左孩子” 设为 “x的右孩子”; + // 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲” + x->right = y->left; + if (y->left != NULL) + y->left->parent = x; + + // 将 “x的父亲” 设为 “y的父亲” + y->parent = x->parent; + + if (x->parent == NULL) + { + //tree = y; // 如果 “x的父亲” 是空节点,则将y设为根节点 + root->node = y; // 如果 “x的父亲” 是空节点,则将y设为根节点 + } + else + { + if (x->parent->left == x) + x->parent->left = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子” + else + x->parent->right = y; // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子” + } + + // 将 “x” 设为 “y的左孩子” + y->left = x; + // 将 “x的父节点” 设为 “y” + x->parent = y; +} + +/* +* 对红黑树的节点(y)进行右旋转 +* +* 右旋示意图(对节点y进行左旋): +* py py +* / / +* y x +* / \ --(右旋)--> / \ # +* x ry lx y +* / \ / \ # +* lx rx rx ry +* +*/ +static void rbtree_right_rotate(RBRoot *root, Node *y) +{ + // 设置x是当前节点的左孩子。 + Node *x = y->left; + + // 将 “x的右孩子” 设为 “y的左孩子”; + // 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲” + y->left = x->right; + if (x->right != NULL) + x->right->parent = y; + + // 将 “y的父亲” 设为 “x的父亲” + x->parent = y->parent; + + if (y->parent == NULL) + { + //tree = x; // 如果 “y的父亲” 是空节点,则将x设为根节点 + root->node = x; // 如果 “y的父亲” 是空节点,则将x设为根节点 + } + else + { + if (y == y->parent->right) + y->parent->right = x; // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子” + else + y->parent->left = x; // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子” + } + + // 将 “y” 设为 “x的右孩子” + x->right = y; + + // 将 “y的父节点” 设为 “x” + y->parent = x; +} + +/* +* 红黑树插入修正函数 +* +* 在向红黑树中插入节点之后(失去平衡),再调用该函数; +* 目的是将它重新塑造成一颗红黑树。 +* +* 参数说明: +* root 红黑树的根 +* node 插入的结点 // 对应《算法导论》中的z +*/ +static void rbtree_insert_fixup(RBRoot *root, Node *node) +{ + Node *parent, *gparent; + + // 若“父节点存在,并且父节点的颜色是红色” + while ((parent = rb_parent(node)) && rb_is_red(parent)) + { + gparent = rb_parent(parent); + + //若“父节点”是“祖父节点的左孩子” + if (parent == gparent->left) + { + // Case 1条件:叔叔节点是红色 + { + Node *uncle = gparent->right; + if (uncle && rb_is_red(uncle)) + { + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); + node = gparent; + continue; + } + } + + // Case 2条件:叔叔是黑色,且当前节点是右孩子 + if (parent->right == node) + { + Node *tmp; + rbtree_left_rotate(root, parent); + tmp = parent; + parent = node; + node = tmp; + } + + // Case 3条件:叔叔是黑色,且当前节点是左孩子。 + rb_set_black(parent); + rb_set_red(gparent); + rbtree_right_rotate(root, gparent); + } + else//若“z的父节点”是“z的祖父节点的右孩子” + { + // Case 1条件:叔叔节点是红色 + { + Node *uncle = gparent->left; + if (uncle && rb_is_red(uncle)) + { + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); + node = gparent; + continue; + } + } + + // Case 2条件:叔叔是黑色,且当前节点是左孩子 + if (parent->left == node) + { + Node *tmp; + rbtree_right_rotate(root, parent); + tmp = parent; + parent = node; + node = tmp; + } + + // Case 3条件:叔叔是黑色,且当前节点是右孩子。 + rb_set_black(parent); + rb_set_red(gparent); + rbtree_left_rotate(root, gparent); + } + } + + // 将根节点设为黑色 + rb_set_black(root->node); +} + +/* +* 添加节点:将节点(node)插入到红黑树中 +* +* 参数说明: +* root 红黑树的根 +* node 插入的结点 // 对应《算法导论》中的z +*/ +static void rbtree_insert(RBRoot *root, Node *node) +{ + Node *y = NULL; + Node *x = root->node; + + // 1. 将红黑树当作一颗二叉查找树,将节点添加到二叉查找树中。 + while (x != NULL) + { + y = x; + if (node->key.hashfile < x->key.hashfile) + x = x->left; + else + x = x->right; + } + rb_parent(node) = y; + + if (y != NULL) + { + if (node->key.hashfile < y->key.hashfile) + y->left = node; // 情况2:若“node所包含的值” < “y所包含的值”,则将node设为“y的左孩子” + else + y->right = node; // 情况3:(“node所包含的值” >= “y所包含的值”)将node设为“y的右孩子” + } + else + { + root->node = node; // 情况1:若y是空节点,则将node设为根 + } + + // 2. 设置节点的颜色为红色 + node->color = RED; + + // 3. 将它重新修正为一颗二叉查找树 + rbtree_insert_fixup(root, node); +} + +/* +* 创建结点 +* +* 参数说明: +* key 是键值。 +* parent 是父结点。 +* left 是左孩子。 +* right 是右孩子。 +*/ +static Node* create_rbtree_node(RBleaf key, Node *parent, Node *left, Node* right) +{ + Node* p; + + if ((p = (Node *)malloc(sizeof(Node))) == NULL) + return NULL; + p->key = key; + p->left = left; + p->right = right; + p->parent = parent; + p->color = BLACK; // 默认为黑色 + + return p; +} + +/* +* 新建结点(节点键值为key),并将其插入到红黑树中 +* +* 参数说明: +* root 红黑树的根 +* key 插入结点的键值 +* 返回值: +* 0,插入成功 +* -1,插入失败 +*/ +int insert_rbtree(RBRoot *root, RBleaf key) +{ + Node *node; // 新建结点 + + // 不允许插入相同键值的节点。 + // (若想允许插入相同键值的节点,注释掉下面两句话即可!) + if (search(root->node, key.hashfile) != NULL) + return -1; + + // 如果新建结点失败,则返回。 + if ((node = create_rbtree_node(key, NULL, NULL, NULL)) == NULL) + return -1; + + rbtree_insert(root, node); + + return 0; +} + +/* +* 红黑树删除修正函数 +* +* 在从红黑树中删除插入节点之后(红黑树失去平衡),再调用该函数; +* 目的是将它重新塑造成一颗红黑树。 +* +* 参数说明: +* root 红黑树的根 +* node 待修正的节点 +*/ +static void rbtree_delete_fixup(RBRoot *root, Node *node, Node *parent) +{ + Node *other; + + while ((!node || rb_is_black(node)) && node != root->node) + { + if (parent->left == node) + { + other = parent->right; + if (rb_is_red(other)) + { + // Case 1: x的兄弟w是红色的 + rb_set_black(other); + rb_set_red(parent); + rbtree_left_rotate(root, parent); + other = parent->right; + } + if ((!other->left || rb_is_black(other->left)) && + (!other->right || rb_is_black(other->right))) + { + // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的 + rb_set_red(other); + node = parent; + parent = rb_parent(node); + } + else + { + if (!other->right || rb_is_black(other->right)) + { + // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。 + rb_set_black(other->left); + rb_set_red(other); + rbtree_right_rotate(root, other); + other = parent->right; + } + // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。 + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); + rb_set_black(other->right); + rbtree_left_rotate(root, parent); + node = root->node; + break; + } + } + else + { + other = parent->left; + if (rb_is_red(other)) + { + // Case 1: x的兄弟w是红色的 + rb_set_black(other); + rb_set_red(parent); + rbtree_right_rotate(root, parent); + other = parent->left; + } + if ((!other->left || rb_is_black(other->left)) && + (!other->right || rb_is_black(other->right))) + { + // Case 2: x的兄弟w是黑色,且w的俩个孩子也都是黑色的 + rb_set_red(other); + node = parent; + parent = rb_parent(node); + } + else + { + if (!other->left || rb_is_black(other->left)) + { + // Case 3: x的兄弟w是黑色的,并且w的左孩子是红色,右孩子为黑色。 + rb_set_black(other->right); + rb_set_red(other); + rbtree_left_rotate(root, other); + other = parent->left; + } + // Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。 + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); + rb_set_black(other->left); + rbtree_right_rotate(root, parent); + node = root->node; + break; + } + } + } + if (node) + rb_set_black(node); +} + +/* +* 删除结点 +* +* 参数说明: +* tree 红黑树的根结点 +* node 删除的结点 +*/ +void rbtree_delete(RBRoot *root, Node *node) +{ + Node *child, *parent; + int color; + + // 被删除节点的"左右孩子都不为空"的情况。 + if ((node->left != NULL) && (node->right != NULL)) + { + // 被删节点的后继节点。(称为"取代节点") + // 用它来取代"被删节点"的位置,然后再将"被删节点"去掉。 + Node *replace = node; + + // 获取后继节点 + replace = replace->right; + while (replace->left != NULL) + replace = replace->left; + + // "node节点"不是根节点(只有根节点不存在父节点) + if (rb_parent(node)) + { + if (rb_parent(node)->left == node) + rb_parent(node)->left = replace; + else + rb_parent(node)->right = replace; + } + else + // "node节点"是根节点,更新根节点。 + root->node = replace; + + // child是"取代节点"的右孩子,也是需要"调整的节点"。 + // "取代节点"肯定不存在左孩子!因为它是一个后继节点。 + child = replace->right; + parent = rb_parent(replace); + // 保存"取代节点"的颜色 + color = rb_color(replace); + + // "被删除节点"是"它的后继节点的父节点" + if (parent == node) + { + parent = replace; + } + else + { + // child不为空 + if (child) + rb_set_parent(child, parent); + parent->left = child; + + replace->right = node->right; + rb_set_parent(node->right, replace); + } + + replace->parent = node->parent; + replace->color = node->color; + replace->left = node->left; + node->left->parent = replace; + + if (color == BLACK) + rbtree_delete_fixup(root, child, parent); + free(node); + + return; + } + + if (node->left != NULL) + child = node->left; + else + child = node->right; + + parent = node->parent; + // 保存"取代节点"的颜色 + color = node->color; + + if (child) + child->parent = parent; + + // "node节点"不是根节点 + if (parent) + { + if (parent->left == node) + parent->left = child; + else + parent->right = child; + } + else + root->node = child; + + if (color == BLACK) + rbtree_delete_fixup(root, child, parent); + free(node); +} + +/* +* 删除键值为key的结点 +* +* 参数说明: +* tree 红黑树的根结点 +* key 键值 +*/ +void delete_rbtree(RBRoot *root, RBleaf key) +{ + Node *z, *node; + + if ((z = search(root->node, key.hashfile)) != NULL) + rbtree_delete(root, z); +} + +/* +* 销毁红黑树 +*/ +static void rbtree_destroy(RBTree tree) +{ + if (tree == NULL) + return; + + if (tree->left != NULL) + rbtree_destroy(tree->left); + if (tree->right != NULL) + rbtree_destroy(tree->right); + + free(tree); +} + +void destroy_rbtree(RBRoot *root) +{ + if (root != NULL) + rbtree_destroy(root->node); + + free(root); +} + +/* +* 打印"红黑树" +* +* tree -- 红黑树的节点 +* key -- 节点的键值 +* direction -- 0,表示该节点是根节点; +* -1,表示该节点是它的父结点的左孩子; +* 1,表示该节点是它的父结点的右孩子。 +*/ +static void rbtree_print(RBTree tree, RBleaf key, int direction) +{ + if (tree != NULL) + { + if (direction == 0) // tree是根节点 + printf("%2d(B) is root\n", tree->key); + else // tree是分支节点 + printf("%2d(%s) is %2d's %6s child\n", tree->key, rb_is_red(tree) ? "R" : "B", key, direction == 1 ? "right" : "left"); + + rbtree_print(tree->left, tree->key, -1); + rbtree_print(tree->right, tree->key, 1); + } +} + +void print_rbtree(RBRoot *root) +{ + if (root != NULL && root->node != NULL) + rbtree_print(root->node, root->node->key, 0); +} diff --git a/rbtree.h b/rbtree.h new file mode 100644 index 0000000..96a5588 --- /dev/null +++ b/rbtree.h @@ -0,0 +1,44 @@ +#pragma once +#define RED 0 // 红色节点 +#define BLACK 1 // 黑色节点 +#include "List.h" + +typedef struct RBleaf { + int hashfile; + char filename[50]; + LinkList index; +}RBleaf; +// 红黑树的节点 +typedef struct RBTreeNode { + unsigned char color; // 颜色(RED 或 BLACK) + RBleaf key; // 关键字(键值) + struct RBTreeNode *left; // 左孩子 + struct RBTreeNode *right; // 右孩子 + struct RBTreeNode *parent; // 父结点 +}Node, *RBTree; + +// 红黑树的根 +typedef struct rb_root { + Node *node; +}RBRoot; + +// 创建红黑树,返回"红黑树的根"! +RBRoot* create_rbtree(); + +// 销毁红黑树 +void destroy_rbtree(RBRoot *root); + +// 将结点插入到红黑树中。插入成功,返回0;失败返回-1。 +int insert_rbtree(RBRoot *root, RBleaf key); + +// 删除结点(key为节点的值) +void delete_rbtree(RBRoot *root, RBleaf key); + + +// 中序遍历"红黑树" +void inorder_rbtree(RBRoot *root); + +// (递归实现)查找"红黑树"中键值为key的节点。找到的话,返回0;否则,返回-1。 +RBleaf* rbtree_search(RBRoot *root, int hashkey); +// 打印红黑树 +void print_rbtree(RBRoot *root); \ No newline at end of file