查找树与哈希方法学习

查找算法评价指标 平均查找长度 查找成功或不成功的 ASL = Σ 概率*比较次数 等概率时与数组是否有序无关,不等概率与序列有关。

顺序查找:ASL=(n+1)/2

折半查找:构建一颗判定树,每个数的层数就是查找到需要的比较次数。比较n次可以找到2^(n-1)个数字。

树表:二叉排序树,充分必要条件是中序遍历是递增的。

散列表:Hash

1.二叉排序树

假设被删结点是*p,其双亲是*f,不失一般性,设*p是*f的左孩子,下面分三种情况讨论:

(1) 若结点*p是0度,则只需修改其父结点*f的指针即可

(2) 若结点*p是1度,则只要使PL或PR 成为其父结点的左子树

(3) 若结点*p是2度,先找到*p的中序后继结点*s(*p的右子树中的最左下的结点),以*s代替*p,执行(2)将*s的左子树链到*s的父结点*q的左(或右)链上。

#include <iostream>
#include <cstdio>
using namespace std;

typedef struct Node {
    int data;
    int flag;
    Node *lchild, *rchild;
} *BTree;

BTree Insert(BTree &T, int key) {
    BTree pre = T, p = T;
    while (p) {
        if (p->data == key)
            return T;
        pre = p;
        p = (key < p->data) ? p->lchild : p->rchild;
    }
    p = new Node;
    p->data = key;
    p->lchild = p->rchild = NULL;
    if (T == NULL)
        T = p;
    else if (key < pre->data)
        pre->lchild = p;
    else
        pre->rchild = p;
    return T;
}
void Del(BTree &T, int key) {
    BTree p = T, pre, s;
    while (p) {
        if (p->data == key)
            break;
        pre = p;
        p = (key < p->data) ? p->lchild : p->rchild;
    }
    if (!p)
        return;
    if (p->lchild == NULL && p->rchild == NULL) {
        if (p == T)
            T = NULL; //删除的是根节点
        else if (p == pre->lchild)
            pre->lchild = NULL;
        else
            pre->rchild = NULL;
        free(p);
    } else if (p->lchild == NULL && p->rchild != NULL) {
        if (pre->lchild == p)
            pre->lchild = p->rchild;
        else
            pre->rchild = p->rchild;
        free(p);
    } else if (p->rchild == NULL && p->lchild != NULL) {
        if (pre->lchild == p)
            pre->lchild = p->lchild;
        else
            pre->rchild = p->lchild;
        free(p);
    } else if (p->lchild != NULL && p->rchild != NULL) {
        pre = p;
        s = p->rchild;
        while (s->lchild) { //中序前驱
            pre = s;
            s = s->lchild;
        }
        p->data = s->data; //以p的中序前趋结点s代替p(即把s的数据复制到p中)
        if (pre != p)
            pre->lchild = s->rchild; //重接q的右子树
        else
            p->rchild = s->rchild; //重接q的左子树。
        free(s);
    }
}
void Debug(BTree &T) {
    if (T) {
        Debug(T->lchild);
        printf("%d ", T->data);
        Debug(T->rchild);
    }
}
int main() {
    BTree T;
    for (int i = 0; i < 10; i++)
        Insert(T, rand() % 100);
    Debug(T);
    puts("");
    Del(T, 67);
    Debug(T);
    return 0;
}

2.平衡二叉树(AVL树)

定义:平衡二叉树或为空树,或为如下性质的二叉排序树:

  1)左右子树深度之差的绝对值不超过1;

  2)左右子树仍然为平衡二叉树.

平衡因子BF=左子树深度-右子树深度.

平衡二叉树每个结点的平衡因子只能是10-1。若其绝对值超过1,则该二叉排序树就是不平衡的。

结构体定义如下

#define LH 1   //左高
#define EH 0    //等高
#define RH -1   //右高
typedef struct Node {
    int data;
    int bf;    //结点的平衡因子
    Node *LChild, *RChild;   //左、右孩子指针
}*AVL;

一、如何保持平衡

