27 查找算法

【例】顺序查找

顺序查找又称线性查找,它对于顺序表和链表都是适用的。对于顺序表,可通过数组下标递增来顺序扫描每个元素;对于链表,则通过指针 next 来依次扫描每个元素

于前面咱们已经实战了顺序表用数组实现,现在我们实战顺序表用指针,也就是申请一个堆空间,使用方式和数组还是一致的

#include <cstdio>
#include <cstdlib>
#include <ctime>

typedef int ElemType;
typedef struct{
    ElemType* elem;//整型指针
    int TableLen;//存储动态数组里面元素的个数
}SSTable;

int Search_Seq(SSTable ST,ElemType key)
{
    ST.elem[0]=key;//让零号元素作为哨兵
    int i;
    for(i=ST.TableLen-1;ST.elem[i]!=key;--i);
    return i;
}

//init进行了随机数生成,随机数生成考研不会考
void ST_Init(SSTable& ST,int len)
{
    //多申请了一个位置,为了存哨兵,不使用哨兵也可以,为了和王道书保持一致
    ST.TableLen=len+1;
    ST.elem=(ElemType*) malloc(sizeof(ElemType)*ST.TableLen);
    int i;
    srand(time(NULL));//随机数生成
    for(i=1;i<ST.TableLen;i++)//因为第0个是哨兵,所以从1随机
    {
        ST.elem[i]=rand()%100;
    }
}
void ST_print(SSTable ST)
{
    for(int i=0;i<ST.TableLen;i++)
    {
        printf("%3d",ST.elem[i]);
    }
    printf("/n");
}

int main()
{
    SSTable ST;
    ElemType key;
    int pos;//存储查询元素的位置
    ST_Init(ST,10);
    ST_print(ST);
    printf("请输入要搜索的key值:\n");
    scanf("%d",&key);
    pos= Search_Seq(ST,key);
    if(pos)
    {
        printf("查找成功 位置为%d\n",pos);
    } else{
        printf("查找失败\n");
    }
    return 0;
}

【例】折半查找

折半查找又称二分查找,它仅适用于有序的顺序表。

针对顺序表有序,使用 qsort 来排序,

qsort 的使用方法如下:

#include <stdlib.h>
void qsort( void *buf, size_t num, size_t size, int (*compare)(const void *, const void *) );

buf:要排序数组的起始地址,也可以是指针,申请了一块连续的堆空间

num:数组中元素的个数

size:数组中每个元素所占用的空间大小

compare:比较规则,需要我们传递一个函数名,这个函数由我们自己编写,

返回值必须是 int 类型,形参是两个 void 类型指针,这个函数我们编写,但是是qsort 内部调用的,相当于我们传递一种行为给 qsort。

代码流程:

  1. 我们初始化顺序表,随机 10 个元素

  1. 使用 qsort 进行排序,排序完毕后,打印

  1. 输入要查找的元素值,存入变量 key 中

  1. 通过二分查找查找对应 key 值,找到则输出在顺序表中的位置,没找到输出未找到

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef int ElemType;

typedef struct {
    ElemType* elem;//整型指针
    int TableLen;//存储动态数组里边元素的个数
}SSTable;

//init 进行了随机数生成,折半查找我们没有使用哨兵
void ST_Init(SSTable& ST, int len)
{
    ST.TableLen = len;
    ST.elem = (ElemType*)malloc(sizeof(ElemType) * ST.TableLen);
    int i;
    srand(time(NULL));//随机数生成
    for (i = 0; i < ST.TableLen; i++)
    {
        ST.elem[i] = rand() % 100;
    }
}

void ST_print(SSTable ST)
{
    for (int i = 0; i < ST.TableLen; i++)
    {
        printf("%3d", ST.elem[i]);
    }
    printf("\n");
}

//时间复杂度 logn
int BinarySearch(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)
            high = mid - 1;
        else
            low = mid + 1;
    }
    return -1;
}

int compare(const void* left, const void* right)//left,right 是任意两个元素的地址值
{
    return *(ElemType*)left - *(ElemType*)right;
//return *(ElemType*)right - *(ElemType*)left;//从大到小
}

