二叉查找树

来源:互联网 发布:ddf24口6类网络配线架 编辑:程序博客网 时间:2024/06/05 00:13
#include <stdio.h>//二叉查找树//结合了链表插入的灵活性和有序数组查找的高效性,适用于经常插入\查找//的场合使用.//其中二叉查找树的特性为每个节点最多有两个子节点,其中左子节点小于当//前节点,右子节点大于当前节点//             6//           /   \//          3     8//         / \   / \//        2   4     11typedef struct _NODE{    int             key;    int             val;    struct _NODE   *left;     struct _NODE   *right;     int             n;} NODE;NODE *root = NULL;//计算当前节点为根的子树节点总数(包含当前节点)int size(NODE *pNode){    if ( pNode == NULL )    {        return 0;     }    return pNode->n;}NODE* newNode(int key, int val){    NODE *pNew = (NODE *)malloc(sizeof(NODE));       if ( pNew == NULL )    {        return NULL;     }    memset(pNew, 0, sizeof(NODE));    pNew->key = key;    pNew->val = val;    pNew->n = 1;    return pNew;}//查找//根据二叉树的特性//1 如果当前查找键小于当前节点,则当前查找节点切为左子节点,//  继续去左子节点查找//2 如果当前查找键大于当前节点,则当前查找节点切为右子节点,//  继续去右子节点查找//3 查找成功,返回键值//4 对应的子节点已经为空,则没有任何匹配项,当前返回-1来表示int _get(NODE *pNode, int key){    if ( pNode == NULL )    {        return -1;     }    if ( key < pNode->key )    {        return _get(pNode->left, key);     }    else if ( key > pNode->key )    {        return _get(pNode->right, key);     }    return pNode->val;}int get(int key){    return _get(root, key);}//插入//1 如果当前关键字小于当前节点,则继续向左子节点进行插入处理//2 如果当前关键字大于当前节点,则继续向右子节点进行插入处理//3 如果当前关键字等于当前节点,则表明当前已经存在,则进行键值替换//4 如果最后移到了树的最左端或最右端,已经没有节点,则在此位置建立//  新的节点项//5 在递归插入过程中,有可能新的KEY没有已存在项,所以新的KEY会//  使用newNode分配一个新的节点对象进行插入,此时该路径上的所有//  关联父节点的N子节点个数都要进行更新,这里采用了递归回退的方//  式进行实现,当插入完成后,递归调用一步步退回每个节点的父节点//  调用位置,则这里进行该路径上所有关联父节点的更新.NODE* _put(NODE* pNode, int key, int val){    if ( pNode == NULL )    {        return newNode(key, val);    }    if ( key < pNode->key )    {        pNode->left = _put(pNode->left, key, val);     }    else if ( key > pNode->key )    {        pNode->right = _put(pNode->right, key, val);     }    else    {        pNode->val = val;     }    pNode->n = size(pNode->left) + size(pNode->right) + 1;    return pNode;}void put(int key, int val){    root = _put(root, key, val);}//获取当前树的最小节点,根据二叉树的特性,则一直遍历//左子树,直到该节点没有左子节点,则该节点就是当前树//的最小节点NODE* _min(NODE* pNode){    if ( pNode->left == NULL )    {        return pNode;     }    return _min(pNode->left);}int min(){    if ( root == NULL )    {        return -1;     }    return _min(root)->key;}//获取最小的第index节点//这里借用了节点中的N成员变量进行实现,因为N成员变量表示//以当前节点为根的子树成员个数,其中也间接隐含了一种排名//的指示.//注意这里index索引值范围是从0开始,比如如下示例(括号中的值//即为当前节点的size),查找index = 0,则返回2,查找index = 1,//则返回4,查找index=2,则返回7.//       4(2)//     /     \//   2(1)   7(1)//   /  \   /  \//int _select(NODE *pNode, int index){    int leftSize = 0;       if (pNode == NULL)    {        return -1;     }    leftSize = size(pNode->left);    if ( leftSize > index )    {        return _select(pNode->left, index);     }    else if ( leftSize < index )    {        return _select(pNode->right, index - leftSize - 1);     }    else    {        return pNode->key;     }}/* index range is 0 ~ (size(root)-1) */int select(int index){    return _select(root, index);}//删除最小节点//即然是最小节点,也就表明待删除的节点是没有左子节点的,所以这里//删除的节点或者仅有一个右子节点,或者没有任何子节点.//这里实现思路就是递归找到最小节点,将其删除,同时将该节点的右子//树重新挂接到当前待删除节点的父节点上.比如当前示例,如果要删除//2,则2的右子树3重新挂接到2的父节点4的左子节点位置.//       4//     /   \//    2     7//   / \   / \//      3 5   9NODE* _deleteMin(NODE *pNode){    if ( pNode == NULL )    {        return NULL;     }    if ( pNode->left == NULL )    {        NODE *pRightNode = pNode->right;        printf("delete min key %d\r\n", pNode->key);        free(pNode);        return pRightNode;     }    pNode->left = _deleteMin(pNode->left);    pNode->n = size(pNode->left) + size(pNode->right) + 1;    return pNode;}void deleteMin(){    root = _deleteMin(root);}//节点删除//二叉树的删除操作相对有点复杂,主要区分两种情况//1 当前节点仅有一个子树或者没有子树,此时删除操作比较简单,//  如果该节点没有左子树,则该节点删除后,将该节点的右子树重//  新挂接到父节点上即可,如果该节点没有右子树,则该节点删除//  后,将该节点的左子树重新挂接到父节点即可.//2 当前节点如果含有两个子节点,此时处理就比较麻烦一些.//  如下示例所示,比如要删除7//  a 首先找到待删除节点右子树中最小值,当前示例中,7右子树最//    小值就是8//  b 之后将节点8与节点7进行替换,替换后7就跑到8的位置,在这个//    位置,由于之前8是最小值,所以原来位置一定是仅有一个子树//    或者没有任何子树//  c 此时8已经移到了原来7的位置,将8的左节点指向原来7的左节//    点5//  d 使用删除最小子节点的算法,针对当前8的新位置(即原来7所在的//    点),进行最小子节点删除,此时原节点7就在这个位置,所以节点//    7的删除处理由原来考虑两个节点同时存在情况,变成了仅有一//    个节点或没有任何节点的删除情况,删除算法见之前删除最小//    节点算法.//  e 最后将当前8的新位置重新嫁接到原来7的父节点4上.//       4//     /   \//    2     7//   / \   / \//      3 5   9//           / \//          8//         / \//           NODE* _delete(NODE *pNode, int key){    if ( pNode == NULL )    {        return NULL;     }    if ( key < pNode->key )    {        pNode->left = _delete(pNode->left, key);     }    else if ( key > pNode->key )    {        pNode->right = _delete(pNode->right, key);     }    else    {        if ( pNode->left == NULL || pNode->right == NULL )         {            NODE* retNode = ((pNode->left == NULL) ? pNode->right : pNode->left);            printf("delete key %d\r\n", pNode->key);            free(pNode);            return retNode;         }        else        {            NODE tmp = *pNode;             *pNode = *_min(tmp.right);            pNode->left = tmp.left;            pNode->right = _deleteMin(tmp.right);        }    }    pNode->n = size(pNode->left) + size(pNode->right) + 1;    return pNode;}void delete(int key){    root = _delete(root, key);}//中序遍历//中序遍历即按从小到大的递增顺序遍历,此时要先不断递归显示//当前节点的左子树,待左子树全部遍历完后,在显示当前节点,之后//再递归显示当前节点的右子树.void showKey(NODE* pNode){    if ( pNode == NULL )    {        return;    }    showKey(pNode->left);     printf("key %d\r\n", pNode->key);    showKey(pNode->right);}//获取满足指定范围的key集合//这里也采用了中序遍历//在lo~hi范围内,需要满足如下条件//1 左边界不能小于lo//2 右边界不能大于hi//3 左边界大于等于lo,同时右边界小于等于hivoid _getKeys(NODE* pNode, int lo, int hi){    if ( pNode == NULL )    {        return;     }    //当前节点如果大于lo,则其左子树(比当前节点小,即进行左边界    //向左延伸试探)可能还有满足左边界不能小于lo的希望    if ( pNode->key > lo )     {        _getKeys(pNode->left, lo, hi);     }    //当前满足条件3,则完全符合条件    if ( pNode->key >= lo && pNode->key <= hi )    {        printf("key %d in lo[%d] ~ hi[%d]\r\n", pNode->key, lo, hi);     }    //当前节点如果小于hi,则其右子树(比当前节点大,即进行右边界    //向右延伸试探)可能还有满足右边界不能大于hi的希望    if ( pNode->key < hi )    {        _getKeys(pNode->right, lo, hi);     }}void getKeys(int lo, int hi){    _getKeys(root, lo, hi);}int main(int argc, char *argv[]){    int i = 0;    int findKey = 0;    int getVal = 0;    int keys[] = {2,  4,  6,  1,  9,  4,  9,  8,  7,  2,  10};    int vals[] = {21, 41, 61, 11, 91, 42, 92, 81, 71, 22, 101};    if ( argc != 2 )    {        printf("Usage: %s <findKey>\r\n", argv[0]);        return 0;     }    for ( i = 0; i < sizeof(keys)/sizeof(keys[0]); i++)    {        put(keys[i], vals[i]);     }    findKey = atoi(argv[1]);    getVal = get(findKey);    printf("key %d, val %d\r\n", findKey, getVal);    printf("min key %d\r\n", min());    printf("select index 3, key is %d\r\n", select(3));    getKeys(3, 7);    deleteMin();    deleteMin();    delete(4);    delete(9);    showKey(root);    return 0;}

0 0
原创粉丝点击