若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性。首先要找出插入新结点后失去平衡的最小子树根结点的指针。然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树。当失去平衡的最小子树被调整为平衡子树后,原有其他所有不平衡子树无需调整,整个二叉排序树就又成为一棵平衡二叉树。

失去平衡的最小子树是指以离插入结点最近,且平衡因子绝对值大于1的结点作为根的子树。假设用A表示失去平衡的最小子树的根结点,则调整该子树的操作可归纳为下列四种情况。

 1L型旋转

由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行一次顺时针旋转操作。 即将A的左孩子B右上旋转代替A作为根结点,A右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。

void L_Rotate(AVL &p) {
    Node *rc = p->RChild;
    p->RChild = rc->LChild;
    rc->LChild = p;
    p->bf = rc->bf = EH;
    p = rc;
}

2R型旋转

由于在A的右孩子的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行一次逆时针旋转操作。即将A的右孩子C左上旋转代替A作为根结点,A左下旋转成为C的左子树的根结点。而原来C的左子树则变成A的右子树。

void R_Rotate(AVL &p) {
    Node *lc = p->LChild;
    p->LChild = lc->RChild;
    lc->RChild = p;
    p->bf = lc->bf = EH;
    p = lc;
}

3)LL与LR型平衡旋转

由于在A的左孩子B的右子数上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将A结点的左孩子B的右子树的根结点D左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。即先使之成为LL型,再按LL型处理

如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为LL型,再按LL型处理成平衡型。

void LeftBalance(AVL &T) { //对以指针T所指结点为根的二叉树作左平衡旋转处理,指针T指向新的根结点
    AVL lc, rd;
    lc = T->LChild;
    switch (lc->bf) {    //判断*T的左子树的平衡因子
    case LH:   //LL,只需要一次R旋转
        T->bf = lc->bf = EH;
        R_Rotate(T);
        break;
    case RH:    //LR,新结点插入在*T的左孩子的右子树上,需要对左子树L旋转,和对T作R旋转
        rd = lc->RChild;    //rd指向*T的左孩子的右子树的根
        switch (rd->bf) {    //修改*T及其左孩子的平衡因子
        case LH:
            T->bf = RH;
            lc->bf = EH;
            break;
        case EH:
            T->bf = lc->bf = EH;
            break;
        case RH:
            T->bf = EH;
            lc->bf = LH;
            break;
        }
        L_Rotate(T->LChild);   //对*T的左子树作左旋平衡处理
        R_Rotate(T);           //对*T作右旋平衡处理
        rd->bf = EH; //是新的根节点,保证平衡
    }
}

4)RR与RL型平衡旋转

由于在A的右孩子C的左子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行两次旋转操作(先顺时针,后逆时针),即先将A结点的右孩子C的左子树的根结点D右上旋转提升到C结点的位置,然后再把该D结点向左上旋转提升到A结点的位置。即先使之成为RR型,再按RR型处理。

如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为RR型,再按RR型处理成平衡型。

void RightBalance(AVL &T) {
    AVL ld, rc;
    rc = T->RChild;
    switch (rc->bf) {
    case RH: //RR,只需要一次L旋转
        T->bf = rc->bf = EH;
        L_Rotate(T);
        break;
    case LH: //RL,需要对右子树R旋转,和对T作L旋转
        ld = rc->LChild;
        switch (ld->bf) {
        case LH:
            T->bf = EH;
            rc->bf = LH;
            break;
        case EH:
            T->bf = rc->bf = EH;
            break;
        case RH:
            T->bf = RH;
            rc->bf = EH;
            break;
        }
        ld->bf = EH;
        R_Rotate(T->RChild);
        L_Rotate(T);
        break;
    }
}

二、如何判断插入后对哪个点平衡操作

采用taller判断是否需要继续进行平衡操作。如果平衡了就设taller为0,如果插入点taller = 1,或向上更新时出现了潜在的不平衡情况taller = 1。平衡操作后要置taller = 0。

