C++中的数据结构与算法

在这里插入图片描述

随处可见的红黑树

一般会用到[key,value]。
例如github中这个例子,第一个是访问网站,第二个是访问次数,但是这个不是静态的,这有个动态排序,并且当我们需要让相应的访问次数加1的时候,我们用红黑树查找的时候会比较快,所以用红黑树表示这个结构比较号。
在这里插入图片描述
所以红黑树普遍用于强查找过程。对于这种强查找的过程:我们普遍用rbtree,hash,b/b+ tree,或者跳表。

红黑树的性质和定义

红黑树的性质:
1.每个结点是红的或者黑的2.根结点是黑的
3.每个叶子结点是黑的
4.如果一个结点是红的,则它的两个儿子都是黑的(红红不相邻)
5.对每个结点,从该结点到其子孙结点的所有路径上的包含相同数目的黑结点
在这里插入图片描述

在这里插入图片描述在这里插入图片描述
对于一个红黑树的定义:注意这个nil指的是叶子节点,也就是那个隐藏的那个黑节点。

typedef int KEY_TYPE;typedef struct _rbtree_node {unsigned char color;struct _rbtree_node *right;struct _rbtree_node *left;struct _rbtree_node *parent;KEY_TYPE key;void *value;
} rbtree_node;typedef struct _rbtree {rbtree_node *root;//根节点rbtree_node *nil;//叶子节点
} rbtree;

红黑树的左右旋

对于红黑树的旋转:
在这里插入图片描述
这里我们需要改变6根指针:
code:
这里主要需要判断的是x的parent是不是根节点。如果是的话,那么之间让根节点root指向y就行。
在这里插入图片描述

void rbtree_left_rotate(rbtree *T, rbtree_node *x) {rbtree_node *y = x->right;  // x  --> y  ,  y --> x,   right --> left,  left --> rightx->right = y->left; //1 1if (y->left != T->nil) { //1 2y->left->parent = x;}y->parent = x->parent; //1 3if (x->parent == T->nil) { //1 4T->root = y;} else if (x == x->parent->left) {x->parent->left = y;} else {x->parent->right = y;}y->left = x; //1 5x->parent = y; //1 6
}

右旋:
也就是上面的代码x改成y,right改成left。

void rbtree_right_rotate(rbtree *T, rbtree_node *y) {rbtree_node *x = y->left;y->left = x->right;if (x->right != T->nil) {x->right->parent = y;}x->parent = y->parent;if (y->parent == T->nil) {T->root = x;} else if (y == y->parent->right) {y->parent->right = x;} else {y->parent->left = x;}x->right = y;y->parent = x;
}

在这里插入图片描述

红黑树的插入

对于插入的时候我们都是一插到底,一直插到根节点。并且插入的节点定义为红色,然后再进行颜色的调整。而且它的父节点也是红色,因为原本的节点他的两个根是黑色,所以,这个父节点应该是红色。
因为红黑树在插入节点之前他已经是一个红黑树了。所以插入红色,不改变黑高的性质。
插入code:

