数据结构精录&总结Episode.8 数据结构入门之查找(基于Visual C++)

最近两天魅族17手机发布,罗永浩老师又要开始直播带货了。但是一改往日的自信,罗永浩老师对于本次魅族17发布会的态度不甚悲观。其中他在微博上的回复提到,魅族17发布会看不看都无所谓,因为首先是可以看回放,其次是大家口头支持一波实际上还是都买Iphone去了。

有点酸,但是支持国货当然更不应该停留在言论上。2020Q1季度的财报显示,手机成交额/成交量仍然是Iphone11占领头衔,除次以外小米Redmi系列勉强上榜,诸如华为等国内一众呼声很高的企业连影子都没有。

再过两天博主也决定要将自己的Iphone更换成华为P40 Pro了,不管曾经这些公司怎么样,做了什么,支持国货总是有利无弊的。尤其是在当前疫情期间国际经济等等形势都不景气的情况下。


查找算法,总结起来分为线性表查找、树表查找和散列表查找三大类,其中线性表查找分为顺序查找和折半查找,树表查找分为二叉查找树与平衡二叉查找树。实际上树表查找中还有许许多多的子分支如红黑树,这些对于非计算机专业的同学来说仅需了解,故次从略。以下是博主自行编写的的查找算法总结表。


上述总结对应的代码如下,具体解释等已嵌入注释中。

// 第八章 查找.cpp : 此文件不包含 "main" 函数。程序执行将分别在下述的各个cpp中。编写—JoeyBG,算法尚有不足之处,敬请谅解。
//

/*
#include <iostream>
#include<algorithm>
#include<math.h>
#include<iomanip>
using namespace std;
*/

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单
// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
//顺序查找
#include<iostream>
using namespace std;
#define Maxsize 100

int SqSearch(int r[],int n,int x)//顺序查找
{
	for(int i=0;i<n;i++) //要判断i是否超过范围n
        if(r[i]==x) //r[i]和x比较
            return i;//返回下标
    return -1;
}

int SqSearch2(int r2[],int n,int x)//顺序查找优化算法
{
	int i;
	r2[0]=x;//待查找元素放入r[0],作为监视哨
	for(i=n;r2[i]!=x;i--);//不需要判断i是否超过范围
    return i;
}

int main()
{
    int i,n,x,r[Maxsize],r2[Maxsize+1];
    cout<<"请输入元素个数n为:"<<endl;
    cin>>n;
    cout<<"请依次n个元素:"<<endl;
    for(int i=0;i<n;i++)
    {
        cin>>r[i];
        r2[i+1]=r[i];//r2[]数组0空间未用,做监视哨
    }
    cout<<endl;
    cout<<"请输入要查找的元素:";
    cin>>x;
    //i=SqSearch(r,n,x);
//    if(i==-1)
//      cout<<"该数列中没有要查找的元素"<<endl;
//    else
//      cout<<"要查找的元素在第"<<i+1<<"位"<<endl;
    i=SqSearch2(r2,n,x);
    if(i==0)
		cout<<"该数列中没有要查找的元素"<<endl;
    else
		cout<<"要查找的元素在第"<<i<<"位"<<endl;
    return 0;
}
//折半查找
#include<iostream>
#include<cstdlib> //排序sort函数需要该头文件
#include<algorithm>
using namespace std;
const int M=100;
int x,n,i;
int s[M];

int BinarySearch(int s[],int n,int x)//二分查找非递归算法
{
   int low=0,high=n-1;  //low指向有序数组的第一个元素,high指向有序数组的最后一个元素
   while(low<=high)
   {
       int middle=(low+high)/2;  //middle为查找范围的中间值
       if(x==s[middle])  //x等于查找范围的中间值,算法结束
          return middle;
       else if(x>s[middle]) //x大于查找范围的中间元素,则从左半部分查找
              low=middle+1;
            else            //x小于查找范围的中间元素,则从右半部分查找
              high=middle-1;
    }
    return -1;
}