bool Insert(AVL &T, int key, bool &taller) { //若在平衡的二叉树T中不存在和e有相同关键字的结点,则插入一个数据元素
    //为e的新结点,并返回1,否则返回0,旋转处理,布尔变量taller反映是否需要继续向上检验平衡因子
    if (!T) { //插入新结点,树“长高”,置taller为true
        T = (AVL) malloc(sizeof(Node));
        T->data = key;
        T->LChild = T->RChild = NULL;
        T->bf = EH;
        taller = 1;
        return 1;
    }
    if (key == T->data) { //树中已存在和e有相同关键字的结点
        taller = 0;
        return 0;
    }
    if (key < T->data) { //在T的左子树中搜索
        if (!Insert(T->LChild, key, taller))   //未插入返回
            return 0;
        if (taller)      //已插入到T的左子树中且左子树“长高”
            switch (T->bf) {     //检查T的平衡度
            case LH:          //原本左子树比右子树高,需要作左平衡处理
                LeftBalance(T);
                taller = 0;
                break;
            case EH:         //原本左、右子树等高,需要作左平衡处理
                T->bf = LH;
                taller = 1;
                break;
            case RH:        //原本右子树比左子树高
                T->bf = EH;
                taller = 0;
                break;
            }
    } else {  //在T的右子树中搜索
        if (!Insert(T->RChild, key, taller))   //未插入
            return 0;
        if (taller)   //已插入到T右子树且右子树长高
            switch (T->bf) {      //检查T的平衡度
            case LH:
                T->bf = EH;
                taller = 0;
                break;
            case EH:
                T->bf = RH;
                taller = 1;
                break;
            case RH:
                RightBalance(T);
                taller = 0;
                break;
            }
    }
    return 1;
}

三、在AVL树中删除元素

bool Delete(AVL &T, int key, bool &shorter) {
    if (!T) {
        shorter = 0;
        return 0;
    }
    if (key == T->data) { //找到了需要删除的结点
        //如果该结点的lchild rchild 至少有一个为NULL 则大功告成
        AVL q = T;
        if (!T->LChild) {
            T = T->RChild;
            free(q);
            shorter = 1;
            return 1;
        } else if (!T->RChild) {
            T = T->LChild;
            free(q);
            shorter = 1;
            return 1;
        } else {
            AVL s = T->LChild;
            while (s->RChild)
                s = s->RChild;
            T->data = s->data;
            key = s->data;
        }
    }
    if (key <= T->data) {
        if (!Delete(T->LChild, key, shorter))
            return 0;
        if (shorter) {
            switch (T->bf) {
            case LH: //T平衡因子EH,深度减少,需向上更新shorter = 1
                T->bf = EH;
                shorter = 1;
                break;
            case EH:
                T->bf = RH;
                shorter = 0;
                break;
            case RH:
                RightBalance(T);
                if (T->RChild->bf == EH)
                    shorter = 0;
                else
                    shorter = 1;
                break;
            }
        }
    } else {
        if (!Delete(T->RChild, key, shorter))
            return 0;
        if (shorter) {
            switch (T->bf) {
            case LH:
                LeftBalance(T);
                if (T->LChild->bf == EH)
                    shorter = 0;
                else
                    shorter = 1;
                break;
            case EH:
                T->bf = LH;
                shorter = 0;
                break;
            case RH:
                T->bf = EH;
                shorter = 1;
                break;
            }
        }
    }
    return 1;
}

四、作为排序树,中序遍历AVL树就得到排序

void output(AVL T) {
    if (T) {
        output(T->LChild);
        printf("%d ", T->data);
        output(T->RChild);
    }
}
int main() {
    vector<int> map;
    map.clear();
    bool flag;
    int key;
    AVL Root = NULL;
    for (int i = 0; i < 10; i++) {
        key = rand() % 1000;
        map.push_back(key);
        Insert(Root, key, flag);
    }
    output(Root);
    puts("");
    for (int i = 0; i < 8; i++) {
        Delete(Root, map[i], flag);
        output(Root);
        puts("");
    }
    return 0;
}

3.红黑树

红黑树,一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。
通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

红黑树的5个性质:

1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点,即空结点(NIL)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。

一,插入

