王道数据结构源码实战ch7查找

二叉排序(搜索)树BST

节点结构体定义

  • 和普通二叉树完全一样
typedef struct BSTNode
{
    KeyType key;
    struct BSTNode *lchild,*rchild;
} BSTNode,*BiTree;

插入节点(递归)

  • 递归出口:当前的节点T==NULL。待插入节点的值小于当前节点的值就向坐子树递归,否则向右子树递归,这里不考虑相同值的情况。
int BST_Insert(BiTree &T,KeyType x)   //在创建中使用的单个元素插入功能
{
    if(T==NULL)    //走到合适的位置插入
    {
        T=(BiTree)malloc(sizeof(BSTNode)) ; //为新节点申请空间,第一个节点作为树根
        T->key=x;
        T->lchild=NULL;
        T->rchild=NULL;
        return 1;  //根节点插入成功
    }

    else if(x==T->key)//相同元素不考虑
        return 0;
    else if(x<T->key)//小于当前节点的值,递归一路向左
        return BST_Insert(T->lchild,x);  //函数调用结束后,左儿子和父亲节点关联起来
    else if(x>T->key)//大于当前节点的值,递归一路向右
        return BST_Insert(T->rchild,x);
}

创建二叉排序树

void Creat_BST(BiTree &T,KeyType str[],int n)  //创建二叉树
{
    T=NULL;
    for(int i=0; i<n; i++)
        BST_Insert(T,str[i]);
}

搜索有没有值为key的元素(非递归)

  • 不仅要找到值为key的节点,还要找到这个节点的父亲,通过引用传出来
BSTNode* BST_Search(BiTree T,KeyType key,BSTNode*  &p)  //查找有没有值为key的元素,有的话并把父亲也找到
{
    p=NULL;   //工作指针,存储要找的节点的父亲
    while(T!=NULL&&key!=T->key)  //当前节点非空,且该节点是数据不是key
    {
        p=T;      //不断更新为当前节点,找到的情况,记录key的父节点;没找到记录最后访问的叶子节点
        if(key<T->key)
            T=T->lchild;
        else
            T=T->rchild;
    }
    return T;  //没找到的情况,T==NULL
}

删除节点(书上没有)

  1. 根节点不为空,通过递归不断寻找待删除的节点
  2. 找到待删除节点后,分三种情况
    i:左子树为空,直接拿右子树接替删除的节点
    ii:右子树为空,直接拿左子树接替删除的节点
    iii:左右子树都不为空,左子树的最大节点(左子树一路向右)接替删除的节点,因为被删除的节点本来一定大于左子树所有节点,现在左子树的最大节点(包含根在内的第二大节点)上移,也能保证它大于左子树的剩余节点,且小于右子树所有节点。对于下面的子树,递归的向上补位。
    同理,用右子树的最小节点来代替也是可以的。
void DeleteNode(BiTree &root,KeyType x)  //树根也可能被删除,所以需要引用
{
    if(root==NULL)
        return;
    if(x<root->key)
        DeleteNode(root->lchild,x);
    else if(x>root->key)
        DeleteNode(root->rchild,x);    //不断递归寻找待删除节点的过程

    else //找到了待删除节点
    {
        if(root->lchild==NULL)  //左子树为空
        {
            BiTree tempNode = root ; //临时存储是为了free ,和链表删除一样
            root=root->rchild;
            free(tempNode);
        }

        else if(root->rchild==NULL)  //右子树为空
        {
            BiTree tempNode = root ;
            root=root->lchild;
            free(tempNode);
        }

        else  //左右子树都不为空时
            //左子树的最大节点(左子树一路向右),或右子树的最小节点(右子树一路向左)来代替
        {
            BiTree tempNode = root->lchild ;
            if(tempNode->rchild!=NULL) //一路向右,直到取得NULL,那么它的父亲就是我们找的
                tempNode=tempNode->rchild;

            root->key= tempNode->key;    //直接用左子树的最大节点的数据填充删除节点
            DeleteNode(root->lchild,tempNode->key);  //递归地删除每一个向上面补充的节点
        }
    }
}

完整代码

#include<bits/stdc++.h>
using namespace std;

typedef  int KeyType;
typedef struct BSTNode
{
    KeyType key;
    struct BSTNode *lchild,*rchild;
} BSTNode,*BiTree;