int recursionBS (int s[],int x,int low,int high) //二分查找递归算法
{
    //low指向数组的第一个元素,high指向数组的最后一个元素
    if(low>high)              //递归结束条件
        return -1;
    int middle=(low+high)/2;  //计算middle值(查找范围的中间值)
    if(x==s[middle])          //x等于s[middle],查找成功,算法结束
        return middle;
    else if(x<s[middle])      //x小于s[middle],则从前半部分查找
             return recursionBS (s,x,low,middle-1);
           else               //x大于s[middle],则从后半部分查找
             return recursionBS (s,x,middle+1,high);
}

int main()
{
    cout<<"该数列中的元素个数n为:";
    cin>>n;
    cout<<"请依次输入数列中的元素:";
    for(i=0;i<n;i++)
       cin>>s[i];
    sort(s,s+n); //二分查找的序列必须是有序的,如果无序需要先排序
    cout<<"排序后的数组为:";
    for(i=0;i<n;i++)
       cout<<s[i]<<" ";
    cout<<endl;
    cout<<"请输入要查找的元素:";
    cin>>x;
    //i=BinarySearch(s,n,x);
    i=recursionBS(s,x,0,n-1);
    if(i==-1)
		cout<<"该数列中没有要查找的元素"<<endl;
    else
		cout<<"要查找的元素在第"<<i+1<<"位"<<endl;//位序和下标差1
    return 0;
}
//二叉查找树
#include<iostream>
using namespace std;
#define ENDFLAG -1
typedef int ElemType;

typedef struct BSTNode{
	ElemType data;	//结点数据域
	BSTNode *lchild,*rchild;	//左右孩子指针
}BSTNode,*BSTree;

BSTree SearchBST(BSTree T,ElemType key)//二叉排序树的递归查找
{
    //若查找成功,则返回指向该数据元素结点的指针,否则返回空指针
    if((!T)|| key==T->data)
        return T;
    else if (key<T->data)
            return SearchBST(T->lchild,key);//在左子树中继续查找
        else
            return SearchBST(T->rchild,key); //在右子树中继续查找
}

void InsertBST(BSTree &T,ElemType e)//二叉排序树的插入
{
    //当二叉排序树T中不存在关键字等于e的数据元素时,则插入该元素
    if(!T)
    {
        BSTree S=new BSTNode; //生成新结点
        S->data=e;             //新结点S的数据域置为e
        S->lchild=S->rchild=NULL;//新结点S作为叶子结点
        T=S;            		//把新结点S链接到已找到的插入位置
    }
    else if(e<T->data)
            InsertBST(T->lchild,e );//插入左子树
        else if(e>T->data)
            InsertBST(T->rchild,e);//插入右子树
}

void CreateBST(BSTree &T )//二叉排序树的创建
{
    //依次读入一个关键字为key的结点,将此结点插入二叉排序树T中
    T=NULL;
    ElemType e;
    cin>>e;
    while(e!=ENDFLAG)//ENDFLAG为自定义常量,作为输入结束标志
    {
        InsertBST(T,e);  //插入二叉排序树T中
        cin>>e;
    }
}

void DeleteBST(BSTree &T,char key)
{
  //从二叉排序树T中删除关键字等于key的结点
    BSTree p=T;BSTree f=NULL;
    BSTree q;
    BSTree s;
    if(!T) return; //树为空则返回
    while(p)//查找
    {
        if(p->data==key) break;  //找到关键字等于key的结点p,结束循环
        f=p;                //f为p的双亲
        if (p->data>key)
            p=p->lchild; //在p的左子树中继续查找
        else
            p=p->rchild; //在p的右子树中继续查找
    }
    if(!p) return; //找不到被删结点则返回
    //三种情况:p左右子树均不空、无右子树、无左子树
    if((p->lchild)&&(p->rchild))//被删结点p左右子树均不空
    {
        q=p;
        s=p->lchild;
        while(s->rchild)//在p的左子树中继续查找其前驱结点,即最右下结点
        {
            q=s;
            s=s->rchild;
        }
        p->data=s->data;  //s的值赋值给被删结点p,然后删除s结点
        if(q!=p)
            q->rchild=s->lchild; //重接q的右子树
        else
            q->lchild=s->lchild; //重接q的左子树
        delete s;
    }
    else
    {
        if(!p->rchild)//被删结点p无右子树,只需重接其左子树
        {
            q=p;
            p=p->lchild;
        }
        else if(!p->lchild)//被删结点p无左子树,只需重接其右子树
        {
             q=p;
             p=p->rchild;
        }
        /*――――――――――将p所指的子树挂接到其双亲结点f相应的位置――――――――*/
        if(!f)
            T=p;  //被删结点为根结点
        else if(q==f->lchild)
                f->lchild=p; //挂接到f的左子树位置
            else
                f->rchild=p;//挂接到f的右子树位置
        delete q;
	}
}