void rbtree_insert(rbtree *T, rbtree_node *z) {rbtree_node *y = T->nil;rbtree_node *x = T->root;while (x != T->nil) {y = x;//y就是x的parentif (z->key < x->key) {x = x->left;} else if (z->key > x->key) {x = x->right;} else { //Existreturn ;}}z->parent = y;if (y == T->nil) {T->root = z;} else if (z->key < y->key) {y->left = z;} else {y->right = z;}z->left = T->nil;z->right = T->nil;z->color = RED;rbtree_insert_fixup(T, z);
}

调整颜色,让其满足性质:

我们发现如果定义为红色之后,满足性质1,2,3。不知道满不满足4,5.所以我们要让其先满足5在满足4。
还没调整前的情况:假设插入的节点是z,z的父节点是y

z是红色
z的父节点y也是红色
z的祖父节点是黑色
z的叔父节点是不确定

那么就两种情况:

  1. z的叔父节点是红色
    在这里插入图片描述
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent; //z --> RED,需要回溯
  1. z的叔父节点是黑色
    这两种情况是调整出来的,因为你调整完之后需要回溯,因为你调整完之后它的祖父可能不满足所以z = z->parent->parent。然后这种情况是要旋转的。然后旋转之后的图是中间状态的图,然后旋转完之后就是改色。
    在这里插入图片描述
if (z == z->parent->right) {z = z->parent;rbtree_left_rotate(T, z);
}

在这里插入图片描述

z->parent->color = BLACK;
z->parent->parent->color = RED;
rbtree_right_rotate(T, z->parent->parent);

然后叔父节点是黑色的完整代码就是这个:

if (z == z->parent->right) {z = z->parent;rbtree_left_rotate(T, z);
}z->parent->color = BLACK;
z->parent->parent->color = RED;
rbtree_right_rotate(T, z->parent->parent);

然后父节点是祖父节点的左子树的情况代码就是这样的:

if (z->parent == z->parent->parent->left) {rbtree_node *y = z->parent->parent->right;if (y->color == RED) {z->parent->color = BLACK;y->color = BLACK;z->parent->parent->color = RED;z = z->parent->parent; //z --> RED,需要回溯} else {if (z == z->parent->right) {z = z->parent;rbtree_left_rotate(T, z);}z->parent->color = BLACK;z->parent->parent->color = RED;rbtree_right_rotate(T, z->parent->parent);}
}

然后父节点是祖父节点的右子树的情况和上面差不多
完整代码就是:

void rbtree_insert_fixup(rbtree *T, rbtree_node *z) {while (z->parent->color == RED) { //z ---> REDif (z->parent == z->parent->parent->left) {rbtree_node *y = z->parent->parent->right;if (y->color == RED) {z->parent->color = BLACK;y->color = BLACK;z->parent->parent->color = RED;z = z->parent->parent; //z --> RED,需要回溯} else {if (z == z->parent->right) {z = z->parent;rbtree_left_rotate(T, z);}z->parent->color = BLACK;z->parent->parent->color = RED;rbtree_right_rotate(T, z->parent->parent);}}else {rbtree_node *y = z->parent->parent->left;if (y->color == RED) {z->parent->color = BLACK;y->color = BLACK;z->parent->parent->color = RED;z = z->parent->parent; //z --> RED} else {if (z == z->parent->left) {z = z->parent;rbtree_right_rotate(T, z);}z->parent->color = BLACK;z->parent->parent->color = RED;rbtree_left_rotate(T, z->parent->parent);}}}T->root->color = BLACK;
}

红黑树的删除

这个比较难,不需要掌握
完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define RED				1
#define BLACK 			2typedef int KEY_TYPE;typedef struct _rbtree_node {unsigned char color;struct _rbtree_node *right;struct _rbtree_node *left;struct _rbtree_node *parent;KEY_TYPE key;void *value;
} rbtree_node;typedef struct _rbtree {rbtree_node *root;rbtree_node *nil;
} rbtree;rbtree_node *rbtree_mini(rbtree *T, rbtree_node *x) {while (x->left != T->nil) {x = x->left;}return x;
}rbtree_node *rbtree_maxi(rbtree *T, rbtree_node *x) {while (x->right != T->nil) {x = x->right;}return x;
}rbtree_node *rbtree_successor(rbtree *T, rbtree_node *x) {rbtree_node *y = x->parent;if (x->right != T->nil) {return rbtree_mini(T, x->right);}while ((y != T->nil) && (x == y->right)) {x = y;y = y->parent;}return y;
}void rbtree_left_rotate(rbtree *T, rbtree_node *x) {rbtree_node *y = x->right;  // x  --> y  ,  y --> x,   right --> left,  left --> rightx->right = y->left; //1 1if (y->left != T->nil) { //1 2y->left->parent = x;}y->parent = x->parent; //1 3if (x->parent == T->nil) { //1 4T->root = y;} else if (x == x->parent->left) {x->parent->left = y;} else {x->parent->right = y;}y->left = x; //1 5x->parent = y; //1 6
}void rbtree_right_rotate(rbtree *T, rbtree_node *y) {rbtree_node *x = y->left;y->left = x->right;if (x->right != T->nil) {x->right->parent = y;}x->parent = y->parent;if (y->parent == T->nil) {T->root = x;} else if (y == y->parent->right) {y->parent->right = x;} else {y->parent->left = x;}x->right = y;y->parent = x;
}void rbtree_insert_fixup(rbtree *T, rbtree_node *z) {while (z->parent->color == RED) { //z ---> REDif (z->parent == z->parent->parent->left) {rbtree_node *y = z->parent->parent->right;if (y->color == RED) {z->parent->color = BLACK;y->color = BLACK;z->parent->parent->color = RED;z = z->parent->parent; //z --> RED,需要回溯} else {if (z == z->parent->right) {z = z->parent;rbtree_left_rotate(T, z);}z->parent->color = BLACK;z->parent->parent->color = RED;rbtree_right_rotate(T, z->parent->parent);}}else {rbtree_node *y = z->parent->parent->left;if (y->color == RED) {z->parent->color = BLACK;y->color = BLACK;z->parent->parent->color = RED;z = z->parent->parent; //z --> RED} else {if (z == z->parent->left) {z = z->parent;rbtree_right_rotate(T, z);}z->parent->color = BLACK;z->parent->parent->color = RED;rbtree_left_rotate(T, z->parent->parent);}}}T->root->color = BLACK;
}void rbtree_insert(rbtree *T, rbtree_node *z) {rbtree_node *y = T->nil;rbtree_node *x = T->root;while (x != T->nil) {y = x;//y就是x的parentif (z->key < x->key) {x = x->left;} else if (z->key > x->key) {x = x->right;} else { //Existreturn ;}}z->parent = y;if (y == T->nil) {T->root = z;} else if (z->key < y->key) {y->left = z;} else {y->right = z;}z->left = T->nil;z->right = T->nil;z->color = RED;rbtree_insert_fixup(T, z);
}void rbtree_delete_fixup(rbtree *T, rbtree_node *x) {while ((x != T->root) && (x->color == BLACK)) {if (x == x->parent->left) {rbtree_node *w= x->parent->right;if (w->color == RED) {w->color = BLACK;x->parent->color = RED;rbtree_left_rotate(T, x->parent);w = x->parent->right;}if ((w->left->color == BLACK) && (w->right->color == BLACK)) {w->color = RED;x = x->parent;} else {if (w->right->color == BLACK) {w->left->color = BLACK;w->color = RED;rbtree_right_rotate(T, w);w = x->parent->right;}w->color = x->parent->color;x->parent->color = BLACK;w->right->color = BLACK;rbtree_left_rotate(T, x->parent);x = T->root;}} else {rbtree_node *w = x->parent->left;if (w->color == RED) {w->color = BLACK;x->parent->color = RED;rbtree_right_rotate(T, x->parent);w = x->parent->left;}if ((w->left->color == BLACK) && (w->right->color == BLACK)) {w->color = RED;x = x->parent;} else {if (w->left->color == BLACK) {w->right->color = BLACK;w->color = RED;rbtree_left_rotate(T, w);w = x->parent->left;}w->color = x->parent->color;x->parent->color = BLACK;w->left->color = BLACK;rbtree_right_rotate(T, x->parent);x = T->root;}}}x->color = BLACK;
}rbtree_node *rbtree_delete(rbtree *T, rbtree_node *z) {rbtree_node *y = T->nil;rbtree_node *x = T->nil;if ((z->left == T->nil) || (z->right == T->nil)) {y = z;} else {y = rbtree_successor(T, z);}if (y->left != T->nil) {x = y->left;} else if (y->right != T->nil) {x = y->right;}x->parent = y->parent;if (y->parent == T->nil) {T->root = x;} else if (y == y->parent->left) {y->parent->left = x;} else {y->parent->right = x;}if (y != z) {z->key = y->key;z->value = y->value;}if (y->color == BLACK) {rbtree_delete_fixup(T, x);}return y;
}rbtree_node *rbtree_search(rbtree *T, KEY_TYPE key) {rbtree_node *node = T->root;while (node != T->nil) {if (key < node->key) {node = node->left;} else if (key > node->key) {node = node->right;} else {return node;}	}return T->nil;
}void rbtree_traversal(rbtree *T, rbtree_node *node) {if (node != T->nil) {rbtree_traversal(T, node->left);printf("key:%d, color:%d\n", node->key, node->color);rbtree_traversal(T, node->right);}
}int main() {int keyArray[20] = {24,25,13,35,23, 26,67,47,38,98, 20,19,17,49,12, 21,9,18,14,15};rbtree *T = (rbtree *)malloc(sizeof(rbtree));if (T == NULL) {printf("malloc failed\n");return -1;}T->nil = (rbtree_node*)malloc(sizeof(rbtree_node));T->nil->color = BLACK;T->root = T->nil;rbtree_node *node = T->nil;int i = 0;for (i = 0;i < 20;i ++) {node = (rbtree_node*)malloc(sizeof(rbtree_node));node->key = keyArray[i];node->value = NULL;rbtree_insert(T, node);}rbtree_traversal(T, T->root);printf("----------------------------------------\n");for (i = 0;i < 20;i ++) {rbtree_node *node = rbtree_search(T, keyArray[i]);rbtree_node *cur = rbtree_delete(T, node);free(cur);rbtree_traversal(T, T->root);printf("----------------------------------------\n");}}

b/b+树

对于,红黑树,b/b+树,都是强查找数据类型,这种像是在一个大的集合中查找一个东西这种比较常见。
对于二叉树,1023个节点,我们可以用10层就可以表示了。对于这种树高,他的影响就是比对次数,找下一个节点多 。如果储存在一些节点是储存到内存中,那么还好,但是如果一些节点是存储在磁盘中,那么查找的次数会变多,所以出现了很多降层高的数据结构。因为如果我们每一个节点都存储在磁盘中,那么每次对比后找下一个节点就是一次磁盘寻址。那么层高越高,查找越耗时。
所以假设是1024个节点,四叉树。每个节点3个数据。那么就是 l o g 4 ( 1024 / 3 ) log_4{(1024/3)} log4(1024/3)那么就是5层就可以了。

btree/b-tree

一颗 M M M B B B T T T,满足以下条件

  1. 每个结点至多拥有 M M M颗子树
  2. 根结点至少拥有两颗子树
  3. 除了根结点以外,其余每个分支结点至少拥有M/2课子树
  4. 所有的叶结点都在同一层上
  5. 有k课子树的分支结点则存在 k − 1 k-1 k1个关键字,关键字按照递增顺序进行排序
  6. 关键字数量满足 c e i l ( M / 2 ) − 1 < = n < = M − 1 ceil(M/2)-1 <= n <= M-1 ceil(M/2)1<=n<=M1

b树每次添加都是添加到叶子节点的,如果叶子节点满了就分裂。如果根满了就,分裂,然后增加树高。

b树删除的时候,要么就是可以之间删除,不能直接删除就借位,借位不够的话就合并(父节点合并)。

创建一个节点:

btree_node *btree_create_node(int t, int leaf) {btree_node *node = (btree_node*)calloc(1, sizeof(btree_node));if (node == NULL) assert(0);node->leaf = leaf;node->keys = (KEY_VALUE*)calloc(1, (2*t-1)*sizeof(KEY_VALUE));node->childrens = (btree_node**)calloc(1, (2*t) * sizeof(btree_node*));node->num = 0;return node;
}

删除一个节点

void btree_destroy_node(btree_node *node) {assert(node);free(node->childrens);free(node->keys);free(node);}

节点分裂:一分为二
在这里插入图片描述

在这里插入图片描述
根节点分裂:一分为三

合并:

//{child[idx], key[idx], child[idx+1]} 
void btree_merge(btree *T, btree_node *node, int idx) {btree_node *left = node->childrens[idx];btree_node *right = node->childrens[idx+1];int i = 0;/data mergeleft->keys[T->t-1] = node->keys[idx];for (i = 0;i < T->t-1;i ++) {left->keys[T->t+i] = right->keys[i];}if (!left->leaf) {for (i = 0;i < T->t;i ++) {left->childrens[T->t+i] = right->childrens[i];}}left->num += T->t;//destroy rightbtree_destroy_node(right);//node for (i = idx+1;i < node->num;i ++) {node->keys[i-1] = node->keys[i];node->childrens[i] = node->childrens[i+1];}node->childrens[i+1] = NULL;node->num -= 1;if (node->num == 0) {T->root = left;btree_destroy_node(node);}
}

完整:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <assert.h>#define DEGREE		3
typedef int KEY_VALUE;typedef struct _btree_node {KEY_VALUE *keys;struct _btree_node **childrens;int num;int leaf;//叶子个数
} btree_node;typedef struct _btree {btree_node *root;int t;
} btree;btree_node *btree_create_node(int t, int leaf) {btree_node *node = (btree_node*)calloc(1, sizeof(btree_node));if (node == NULL) assert(0);node->leaf = leaf;node->keys = (KEY_VALUE*)calloc(1, (2*t-1)*sizeof(KEY_VALUE));node->childrens = (btree_node**)calloc(1, (2*t) * sizeof(btree_node*));node->num = 0;return node;
}void btree_destroy_node(btree_node *node) {assert(node);free(node->childrens);free(node->keys);free(node);}void btree_create(btree *T, int t) {T->t = t;btree_node *x = btree_create_node(t, 1);T->root = x;}void btree_split_child(btree *T, btree_node *x, int i) {int t = T->t;btree_node *y = x->childrens[i];btree_node *z = btree_create_node(t, y->leaf);z->num = t - 1;int j = 0;for (j = 0;j < t-1;j ++) {z->keys[j] = y->keys[j+t];}if (y->leaf == 0) {for (j = 0;j < t;j ++) {z->childrens[j] = y->childrens[j+t];}}y->num = t - 1;for (j = x->num;j >= i+1;j --) {x->childrens[j+1] = x->childrens[j];}x->childrens[i+1] = z;for (j = x->num-1;j >= i;j --) {x->keys[j+1] = x->keys[j];}x->keys[i] = y->keys[t-1];x->num += 1;}void btree_insert_nonfull(btree *T, btree_node *x, KEY_VALUE k) {int i = x->num - 1;if (x->leaf == 1) {while (i >= 0 && x->keys[i] > k) {x->keys[i+1] = x->keys[i];i --;}x->keys[i+1] = k;x->num += 1;} else {while (i >= 0 && x->keys[i] > k) i --;if (x->childrens[i+1]->num == (2*(T->t))-1) {btree_split_child(T, x, i+1);if (k > x->keys[i+1]) i++;}btree_insert_nonfull(T, x->childrens[i+1], k);}
}void btree_insert(btree *T, KEY_VALUE key) {//int t = T->t;btree_node *r = T->root;if (r->num == 2 * T->t - 1) {btree_node *node = btree_create_node(T->t, 0);T->root = node;node->childrens[0] = r;btree_split_child(T, node, 0);int i = 0;if (node->keys[0] < key) i++;btree_insert_nonfull(T, node->childrens[i], key);} else {btree_insert_nonfull(T, r, key);}
}void btree_traverse(btree_node *x) {int i = 0;for (i = 0;i < x->num;i ++) {if (x->leaf == 0) btree_traverse(x->childrens[i]);printf("%C ", x->keys[i]);}if (x->leaf == 0) btree_traverse(x->childrens[i]);
}void btree_print(btree *T, btree_node *node, int layer)
{btree_node* p = node;int i;if(p){printf("\nlayer = %d keynum = %d is_leaf = %d\n", layer, p->num, p->leaf);for(i = 0; i < node->num; i++)printf("%c ", p->keys[i]);printf("\n");
#if 0printf("%p\n", p);for(i = 0; i <= 2 * T->t; i++)printf("%p ", p->childrens[i]);printf("\n");
#endiflayer++;for(i = 0; i <= p->num; i++)if(p->childrens[i])btree_print(T, p->childrens[i], layer);}else printf("the tree is empty\n");
}int btree_bin_search(btree_node *node, int low, int high, KEY_VALUE key) {int mid;if (low > high || low < 0 || high < 0) {return -1;}while (low <= high) {mid = (low + high) / 2;if (key > node->keys[mid]) {low = mid + 1;} else {high = mid - 1;}}return low;
}//{child[idx], key[idx], child[idx+1]} 
void btree_merge(btree *T, btree_node *node, int idx) {btree_node *left = node->childrens[idx];btree_node *right = node->childrens[idx+1];int i = 0;/data mergeleft->keys[T->t-1] = node->keys[idx];for (i = 0;i < T->t-1;i ++) {left->keys[T->t+i] = right->keys[i];}if (!left->leaf) {for (i = 0;i < T->t;i ++) {left->childrens[T->t+i] = right->childrens[i];}}left->num += T->t;//destroy rightbtree_destroy_node(right);//node for (i = idx+1;i < node->num;i ++) {node->keys[i-1] = node->keys[i];node->childrens[i] = node->childrens[i+1];}node->childrens[i+1] = NULL;node->num -= 1;if (node->num == 0) {T->root = left;btree_destroy_node(node);}
}void btree_delete_key(btree *T, btree_node *node, KEY_VALUE key) {if (node == NULL) return ;int idx = 0, i;while (idx < node->num && key > node->keys[idx]) {idx ++;}if (idx < node->num && key == node->keys[idx]) {if (node->leaf) {for (i = idx;i < node->num-1;i ++) {node->keys[i] = node->keys[i+1];}node->keys[node->num - 1] = 0;node->num--;if (node->num == 0) { //rootfree(node);T->root = NULL;}return ;} else if (node->childrens[idx]->num >= T->t) {btree_node *left = node->childrens[idx];node->keys[idx] = left->keys[left->num - 1];btree_delete_key(T, left, left->keys[left->num - 1]);} else if (node->childrens[idx+1]->num >= T->t) {btree_node *right = node->childrens[idx+1];node->keys[idx] = right->keys[0];btree_delete_key(T, right, right->keys[0]);} else {btree_merge(T, node, idx);btree_delete_key(T, node->childrens[idx], key);}} else {btree_node *child = node->childrens[idx];if (child == NULL) {printf("Cannot del key = %d\n", key);return ;}if (child->num == T->t - 1) {btree_node *left = NULL;btree_node *right = NULL;if (idx - 1 >= 0)left = node->childrens[idx-1];if (idx + 1 <= node->num) right = node->childrens[idx+1];if ((left && left->num >= T->t) ||(right && right->num >= T->t)) {int richR = 0;if (right) richR = 1;if (left && right) richR = (right->num > left->num) ? 1 : 0;if (right && right->num >= T->t && richR) { //borrow from nextchild->keys[child->num] = node->keys[idx];child->childrens[child->num+1] = right->childrens[0];child->num ++;node->keys[idx] = right->keys[0];for (i = 0;i < right->num - 1;i ++) {right->keys[i] = right->keys[i+1];right->childrens[i] = right->childrens[i+1];}right->keys[right->num-1] = 0;right->childrens[right->num-1] = right->childrens[right->num];right->childrens[right->num] = NULL;right->num --;} else { //borrow from prevfor (i = child->num;i > 0;i --) {child->keys[i] = child->keys[i-1];child->childrens[i+1] = child->childrens[i];}child->childrens[1] = child->childrens[0];child->childrens[0] = left->childrens[left->num];child->keys[0] = node->keys[idx-1];child->num++;node->keys[idx-1] = left->keys[left->num-1];left->keys[left->num-1] = 0;left->childrens[left->num] = NULL;left->num --;}} else if ((!left || (left->num == T->t - 1))&& (!right || (right->num == T->t - 1))) {if (left && left->num == T->t - 1) {btree_merge(T, node, idx-1);					child = left;} else if (right && right->num == T->t - 1) {btree_merge(T, node, idx);}}}btree_delete_key(T, child, key);}}int btree_delete(btree *T, KEY_VALUE key) {if (!T->root) return -1;btree_delete_key(T, T->root, key);return 0;
}int main() {btree T = {0};btree_create(&T, 3);srand(48);int i = 0;char key[27] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";for (i = 0;i < 26;i ++) {//key[i] = rand() % 1000;printf("%c ", key[i]);btree_insert(&T, key[i]);}btree_print(&T, T.root, 0);for (i = 0;i < 26;i ++) {printf("\n---------------------------------\n");btree_delete(&T, key[25-i]);//btree_traverse(T.root);btree_print(&T, T.root, 0);}}

b树和b+树的区别

对于b树还有b+树的区别:

  • b树的每个节点都是可以存储数据的
  • b+树的只有叶子节点可以存储数据,内节点只能当索引用。

对于一个节点,b树比b+树所需的内存要大,所以相同的内存,b+树存的节点多与b树。

然后举个例子:假如有1000w个节点数据,那么如果用b树存储这些数据,那么数据会放在内存中,有的数据会放在磁盘中,那么如果我们用b+树进行存储的这些数据的话,我们是将索引关系放在内存中,数据放在磁盘中,然后查找的时候通过一次寻址(内存向磁盘寻址)就可以了。
所以b+树通常用来做磁盘索引,特别是那种大量的索引。所以向MySQL等都是b+树。

海量数据去重的Hash与BloomFilter

总体脉络

在这里插入图片描述
背景:

  • 使用 word 文档时,word 如何判断某个单词是否拼写正确?
  • 网络爬虫程序,怎么让它不去爬相同的 url 页面?
  • 垃圾邮件过滤算法如何设计?
  • 公安办案时,如何判断某嫌疑人是否在网逃名单中?
  • 缓存穿透问题如何解决?

主要解决的就是从海量数据中查询某个字符串是否存在

散列表

对于平衡二叉树增删改查时间复杂度为 O(nlog(n));平衡的目的是增删改后,保证下次搜索能稳定排除一半的数据;
直观理解:100万个节点,最多比较 20 次;10 亿个节点,最多比较 30 次;
总结:通过比较保证有序,通过每次排除一半的元素达到快速

平衡二叉树结构有序,提升搜索效率。而散列表则是key与节点存储位置的映射关系。

根据 key 计算 key 在表中的位置的数据结构;是 key 和其所在
存储地址的映射关系;
注意:散列表的节点中 kv 是存储在一起的;
hash 函数

struct node {void *key;void *val;struct node *next;
};

在这里插入图片描述

hash 函数

映射函数 Hash(key)=addr;hash 函数可能会把两个或两个以上的不同 key 映射到同一地址,这种情况称之为冲突(或者hash 碰撞);

选择hash

  • 计算速度快
  • 强随机分布(等概率、均匀地分布在整个地址空间)murmurhash1,murmurhash2(使用最多),murmurhash3,siphash( redis6.0 当中使用,rust 等大多数语言选用的 hash 算法来实
    现 hashmap)
    cityhash 都具备强随机分布性;测试地址如下:
    https://github.com/aappleby/smhasher
  • siphash 主要解决字符串接近的强随机分布性;

操作流程

hash 函数

struct node {void *key;void *val;struct node *next;
};

在这里插入图片描述
我们通常就是将一个key通过哈希函数得到一个整数,然后我们会将这个整数对数组的长度进行取余,然后他肯定会落在数组的某个槽位中。然后我们会在改槽位中增加一个节点。
然后随着我们插入的数增多,他肯定会出现多个数落在同一个槽位中,这就形成了hash冲突。上面是拉链发。

冲突

负载因子
数组存储元素的个数 / 数组长度;用来形容散列表的存储密度;负载因子越小,冲突概率越小,负载因子越大,冲突概率越大。

冲突处理

当负载因子在合理范围内的话:
链表法

引用链表来处理哈希冲突;也就是将冲突元素用链表链接起来;这也是常用的处理冲突的方式;但是可能出现一种极端情况,冲突元素比较多,该冲突链表过长,这个时候可以将这个链表转换为红黑树、最小堆(java hashmap);由原来链表时间复杂度 转换为红黑树时间复杂度 ;那么判断该链表过长的依据是多少?可以采用超过 256(经验值)个节点的时候将链表结构转换为红黑树或堆结构(java hashmap);

开放寻址法

将所有的元素都存放在哈希表的数组中,不使用额外的数据结构;一般使用线性探查的思路解决;

  1. 当插入新元素的时,使用哈希函数在哈希表中定位元素位置;
  2. 检查数组中该槽位索引是否存在元素。如果该槽位为空,则插入,否则3;
  3. 在 2 检测的槽位索引上加一定步长接着检查2; 加一定步长分为以下几种:
    i+1,i+2,i+3,i+4, … ,i+n(不好)
    i − 1 2 i- 1^2 i12, i + 1 2 i+ 1^2 i+12, i − 3 2 i- 3^2 i32, 1 + 4 2 1+4^2 1+42, … 这两种都会导致同类 hash 聚集;也就是近似值它的hash值也近似,那么它的数组槽位也靠近,形成 hash 聚集;第一种同类聚集冲突在前,第二种只是将聚集冲突延后; 另外还可以使用双重哈希来解决上面出现hash聚集现象:
在.net HashTable类的hash函数Hk定义如下:
Hk(key) = [GetHash(key) + k * (1 +(((GetHash(key) >> 5) + 1) %(hashsize – 1)))] % hashsize
在此 (1 + (((GetHash(key) >> 5) + 1) %
(hashsize – 1))) 与 hashsize互为素数(两数互为素数表示两者没有共同的质因⼦);
执⾏了 hashsize 次探查后,哈希表中的每⼀个位置都有且只有⼀次被访问到,
也就是说,对于给定的 key,对哈希表中的同⼀位置不会同时使⽤Hi 和 Hj;

当负载因子不在合理范围内的话:
扩容

used>size

缩容

used<0.1*size

rehash

stl中散列表的实现

对于map,set,mltimap,multiset都是用红黑树实现的。

而在 STL 中 unordered_map,unordered_set、unordered_multimap、unordered_multiset 四兄弟底层实
现都是散列表;

在这里插入图片描述
他主要的目的就是将后面的给串成一个单链表,这样方便我们的迭代器进行依次查找。实际上还是hash,为了实现迭代器,将后面具体节点串成一个单链表

布隆过滤器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/5808.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flutter 弃用 WillPopScope 使用 PopScope 替代方法

Flutter 弃用 WillPopScope 使用 PopScope 替代方法 视频 https://youtu.be/u3qdqUvFWiM https://www.bilibili.com/video/BV1aJ4m1n7FZ 前言 原文 https://ducafecat.com/blog/migrating-from-willpopscope-to-popscope-in-flutter 了解如何在 Flutter 3.16 中将弃用的 Wil…

【Mac】Mac安装软件常见问题解决办法

前言 刚开始用Mac系统的小伙伴或者在更新系统版本后运行App的朋友会经常碰到弹窗提示「xxx已损坏&#xff0c;无法打开&#xff0c;您应该将它移到废纸篓」、「打不开xxx&#xff0c;因为Apple无法检查其是否包含恶意软件」、「打不开xxx&#xff0c;因为它来自身份不明的开发…

SCI一区 | MFO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测(Matlab)

SCI一区 | MFO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测&#xff08;Matlab&#xff09; 目录 SCI一区 | MFO-CNN-LSTM-Mutilhead-Attention多变量时间序列预测&#xff08;Matlab&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现MFO-CNN…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-6.4--汇编LED驱动程序

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

自定义SpringBoot的starter

案例需求&#xff1a;自定义redis-stater。要求当导入redis坐标时&#xff0c;SpringBoot自动创建Jedis的Bean。 实现步骤&#xff1a; 1、创建redis-spring-boot-autoconfigure模块 2、创建redis-spring-boot-starter模块&#xff0c;依赖redis-spring-boot-autoconfigure的…

4G远程温湿度传感器在农业中的应用—福建蜂窝物联网科技有限公司

解决方案 农业四情监测预警解决方案 农业四情指的是田间的虫情、作物的苗情、气候的灾情和土壤墒情。“四情”监测预警系统的组成包括管式土壤墒情监测站、虫情测报灯、气象站、农情监测摄像机&#xff0c;可实时监测基地状况,可以提高监测的效率和准确性&#xff0c;为农业生…

【云原生】Docker 实践(一):在 Docker 中部署第一个应用

Docker 实践&#xff08;一&#xff09;&#xff1a;在 Docker 中部署第一个应用 1.使用 YUM 方式安装 Docker2.验证 Docker 环境3.在 Docker 中部署第一个应用3.1 小插曲&#xff1a;docker pull 报 missing signature key 错误3.2 重新安装 Nginx 1.使用 YUM 方式安装 Docker…

2024年教你怎么将学浪视频保存到本地

你是否曾为无法将学浪视频保存到本地而烦恼&#xff1f;现在&#xff0c;我们将在2024年教给你如何解决这个问题&#xff01;只需简单几步操作&#xff0c;即可轻松将学浪视频保存到您的本地设备&#xff0c;随时随地想看就看&#xff01; 我已经将下载学浪的工具打包好了&…

Vue+Element UI el-progress进度条内显示自定义数字及文字

需求 进度条内展示 具体的数字值&#xff0c;进度条外展示 百分比数值 数据 data() {return {reNum: 3214,rePer:40,warmPer: 40,warmNum:2132,}}因为样式要求&#xff0c;显示的百分数也是自己写的哈 &#xff0c;没有用进度条自带的 代码 <div class"pick"&g…

(八)Servlet教程——创建Web项目以及Servlet的实现

1. 打开Idea编辑器 2. 点击界面上的“新建项目”按钮 3. 设置好项目名称和位置 应用服务器选择之前设置好的Tomcat服务器 构建系统默认选择Maven 4. 点击“下一步”按钮 5. 点击“完成”按钮&#xff0c;Idea就创建好了项目&#xff0c;创建完成后的目录结构如下图所示 6. 此…

网络安全之弱口令与命令爆破(中篇)(技术进阶)

目录 一&#xff0c;什么是弱口令&#xff1f; 二&#xff0c;为什么会产生弱口令呢&#xff1f; 三&#xff0c;字典的生成 四&#xff0c;使用Burpsuite工具验证码爆破 总结 笔记改错 一&#xff0c;什么是弱口令&#xff1f; 弱口令就是容易被人们所能猜到的密码呗&a…

STM32标准库编译前置条件配置

本文基于stm32f104系列芯片&#xff0c;记录编程代码前需要的操作&#xff1a; 添加库文件 在ST官网下载标准库STM32F10x_StdPeriph_Lib_V3.5.0&#xff0c;解压后&#xff0c;得到以下界面 启动文件 进入Libraries&#xff0c;然后进入CMSIS&#xff0c;再进入CM3&#xff…

Debian 12 -bash: netstat: command not found 解决办法

问题表现&#xff1a; debian 12系统中&#xff0c;不能使用 netstat命令 处理办法&#xff1a; netstat 命令就的net-tools中&#xff0c;把net-tools工具安装上就好了。 apt-get install netstat 安装之后就可以使用netstat 命令了&#xff0c;如查询端口情况&#xff1a; …

什么是内存缓存 DDoS 攻击,改如何防护

DDOS 缓存服务器是世界上许多大型网站&#xff08;如 Facebook、Flickr、Twitter、Reddit、YouTube、Github&#xff09;使用的一项技术。主要作用是利用DDOS缓存技术处理的动态网页应用&#xff0c;可以减轻网站数据库的压力&#xff0c;当这些网站出现大规模连接请求时&#…

python学习之词云图片生成

代码实现 import jieba import wordcloudf open("D:/Pythonstudy/data/平凡的世界.txt", "r", encoding"utf-8") t f.read() print(t) f.close() ls jieba.lcut(t) txt " ".join(ls)w wordcloud.WordCloud(font_path"D:/cc…

如何利用有限的数据发表更多的SCI论文?——利用ArcGIS探究环境和生态因子对水体、土壤和大气污染物的影响

原文链接&#xff1a;如何利用有限的数据发表更多的SCI论文&#xff1f;——利用ArcGIS探究环境和生态因子对水体、土壤和大气污染物的影响https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247602528&idx6&snc89e862270fe54239aa4f796af07fb71&chksmfa82…

【前端探索者:从零到精通的Web前端实战专栏】

🚀 在这个代码编织梦想的时代,Web前端作为互联网的颜值担当,正以日新月异的速度重塑数字世界。想要在前端江湖里游刃有余,你需要的不仅仅是一把锋利的剑,更是一套完整的武功秘籍!今天,我们就为你揭开【Web前端】专栏的神秘面纱,带你从菜鸟到大神,一飞冲天! 📚 专栏…

基于Spring Boot的校园闲置物品交易网站设计与实现

基于Spring Boot的校园闲置物品交易网站设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 系统功能界面图&#xff0c;在系统首页可以查看…

ClickHouse高原理与实践

ClickHouse高原理与实践 1 ClickHouse的特性1.1. OLAP1.2. 列式存储1.3. 表引擎1.4. 向量化执行1.5. 分区1.6. 副本与分片1.7 其他特性 2. ClickHouse模块设计2.1 Parser分析器与Interpreter解释器2.2 Storage2.3 Column与Field2.4 DataType2.5 Block2.6 Cluster与Replication …

ROS2专栏(三) | 理解ROS2的动作

​ 1. 创建一个动作 目标&#xff1a; 在ROS 2软件包中定义一个动作。 1.1 新建包 设置一个 workspace 并创建一个名为 action_tutorials_interfaces 的包&#xff1a; mkdir -p ros2_ws/src #you can reuse existing workspace with this naming convention cd ros2_ws/s…