int BST_Insert(BiTree &T,KeyType x)   //在创建中使用的单个元素插入功能
{
    if(T==NULL)    //走到合适的位置插入
    {
        T=(BiTree)malloc(sizeof(BSTNode)) ; //为新节点申请空间,第一个节点作为树根
        T->key=x;
        T->lchild=NULL;
        T->rchild=NULL;
        return 1;  //根节点插入成功
    }

    else if(x==T->key)//相同元素不考虑
        return 0;
    else if(x<T->key)//小于当前节点的值,递归一路向左
        return BST_Insert(T->lchild,x);  //函数调用结束后,左儿子和父亲节点关联起来
    else if(x>T->key)//大于当前节点的值,递归一路向右
        return BST_Insert(T->rchild,x);
}


void Creat_BST(BiTree &T,KeyType str[],int n)  //创建二叉树
{
    T=NULL;
    for(int i=0; i<n; i++)
        BST_Insert(T,str[i]);
}

BSTNode* BST_Search(BiTree T,KeyType key,BSTNode*  &p)  //查找有没有值为key的元素,有的话并把父亲也找到
{
    p=NULL;   //工作指针,存储要找的节点的父亲
    while(T!=NULL&&key!=T->key)  //当前节点非空,且该节点是数据不是key
    {
        p=T;      //不断更新为当前节点,找到的情况,记录key的父节点;没找到记录最后访问的叶子节点
        if(key<T->key)
            T=T->lchild;
        else
            T=T->rchild;
    }
    return T;  //没找到的情况,T==NULL
}

void InOrder(BiTree T)
{
    if(T!=NULL)
    {
        InOrder(T->lchild);
        cout<<T->key<<" ";
        InOrder(T->rchild);
    }
}

void DeleteNode(BiTree &root,KeyType x)  //树根也可能被删除,所以需要引用
{
    if(root==NULL)
        return;
    if(x<root->key)
        DeleteNode(root->lchild,x);
    else if(x>root->key)
        DeleteNode(root->rchild,x);    //不断递归寻找待删除节点的过程

    else //找到了待删除节点
    {
        if(root->lchild==NULL)  //左子树为空
        {
            BiTree tempNode = root ; //临时存储是为了free ,和链表删除一样
            root=root->rchild;
            free(tempNode);
        }

        else if(root->rchild==NULL)  //右子树为空
        {
            BiTree tempNode = root ;
            root=root->lchild;
            free(tempNode);
        }

        else  //左右子树都不为空时
            //左子树的最大节点(左子树一路向右),或右子树的最小节点(右子树一路向左)来代替
        {
            BiTree tempNode = root->lchild ;
            if(tempNode->rchild!=NULL) //一路向右,直到取得NULL,那么它的父亲就是我们找的
                tempNode=tempNode->rchild;

            root->key= tempNode->key;    //直接用左子树的最大节点的数据填充删除节点
            DeleteNode(root->lchild,tempNode->key);  //递归地删除每一个向上面补充的节点
        }
    }
}

int main()
{
    BiTree T=NULL;   //树根
    BiTree parent;  //存储父亲节点的地址
    BiTree search;
    KeyType str[]= {54,20,66,40,28,79,58};

    Creat_BST(T,str,7);    //创建
    InOrder(T);
    cout<<endl;

    search=BST_Search(T,40,parent); //查找并不是遍历
    if(search!=NULL)
        cout<<search->key<<" is found"<<endl;
    else
        cout<<"not found"<<endl;

    DeleteNode(T,79); //删除时候,需要知道父亲节点,以便把下面的子树接上去
    InOrder(T);
    cout<<endl;
    return 0;

}


顺序查找

int Search_Seq(SSTable ST,ElemType key)  //顺序查找key的位置
{
    int i=0;
    ST.elem[0]=key;   //放置哨兵,保证查找不会越界
    for(i=ST.TableLen-1; ST.elem[i]!=key; i--); //空循环,查找失败,i=0;
    return i;
    
}

关于srand()和rand()

  • 所在的头文件#include<stdlib.h>
  • 初始化随机数发生器srand() :用来设置rand()产生随机数时的随机数种子,改变了随机数种子,才能产生新的一组随机数
  • 随机数发生器 rand() :和srand()要一起使用,其中srand()用来初始化随机数种子,rand()用来产生随机数

折半查找

int Binary_Search(SSTable L,ElemType key)   //二分查找
{
    int low=0,high=L.TableLen-1,mid;
    while(low<=high)
    {
        mid=(low+high)/2;
        if(L.elem[mid]==key)
            return mid;
        else if(L.elem[mid]<key)
            low=mid+1;   //确定是在中值的右边,左端点压缩到原中点的右边一个
        else
            high=mid-1;  //确定是在中值的左边,右边点压缩到原中点的左边一个
    }
}