void InOrderTraverse(BSTree &T)//中序遍历
{
    if(T)
	{
        InOrderTraverse(T->lchild);
        cout<<T->data<<"\t";
        InOrderTraverse(T->rchild);
	}
}

int main()
{
	BSTree T;
	cout<<"请输入一些整型数,-1结束"<<endl;
	CreateBST(T);
	cout<<"当前有序二叉树中序遍历结果为"<<endl;
	InOrderTraverse(T);
	cout<<endl;
	ElemType key;//待查找或待删除内容
	cout<<"请输入待查找关键字"<<endl;
	cin>>key;
	BSTree result=SearchBST(T,key);
	if(result)
        cout<<"找到"<<key<<endl;
	else
        cout<<"未找到"<<key<<endl;
	cout<<"请输入待删除关键字"<<endl;
	cin>>key;
	DeleteBST(T,key);
	cout<<"当前有序二叉树中序遍历结果为"<<endl;
	InOrderTraverse(T);
	return 0;
}

//平衡二叉查找树
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

typedef struct AVLNode{
   int data;
   int height;
   struct AVLNode *lchild;
   struct AVLNode *rchild;
}*AVLTree;

AVLTree Empty(AVLTree &T)//删除树
{
    if(T==NULL) return NULL;
    Empty(T->lchild);
    Empty(T->rchild);
    delete T;
    return NULL;
}

inline int Height(AVLTree T)//计算高度
{
    if(T==NULL) return 0;
    return T->height;
}

void updateHeight(AVLTree &T)
{
     T->height=max(Height(T->lchild),Height(T->rchild))+1;
}

AVLTree LL_Rotation(AVLTree &T)//LL旋转
{
    AVLTree temp=T->lchild;
    T->lchild=temp->rchild;
    temp->rchild=T;
    updateHeight(T);//更新高度
    updateHeight(temp);
    return temp;
}

AVLTree RR_Rotation(AVLTree &T)//RR旋转
{
    AVLTree temp=T->rchild;
    T->rchild=temp->lchild;
    temp->lchild=T;
    updateHeight(T);//更新高度
    updateHeight(temp);
    return temp;
}

AVLTree LR_Rotation(AVLTree &T)//LR旋转
{
     T->lchild=RR_Rotation(T->lchild);
     return LL_Rotation(T);
}

AVLTree RL_Rotation(AVLTree &T)//RL旋转
{
    T->rchild=LL_Rotation(T->rchild);
    return RR_Rotation(T);
}

AVLTree Insert(AVLTree &T,int x)
{
    if(T==NULL) //如果为空,创建新结点
    {
        T=new AVLNode;
        T->lchild=T->rchild=NULL;
        T->data=x;
        T->height=1;
        return T;
     }
    if(T->data==x) return T;//查找成功,什么也不做,查找失败时才插入
    if(x<T->data)//插入到左子树
    {
        T->lchild=Insert(T->lchild,x);//注意插入后饭后结果挂接到T->lchild
        if(Height(T->lchild)-Height(T->rchild)==2)//插入后看是否平衡,如果不平衡显然是插入的那一边高度大
        {                                         //沿着高度大的那条路径判断
            if(x<T->lchild->data)//判断是LL还是LR,即插入的是lchild节点的lchild 还是rchild
                T=LL_Rotation(T);
            else
                T=LR_Rotation(T);
        }
    }
    else//插入到右子树
    {
        T->rchild=Insert(T->rchild,x);
        if(Height(T->rchild)-Height(T->lchild)==2)
        {
            if(x>T->rchild->data)
                T=RR_Rotation(T);
            else
                T=RL_Rotation(T);
        }
    }
    updateHeight(T);
    return T;
}

