• 查找树与哈希方法学习


    查找算法评价指标 平均查找长度 查找成功或不成功的 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个字节。

  • 相关阅读:
    C#学习-类型转换
    《学习之道》第八章总结
    《学习之道》第八章有了待办清单,重要的是说做就做
    《学习之道》第八章学习方法27薪水
    《学习之道》第八章行动日志是第二双眼睛
    C#学习-接口与抽象类
    C#学习-显式接口
    《学习之道》第八章再次提到休息
    《学习之道》第八章执行日志的经验
    4..8矩阵的乘法运算
  • 原文地址:https://www.cnblogs.com/updateofsimon/p/3455359.html
Copyright © 2020-2023  润新知