在对红黑树进行插入操作时,我们一般总是插入红色的结点,因为插入一个红色结点只会破坏性质2或性质4。
那么,我们插入一个结点后,可能会使原树的哪些性质改变列?
由于,我们是按照二叉树的方式进行插入,因此元素的搜索性质不会改变。

如果插入的结点是根结点,性质2会被破坏,如果插入结点的父结点是红色,则会破坏性质4。

我们的回复策略很简单,
其一、把出现违背红黑树性质的结点向上移,如果能移到根结点,那么很容易就能通过直接修改根结点来恢复红黑树的性质。直接通过修改根结点来恢复红黑树应满足的性质。
其二、穷举所有的可能性,之后把能归于同一类方法处理的归为同一类,不能直接处理的化归到下面的几种情况,

情况1:插入的是根结点
原树是空树,此情况只会违反性质2。
  对策:直接把此结点涂为黑色。
情况2:插入的结点的父结点是黑色
此不会违反性质2和性质4,红黑树没有被破坏。
  对策:什么也不做。
情况3:当前结点的父结点是红色且祖父结点的另一个子结点(叔叔结点)是红色
与此同时,又分为父结点是祖父结点的左子还是右子,对于对称性,我们只要解开一个方向就可以了。我们先考虑父结点为祖父左子的情况。

父节点为红色性质4被破坏。

  对策:将当前节点的父节点和叔叔节点涂黑,祖父结点涂红,把当前结点指向祖父节点,从新的当前节点重新开始算法。

如插入节点4:                  变为:

 

情况4:当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的右子

父节点为红色性质4被破坏。

对策:当前节点的父节点做为新的当前节点,以新当前节点为支点左旋

如插入节点7:                     变为:

  

情况5:当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的左子

解法:父节点变为黑色,祖父节点变为红色,在祖父节点为支点右旋

如插入节点7:                     变为:

   

#include <iostream>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;

#define RED 0
#define BLACK 1

struct RB_Node {
    bool COLOR;
    RB_Node* right;
    RB_Node* left;
    RB_Node* parent;
    int key;
    RB_Node() {
        right = NULL;
        left = NULL;
        parent = NULL;
    }
};
RB_Node *m_nullNode;
RB_Node *m_root;

void init() {
    m_nullNode = new RB_Node();
    m_root = m_nullNode;
    m_nullNode->right = m_root;
    m_nullNode->left = m_root;
    m_nullNode->parent = m_root;
    m_nullNode->COLOR = BLACK;
}

bool Empty() {
    if (m_root == m_nullNode)
        return 1;
    return 0;
}

//左旋代码实现
void RotateLeft(RB_Node* node) {
    if (node == m_nullNode || node->right == m_nullNode) {
        return;   //can't rotate
    }
    RB_Node* lower_right = node->right;
    lower_right->parent = node->parent;
    node->right = lower_right->left;
    if (lower_right->left != m_nullNode) {
        lower_right->left->parent = node;
    }
    if (node->parent == m_nullNode) //rotate node is root
            {
        m_root = lower_right;
        m_nullNode->left = m_root;
        m_nullNode->right = m_root;
        //m_nullNode->parent = m_root;
    } else {
        if (node == node->parent->left) {
            node->parent->left = lower_right;
        } else {
            node->parent->right = lower_right;
        }
    }
    node->parent = lower_right;
    lower_right->left = node;
}

//右旋代码实现
void RotateRight(RB_Node* node) {
    if (node == m_nullNode || node->left == m_nullNode) {
        return; //can't rotate
    }
    RB_Node* lower_left = node->left;
    node->left = lower_left->right;
    lower_left->parent = node->parent;
    if (lower_left->right != m_nullNode) {
        lower_left->right->parent = node;
    }
    if (node->parent == m_nullNode) //node is root
            {
        m_root = lower_left;
        m_nullNode->left = m_root;
        m_nullNode->right = m_root;
    } else {
        if (node == node->parent->right) {
            node->parent->right = lower_left;
        } else {
            node->parent->left = lower_left;
        }
    }
    node->parent = lower_left;
    lower_left->right = node;
}
inline RB_Node* InOrderPredecessor(RB_Node* node) {
    if (node == m_nullNode)       //null node has no predecessor
            {
        return m_nullNode;
    }
    RB_Node* result = node->left;     //get node's left child
    while (result != m_nullNode) //try to find node's left subtree's right most node
    {
        if (result->right != m_nullNode) {
            result = result->right;
        } else {
            break;
        }
    }      //after while loop result==null or result's right child is null
    if (result == m_nullNode) {
        RB_Node* index = node->parent;
        result = node;
        while (index != m_nullNode && result == index->left) {
            result = index;
            index = index->parent;
        }
        result = index;         // first right parent or null
    }
    return result;
}