//顺序查找 与 折半查找
int main()
{
    SSTable ST;
    ElemType key;
    int pos;//存储查询元素的位置
    ST_Init(ST, 10);
    qsort(ST.elem, ST.TableLen, sizeof(ElemType), compare);//qsort 实现的是快排
    ST_print(ST);
    printf("二分查找,请输入要搜索的 key 值:\n");
    scanf("%d", &key);
//有序数组
    pos = BinarySearch(ST, key);//二分查找,也叫折半查找
    if (pos != -1)
    {
        printf("查找成功 位置为 %d\n", pos);
    }
    else {
        printf("查找失败\n");
    }
    return 0;
}

【例】二叉排序树

二叉排序树(也称二叉查找树)或者是一棵空树,或者是具有下列特性的二叉树:

1)若左子树非空,则左子树上所有结点的值均小于根结点的值。

2)若右子树非空,则右子树上所有结点的值均大于根结点的值。

3)左、右子树也分别是一棵二叉排序树。

步骤:首先我们新建了一颗二叉排序树,使用的是非递归的方法新建,当然递归的代码也给出了,不过注释了。然后针对建好的二叉排序树进行了中序遍历输出,接着对二叉排序树进行查找,我们可以看到二叉排序树的最大查找次数是树的高度。

#include <stdio.h>
#include <stdlib.h>
typedef int KeyType;
typedef struct BSTNode{
    KeyType key;
    struct BSTNode *lchild,*rchild;
}BSTNode,*BiTree;
//54,20,66,40,28,79,58
// 王道书上的递归写法,代码简单,但是理解有难度
//int BST_Insert(BiTree &T,KeyType k)
//{
// if(NULL==T)
// { //为新节点申请空间,第一个结点作为树根,后面递归再进入的不是树根,是为叶子结点
// T=(BiTree)malloc(sizeof(BSTNode));
// T->key=k;
// T->lchild=T->rchild=NULL;
// return 1;//代表插入成功
// }
// else if(k==T->key)
// return 0;//发现相同元素,就不插入
// else if(k<T->key)//如果要插入的结点,小于当前结点
// //函数调用结束后,左孩子和原来的父亲会关联起来,巧妙利用了引用机制
// return BST_Insert(T->lchild,k);
// else
// return BST_Insert(T->rchild,k);
//}
//54,20,66,40,28,79,58
int BST_Insert(BiTree& T, KeyType k)
{
    if (NULL == T)
    { //为新节点申请空间,第一个结点作为树根,T 是树根
        T = (BiTree)malloc(sizeof(BSTNode));
        T->key = k;
        T->lchild = T->rchild = NULL;
        return 1;//代表插入成功
    }
    BiTree p = T,parent;//p 用来遍历,parent 用来存 p 的父亲
    while (p)
    {
        parent = p;
        if (k == p->key)
        {
            return 0;
        }
        else if (k < p->key)
        {
            p = p->lchild;
        }
        else {
            p = p->rchild;
        }
    }
//开始申请空间并插入
    BiTree pnew = (BiTree)malloc(sizeof(BSTNode));
    pnew->key = k;
    pnew->lchild = pnew->rchild = NULL;
    if (k > parent->key)
    {
        parent->rchild = pnew;//新结点在父亲的右边
    }
    else {
        parent->lchild = pnew;//新结点在父亲的左边
    }
}
//创建二叉排序树
void Creat_BST(BiTree &T,KeyType str[],int n)
{
    T=NULL;//T 是树根
    int i=0;
    while(i<n)
    {
        BST_Insert(T,str[i]);//把某一个结点放入二叉查找树
        i++;
    }
}
//也可以用递归实现,只不过循环查找已经非常简单清晰,用递归必要性不强
BSTNode *BST_Search(BiTree T,KeyType key,BiTree &p)
{
    p=NULL;//存储要找的结点的父亲
    while(T!=NULL&&key!=T->key)
    {
        p=T;
        if(key<T->key) T=T->lchild;//比当前节点小,就左边找
        else T=T->rchild;//比当前节点大,右边去
    }
    return T;
}
void InOrder(BiTree T)
{
    if(T!=NULL)
    {
        InOrder(T->lchild);
        printf("%3d",T->key);
        InOrder(T->rchild);
    }
}
//二叉排序树的创建,中序遍历,查找,删除
int main()
{
    BiTree T=NULL;//树根
    BiTree parent;//存储父亲结点的地址值
    BiTree search;
    KeyType str[7]={54,20,66,40,28,79,58};//将要进入二叉排序树的元素值
    Creat_BST(T,str,7);
    InOrder(T);
    printf("\n");
    search=BST_Search(T,40,parent);
    if(search)
    {
        printf("找到对应结点,值=%d\n",search->key);
    }else{
        printf("未找到对应结点\n");//没找到 search 返回的是 NULL
    }
    return 0;
}