关于sort()

  • sort(ST.elem,ST.elem+ST.TableLen,compare); //自定义比较函数
    int cmp(const int &a,const int &b) //自定义的降序函数
    {
      return a>b
    }

  • C++11模板 升序:sort(s.begin(),s.end(),less<data_type>())
    降序:sort(s.begin(),s.end(),greater<data_type>())

完整代码(顺序+折半查找)

#include<bits/stdc++.h>
using namespace std;
#include<algorithm>

typedef  int ElemType;
typedef struct BSTNode
{
    ElemType *elem;   //存储首地址,动态数组,用的是堆空间
    int TableLen;
} SSTable;

int Search_Seq(SSTable ST,ElemType key)  //顺序查找key的位置
{
    int i=0;
    ST.elem[0]=key;   //放置哨兵,保证查找不会越界
    for(i=ST.TableLen-1; ST.elem[i]!=key; i--); //空循环,查找失败,i=0;
    return i;
}

int Binary_Search(SSTable L,ElemType key)   //二分查找
{
    int low=0,high=L.TableLen-1,mid;
    while(low<=high)
    {
        mid=(low+high)/2;
        if(L.elem[mid]==key)
            return mid;
        else if(L.elem[mid]<key)
            low=mid+1;   //确定是在中值的右边,左端点压缩到原中点的右边一个
        else
            high=mid-1;  //确定是在中值的左边,右边点压缩到原中点的左边一个
    }
}

void ST_Init(SSTable &ST,int len)  //初始化顺序表
{
    ST.TableLen=len+1;   //多一个位置存储哨兵
    ST.elem=(ElemType *)malloc(sizeof(ElemType)*ST.TableLen);
    srand(time(NULL));   //随机数生成,没有这一步,每次的随机数是一样的
    for(int i=0; i<ST.TableLen; i++)
        ST.elem[i]=rand()%100;
}

int cmp(const void *a,const void *b)  //qsort的行为参数
{
    return *(ElemType*)a-*(ElemType*)b;   //写大于号会出问题,(a-b)<0,a<b,从小到大
}

bool cmp2(int a,int b)  //sort的行为参数,与上面的cmp相反
{
    return a-b;    //(a-b)>0,a>b,从大到小
}

int main()
{
    SSTable ST;
    ST_Init(ST,10);

    for(int i=0; i<=10; i++)
        cout<<ST.elem[i]<<" ";
    cout<<endl;

    //顺序查找的测试
    int key;
    cin>>key;
    int pos=Search_Seq(ST,key);
    if(pos!=0)
        cout<<"search succuss,the position is "<<pos<<endl;
    else
        cout<<"search fail"<<endl;


    //折半查找的测试
    qsort(ST.elem,ST.TableLen,sizeof(int),cmp); //快排,compare是传递一种行为
    for(int i=0; i<=10; i++)
        cout<<ST.elem[i]<<" ";
    cout<<endl;

    //sort(ST.elem,ST.elem+ST.TableLen,cmp2);
    //sort(s.begin(),s.end(),less<int>()); //降序模版
    //sort(s.begin(),s.end(),greater<int>());  //升序模版

    int key2;
    cin>>key2;
    int pos2=Binary_Search(ST,key2);
    if(pos2!=0)
        cout<<"search succuss,the position is "<<pos2<<endl;
    else
        cout<<"search fail"<<endl;

    return 0;
}

哈希(散列)查找

  • 什么是散列函数 :把查找表中关键字映射为该关键字对应的地址
  • 什么是散列冲突 :不同元素经过散列函数计算的地址是同一个。无冲突的情况下,查找时间复杂度为O(1)
  • 怎么解决散列冲突:开放定址法;链地址法
#include<iostream>
using namespace std;
#define MaxKey 1000

int Hash(const char* key)   //业界常用的哈希函数
{
    int h=0,g;
    while(*key)   //把每一个字节的值拿出来
    {
        h=(h<<4)+*key++;
        g=h&0xf0000000;
        if(g)
        {
            h^=g>>24;
        }
        h&=~g;
    }
    return h% MaxKey;   //算出来的数组下标必须在0-999之间
}
int main()
{
    const char *pStr[5]= {"xiongda","lele","hanmeimei","wangdao","fangfang"}; //定义字符串数组,const表示不能改变里面的字符串
    const char *Hash_table[MaxKey]= {NULL}; //建立哈希表,并清空
    for(int i=0; i<5; i++)
    {
        printf("%s is key=%d\n",pStr[i],Hash(pStr[i]));
        Hash_table[Hash(pStr[i])]=pStr[i];   //把字符串放到哈希表对应计算出的下标的位置
    }

    cout<<Hash_table[377]<<endl;
    return 0;
}

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WYF19999

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值