inline RB_Node* InOrderSuccessor(RB_Node* node) {
    if (node == m_nullNode)       //null node has no successor
            {
        return m_nullNode;
    }
    RB_Node* result = node->right;   //get node's right node
    while (result != m_nullNode) //try to find node's right subtree's left most node
    {
        if (result->left != m_nullNode) {
            result = result->left;
        } else {
            break;
        }
    }       //after while loop result==null or result's left child is null
    if (result == m_nullNode) {
        RB_Node* index = node->parent;
        result = node;
        while (index != m_nullNode && result == index->right) {
            result = index;
            index = index->parent;
        }
        result = index;         //first parent's left or null
    }
    return result;
}
//查找key
RB_Node* find(int key) {
    RB_Node* index = m_root;
    while (index != m_nullNode) {
        if (key < index->key) {
            index = index->left;
        } else if (key > index->key) {
            index = index->right;
        } else {
            break;
        }
    }
    return index;
}
void InsertFixUp(RB_Node*);
void DeleteFixUp(RB_Node*);
//因为将z着为红色,可能会违反某一红黑性质
//所以需要调用下面的RB-INSERT-FIXUP(T, z)来保持红黑性质。
bool Insert(int key) {
    RB_Node* insert_point = m_nullNode;
    RB_Node* index = m_root;
    while (index != m_nullNode) {
        insert_point = index;
        if (key < index->key) {
            index = index->left;
        } else if (key > index->key) {
            index = index->right;
        } else {
            return 0;
        }
    }
    RB_Node* insert_node = new RB_Node();
    insert_node->key = key;
    insert_node->COLOR = RED;
    insert_node->right = m_nullNode;
    insert_node->left = m_nullNode;
    if (insert_point == m_nullNode) //如果插入的是一颗空树
            {
        m_root = insert_node;
        m_root->parent = m_nullNode;
        m_nullNode->left = m_root;
        m_nullNode->right = m_root;
        m_nullNode->parent = m_root;
    } else {
        if (key < insert_point->key) {
            insert_point->left = insert_node;
        } else {
            insert_point->right = insert_node;
        }
        insert_node->parent = insert_point;
    }
    InsertFixUp(insert_node);    //调用InsertFixUp修复红黑树性质。
    return 1;
}

void InsertFixUp(RB_Node* node) {
    while (node->parent->COLOR == RED) {
        if (node->parent == node->parent->parent->left) {
            RB_Node* uncle = node->parent->parent->right;
            if (uncle->COLOR == RED)   //插入情况1,z的叔叔y是红色的。
            {
                node->parent->COLOR = BLACK;
                uncle->COLOR = BLACK;
                node->parent->parent->COLOR = RED;
                node = node->parent->parent;
            } else if (uncle->COLOR == BLACK)  //插入情况2:z的叔叔y是黑色的,。
            {
                if (node == node->parent->right) //且z是右孩子
                        {
                    node = node->parent;
                    RotateLeft(node);
                } else                 //插入情况3:z的叔叔y是黑色的,但z是左孩子。
                {
                    node->parent->COLOR = BLACK;
                    node->parent->parent->COLOR = RED;
                    RotateRight(node->parent->parent);
                }
            }
        } else //这部分是针对为插入情况1中,z的父亲现在作为祖父的右孩子
        {
            RB_Node* uncle = node->parent->parent->left;
            if (uncle->COLOR == RED) {
                node->parent->COLOR = BLACK;
                uncle->COLOR = BLACK;
                uncle->parent->COLOR = RED;
                node = node->parent->parent;
            } else if (uncle->COLOR == BLACK) {
                if (node == node->parent->left) {
                    node = node->parent;
                    RotateRight(node);     //与上述代码相比,左旋改为右旋
                } else {
                    node->parent->COLOR = BLACK;
                    node->parent->parent->COLOR = RED;
                    RotateLeft(node->parent->parent);   //右旋改为左旋
                }
            }
        }
    }
    m_root->COLOR = BLACK;
}