【例】二叉排序树删除

#include <cstdio>
#include <cstdlib>

typedef int KeyType;
typedef struct BSTNode{
    KeyType key;
    struct BSTNode *lchild,*rchild;
}BSTNode,*BiTree;
//54,20,66,40,28,79,58
// 王道书上的递归写法,代码简单,但是理解有难度
//int BST_Insert(BiTree &T,KeyType k)
//{
// if(NULL==T)
// { //为新节点申请空间,第一个结点作为树根,后面递归再进入的不是树根,是为叶子结点
// T=(BiTree)malloc(sizeof(BSTNode));
// T->key=k;
// T->lchild=T->rchild=NULL;
// return 1;//代表插入成功
// }
// else if(k==T->key)
// return 0;//发现相同元素,就不插入
// else if(k<T->key)//如果要插入的结点,小于当前结点
// //函数调用结束后,左孩子和原来的父亲会关联起来,巧妙利用了引用机制
// return BST_Insert(T->lchild,k);
// else
// return BST_Insert(T->rchild,k);
//}
//54,20,66,40,28,79,58

int BST_Insert(BiTree& T, KeyType k)
{
    if (NULL == T)
    { //为新节点申请空间,第一个结点作为树根,T 是树根
        T = (BiTree)malloc(sizeof(BSTNode));
        T->key = k;
        T->lchild = T->rchild = NULL;
        return 1;//代表插入成功
    }
    BiTree p = T,parent;//p 用来遍历,parent 用来存 p 的父亲
    while (p)
    {
        parent = p;
        if (k == p->key)
        {
            return 0;
        }
        else if (k < p->key)
        {
            p = p->lchild;
        }
        else {
            p = p->rchild;
        }
    }
//开始申请空间并插入
    BiTree pnew = (BiTree)malloc(sizeof(BSTNode));
    pnew->key = k;
    pnew->lchild = pnew->rchild = NULL;
    if (k > parent->key)
    {
        parent->rchild = pnew;//新结点在父亲的右边
    }
    else {
        parent->lchild = pnew;//新结点在父亲的左边
    }
}
//创建二叉排序树
void Creat_BST(BiTree &T,KeyType str[],int n)
{
    T=NULL;//T 是树根
    int i=0;
    while(i<n)
    {
        BST_Insert(T,str[i]);//把某一个结点放入二叉查找树
        i++;
    }
}
//也可以用递归实现,只不过循环查找已经非常简单清晰,用递归必要性不强
BSTNode *BST_Search(BiTree T,KeyType key,BiTree &p)
{
    p=NULL;//存储要找的结点的父亲
    while(T!=NULL&&key!=T->key)
    {
        p=T;
        if(key<T->key) T=T->lchild;//比当前节点小,就左边找
        else T=T->rchild;//比当前节点大,右边去
    }
    return T;
}
//这个书上没有二叉排序树删除代码--考大题没那么高
void DeleteNode(BiTree &root,KeyType x){
    if(root == NULL){
        return;
    }
    if(root->key>x){
        DeleteNode(root->lchild,x);//往左子树找要删除的结点
    }else if(root->key<x){
        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;
            while(tempNode->rchild!=NULL){//向右找到最大的
                tempNode = tempNode->rchild;
            }
            root->key = tempNode->key;//把 tempNode 对应的值替换到要删除的值的位置上
            DeleteNode(root->lchild,tempNode->key);//删除 tempNode
        }
    }
}
void InOrder(BiTree T)
{
    if(T!=NULL)
    {
        InOrder(T->lchild);
        printf("%3d",T->key);
        InOrder(T->rchild);
    }
}
//二叉排序树的创建,中序遍历,查找,删除
int main()
{
    BiTree T=NULL;//树根
    BiTree parent;//存储父亲结点的地址值
    BiTree search;
    KeyType str[7]={54,20,66,40,28,79,58};//将要进入二叉排序树的元素值
    Creat_BST(T,str,7);
    InOrder(T);
    printf("\n");
    search=BST_Search(T,40,parent);
    if(search)
    {
        printf("找到对应结点,值=%d\n",search->key);
    }else{
        printf("未找到对应结点\n");//没找到 search 返回的是 NULL
    }
    DeleteNode(T,40);//删除某个结点
    InOrder(T);
    printf("\n");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值