AVLTree adjust(AVLTree &T)//删除结点后,需要判断是否还是平衡,如果不平衡,就要调整
{
    if(T==NULL) return NULL;
    if(Height(T->lchild)-Height(T->rchild)==2)//沿着高度大的那条路径判断
    {
        if(Height(T->lchild->lchild)>=Height(T->lchild->rchild))
            T=LL_Rotation(T);
        else
            T=LR_Rotation(T);
    }
    if(Height(T->rchild)-Height(T->lchild)==2)//沿着高度大的那条路径判断
    {
        if(Height(T->rchild->rchild)>=Height(T->rchild->lchild))
            T=RR_Rotation(T);
        else
            T=RL_Rotation(T);
    }
    updateHeight(T);
    return T;
}

AVLTree Delete(AVLTree &T,int x)
{
    if(T==NULL) return NULL;
    if(T->data==x)//如果找到删除节点
    {
        if(T->rchild==NULL)//如果该节点的右孩子为NULL,那么直接删除
        {
            AVLTree temp=T;
            T=T->lchild;
            delete temp;
        }
        else//否则,将其右子树的最左孩子作为这个节点,并且递归删除这个节点的值
        {
           AVLTree temp;
           temp=T->rchild;
           while(temp->lchild)
              temp=temp->lchild;
           T->data=temp->data;
           T->rchild=Delete(T->rchild,T->data);
           updateHeight(T);
        }
        return T;
    }

    if(T->data>x)//调节删除节点后可能涉及的节点
        T->lchild=Delete(T->lchild,x);
    if(T->data<x)
        T->rchild=Delete(T->rchild,x);
    updateHeight(T);
	T=adjust(T);
    return T;
}

void Preorder(AVLTree T)//前序遍历方便看树的结果
{
    if(T==NULL) return ;
    cout<<T->data<<"\t"<<T->height<<endl;
    Preorder(T->lchild);
    Preorder(T->rchild);
}

 void Inorder(AVLTree T)//中序遍历方便看树的结果
{
    if(T==NULL) return ;
    Inorder(T->lchild);
    cout<<T->data<<"\t"<<T->height<<endl;
    Inorder(T->rchild);
}

 void Posorder(AVLTree T)//后序遍历方便看树的结果
{
    if(T==NULL) return ;
    Posorder(T->lchild);
    Posorder(T->rchild);
    cout<<T->data<<"\t"<<T->height<<endl;
}

void show(AVLTree T)
{
    Preorder(T);
    cout<<endl;
    Inorder(T);
    cout<<endl;
    Posorder(T);
}

AVLTree CreateAVL(AVLTree &T)
{
    int n,x;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>x;
        T=Insert(T,x);
    }
    return T;
}
int main()
{
    int x;
    AVLTree root=NULL;
    root=Empty(root);
    CreateAVL(root);
    show(root);
    cin>>x;
    root=Delete(root,x);
    show(root);
    return 0;
}
//散列表
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;

#define m 15//哈希表的表长
#define NULLKEY 0//单元为空的标记

int HT[m],HC[m];

int H(int key)//哈希函数
{
	return key%13;
}

int Linedetect(int HT[],int H0,int key,int &cnt)
{
    int Hi;
    for(int i=1;i<m;++i)
    {
        cnt++;
        Hi=(H0+i)%m; //按照线性探测法计算下一个哈希地址Hi
        if(HT[Hi]==NULLKEY)
            return Hi;	//若单元Hi为空,则所查元素不存在
        else if(HT[Hi]==key)
            return Hi; //若单元Hi中元素的关键字为key
    }
    return -1;
}