bool Delete(int key) {
    RB_Node* delete_point = find(key);
    if (delete_point == m_nullNode) {
        return false;
    }
    if (delete_point->left != m_nullNode && delete_point->right != m_nullNode) {
        RB_Node* successor = InOrderSuccessor(delete_point);
        delete_point->key = successor->key;
        delete_point = successor;
    }
    RB_Node* delete_point_child;
    if (delete_point->right != m_nullNode) {
        delete_point_child = delete_point->right;
    } else if (delete_point->left != m_nullNode) {
        delete_point_child = delete_point->left;
    } else {
        delete_point_child = m_nullNode;
    }
    delete_point_child->parent = delete_point->parent;
    if (delete_point->parent == m_nullNode)            //delete root node
            {
        m_root = delete_point_child;
        m_nullNode->parent = m_root;
        m_nullNode->left = m_root;
        m_nullNode->right = m_root;
    } else if (delete_point == delete_point->parent->right) {
        delete_point->parent->right = delete_point_child;
    } else {
        delete_point->parent->left = delete_point_child;
    }
    if (delete_point->COLOR == BLACK
            && !(delete_point_child == m_nullNode
                    && delete_point_child->parent == m_nullNode)) {
        DeleteFixUp(delete_point_child);
    }
    delete delete_point;
    return true;
}

void DeleteFixUp(RB_Node* node) {
    while (node != m_root && node->COLOR == BLACK) {
        if (node == node->parent->left) {
            RB_Node* brother = node->parent->right;
            if (brother->COLOR == RED)   //情况1:x的兄弟w是红色的。
            {
                brother->COLOR = BLACK;
                node->parent->COLOR = RED;
                RotateLeft(node->parent);
            } else     //情况2:x的兄弟w是黑色的,
            {
                if (brother->left->COLOR == BLACK
                        && brother->right->COLOR == BLACK)
                //且w的俩个孩子都是黑色的。
                {
                    brother->COLOR = RED;
                    node = node->parent;
                } else if (brother->right->COLOR == BLACK)
                //情况3:x的兄弟w是黑色的,w的右孩子是黑色(w的左孩子是红色)。
                {
                    brother->COLOR = RED;
                    brother->left->COLOR = BLACK;
                    RotateRight(brother);
                } else if (brother->right->COLOR == RED)
                //情况4:x的兄弟w是黑色的,且w的右孩子时红色的。
                {
                    brother->COLOR = node->parent->COLOR;
                    node->parent->COLOR = BLACK;
                    brother->right->COLOR = BLACK;
                    RotateLeft(node->parent);
                    node = m_root;
                }
            }
        } else  //下述情况针对上面的情况1中,node作为右孩子而阐述的。
                //同样,原理一致,只是遇到左旋改为右旋,遇到右旋改为左旋,即可
        {
            RB_Node* brother = node->parent->left;
            if (brother->COLOR == RED) {
                brother->COLOR = BLACK;
                node->parent->COLOR = RED;
                RotateRight(node->parent);
            } else {
                if (brother->left->COLOR == BLACK
                        && brother->right->COLOR == BLACK) {
                    brother->COLOR = RED;
                    node = node->parent;
                } else if (brother->left->COLOR == BLACK) {
                    brother->COLOR = RED;
                    brother->right->COLOR = BLACK;
                    RotateLeft(brother);
                } else if (brother->left->COLOR == RED) {
                    brother->COLOR = node->parent->COLOR;
                    node->parent->COLOR = BLACK;
                    brother->left->COLOR = BLACK;
                    RotateRight(node->parent);
                    node = m_root;
                }
            }
        }
    }
    m_nullNode->parent = m_root;   //最后将node置为根结点
    node->COLOR = BLACK;   //并改为黑色。
}