int Seconddetect(int HT[],int H0,int key,int &cnt)
{
    int Hi;
    for(int i=1;i<=m/2;++i)
    {
        int i1=i*i;
        int i2=-i1;
        cnt++;
        Hi=(H0+i1)%m; //按照线性探测法计算下一个哈希地址Hi
        if(HT[Hi]==NULLKEY)//若单元Hi为空,则所查元素不存在
            return Hi;
        else if(HT[Hi]==key)//若单元Hi中元素的关键字为key
            return Hi;
        cnt++;
        Hi=(H0+i2)%m; //按照线性探测法计算下一个哈希地址Hi
        if(Hi<0)
            Hi+=m;
        if(HT[Hi]==NULLKEY)//若单元Hi为空,则所查元素不存在
            return Hi;
        else if(HT[Hi]==key)//若单元Hi中元素的关键字为key
            return Hi;
    }
    return -1;
}

int SearchHash(int HT[],int key)
{
    //在哈希表HT中查找关键字为key的元素,若查找成功,返回哈希表的单元标号,否则返回-1
    int H0=H(key); //根据哈希函数H(key)计算哈希地址
    int Hi,cnt=1;
    if(HT[H0]==NULLKEY)//若单元H0为空,则所查元素不存在
        return -1;
    else if(HT[H0]==key)//若单元H0中元素的关键字为key,则查找成功
        {
            cout<<"查找成功,比较次数:"<<cnt<<endl;
            return H0;
        }
        else
        {
            Hi=Linedetect(HT,H0,key,cnt);
            if(HT[Hi]==key)//若单元Hi中元素的关键字为key,则查找成功
            {
                cout<<"查找成功,比较次数:"<<cnt<<endl;
                return Hi;
            }
            else
                return -1;	//若单元Hi为空,则所查元素不存在
        }
}

bool InsertHash(int HT[],int key)
{
    int H0=H(key); //根据哈希函数H(key)计算哈希地址
    int Hi=-1,cnt=1;
    if(HT[H0]==NULLKEY)
    {
        HC[H0]=1;//统计比较次数
        HT[H0]=key;	//若单元H0为空,放入
        return 1;
    }
    else
    {
        Hi=Linedetect(HT,H0,key,cnt);//线性探测
        //Hi=Seconddetect(HT,H0,key,cnt);//二次探测
        if((Hi!=-1)&&(HT[Hi]==NULLKEY))
        {
            HC[Hi]=cnt;
            HT[Hi]=key;//若单元Hi为空,放入
            return 1;
        }
    }
    return 0;
}

void print(int HT[])
{
    for(int i=0;i<m;i++)
        cout<<HT[i]<<"\t";
    cout<<endl;
}

int main()
{
	int x;
	memset(HT,0,sizeof(HT));
	memset(HC,0,sizeof(HC));
	print(HT);
	cout<<"输入12个关键字,存入哈希表中:"<<endl;
	for(int i=0;i<12;i++)
	{
		cin>>x;//14 36 42 38 40 15 19 12 51 65 34 25
		if(!InsertHash(HT,x))
		{
		    cout<<"创建哈希表失败!"<<endl;
		    return 0;
		}
	}
	cout<<"输出哈希表:"<<endl;
	print(HT);
	print(HC);
	cout<<"输入要查找的关键字"<<endl;
	cin>>x;
	int result=SearchHash(HT,x);
	if(result!=-1)
		cout<<"在第"<<result+1<<"位置找到"<<endl;
	else
		cout<<"未找到"<<endl;
    return 0;
}

/*
参考资料:
1、陈小玉:趣学数据结构,人民邮电出版社,2019.09
*/

由于查找章节北理乐学平台是没有给出具体的代码练习题的,仅有知识点考核的选择题,在仔细阅读了上述总结的概念和代码后,正确回答它们不难,此略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值