void InOrderTraverse(RB_Node* node) {
    if (node == m_nullNode) {
        return;
    } else {
        InOrderTraverse(node->left);
        cout << node->key << "  ";
        InOrderTraverse(node->right);
    }
}

int main() {
    init();
    vector<int> key;
    for (int i = 0; i < 10; ++i) {
        int t = rand() % 100;
        key.push_back(t);
        Insert(t);
        cout << "insert:" << t << endl;   //添加结点
    }
    for (size_t i = 0; i < key.size(); i++) {
        cout << "Delete:" << key[i] << "  : ";
        Delete(key[i]);
        InOrderTraverse(m_root);
        puts("");
    }
    return 0;
}

Hash表

衡量标准:hash函数,处理冲突方法,装填因子α(hash表密度)

hash函数构造方法

1.直接定址法
2.数字分析法
3.平方取中法
4.折叠法
5.除留余数法
6.随机数法 

1.开放地址法:寻找下一个空的hash地址解决冲突

  线性探测法:顺次找到下一个空地址(循环队列)发生“聚集”。

  二次探测法:Hi = ( Hash(key) + di ) mod m di为增量序列12-1222-22q2,找到新的地址

  伪随机探测法:di为伪随机数字,也是有固定顺序的。

 查找时,从定位到的地址开始直到找到地址内容为空,说明不存在查找数据元素。

2.链地址法

同一hash地址的连接成一个链表,这样完全解决了聚集问题和hash表大小问题。

#define H 21997
int next[H], head[H];
void insert(int i) {
    int key = (p[i].x*p[i].x+p[i].y*p[i].y)%H;
    next[i] = head[key];
    p[i].x = p[i].x;
    p[i].y = p[i].y;
    head[key] = i;
}
int find(int x,int y) {
    int key=(x*x+y*y)%H;
    for(int i = head[key];i != -1;i = next[i])
        if(p[i].x == x && p[i].y == y)
            return i;
    return -1;
}

在实现爬虫的时候,我们使用Hash结构去存储我们用过的URL的时候,有些URL可能长度很长,为了更加节省空间,我们就要对URL进行压缩,帮它减减肥,这个我们介绍这个MD5算法,可以对URL进行有效的压缩。

  下面我们会讲讲MD5算法的一些细节,不过我们大可不必在意,我们只需要知道,我们对一个很长很长的字符串进行MD5压缩,返回的是一个128位整数,这个字符串就是原字符串的唯一标示符,就好像是我们的身份证一样,通过这个id就可以标识唯一的你。

  MD5算法描述:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过一系列的处理后,算法的输出有4个32位分组组成,将这4个32位分组级联后将生成一个128位的散列值。在MD5算法中,首先需要对信息进行填充,使其位长度对512求余的结果等于448,因此,信息的位长度将被扩展为N*512+448,在信息的后面附加一个1和无数个0,知道满足上面的条件才停止用0对信息进行填充,然后在这个结果的后面附加一个64位二进制表示的填充前的信息长度,通过这几步的处理,现在的信息字节长度=N*512+448+64=(N+1)*512,即长度恰好是512的整数倍。

 在Java中,java.security.MessageDigest中定义了MD5的计算,只需要简单地调用即可得到MD5的128位整数,然后将此128位转换成十六进制表示即可。通过这样的转换就可以将很长的字符串变成长度为32的字符串。

  先来看看MessageDigest这个类:

    介绍:此 MessageDigest 类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。信息摘要是安全的单向哈希函数,它接收任意大小的数据,并输出固定长度的哈希值。

    getInstance(String algorithm):这个该类的静态方法,返回具有指定算法名称的信息摘要。

    update(byte[] input):使用指定的byte数组去更新摘要,这个byte数组其实就是我们要压缩的那个字符串的数组。

    digest():通过执行诸如填充之类的最终操作完成哈希计算,其实就是根据MD5算法计算出一个128位的长整数,也即使16个字节。

原文地址:https://www.cnblogs.com/updateofsimon/p/3455359.html