二叉搜索树 set和map的使用

疯狂被积分虐,持续断更。—2018.12.30(雪)。

本来又懒得写东西了,但想想数据结构是以后常用的知识,还是努力努力吧。

为了能更加准确地表达表达概念,我将会从《挑战程序设计竞赛》中摘取部分内容用于博客描述。此外,以下所有图片均引用自《挑战程序设计竞赛》。

二叉搜索树性质与操作介绍

显而易见,二叉搜索树也是以树的形式来储存数据,但数据的排列方式有以下特点:对于所有的节点,左子节点上的数一定小于自己,右子节点上的数一定大于自己。所以,最小的数字一定在最左,而最大的数字在最右,且无相同数据。如图:
在这里插入图片描述

二叉搜索树的操作

(1)查询是否包括某一数值:如果大于当前节点就向右,反之向左走,等于就输出,如果不存在则最后返回空。
(2)插入一个数值:如上用查询的方法找到应该呆的位置,增加节点就好。
(3)删除一个数值:删除操作往往会比较困难,因为一旦删除,就会有子节点悬空,于是要提节点,此时又有好多情况:
1.需要删除的节点没有左儿子,就提右儿子。解释:右儿子>需删除节点>需删除节点的父亲,直接连接无问题。
2.需要删除的节点的左儿子没有右儿子,就提左儿子。解释:同上。
3.以上都不满足的话,就把左儿子的子孙中最大的节点提到需要删除的节点上。解释:为持续保持左小右大。

时间复杂度

O(logn)。每次都二分。

操作实现

//建立结构体 
struct node
{
    int val;//值 
    node *left,*right; //左右子节点 
}; 
//插入x,返回地址
node *insert(node *p,int x) 
{
    if(*p==NULL)//空树时做 
    {
        node *q=new node;
        q->val=x;
        q->left=q->right=NULL:
        return q; 
    }
    else
    {
        if(x<p->val) p->left=insert(p->left,x);//找 
        else p->right=insert(p->right,x);
        return p;//除了最后一次加入的那个节点,其余返回的都是原有的地址值 
    } 
}

//查询是否包括x
bool Find(node *p,int x)
{
    if(p==NULL) return false;
    else if(x==p->val) return true;
    else if(x<p->val) return Find(p->left,x);
    else return Find(p->right,x); 
}

//删除x数值
node *remove(node *p,int x)
{
 //找值
    if(p==NULL) return NULL;
    else if(x<p->val) p->left=remove(p->left,x);
    else if(x>p->val) p->right=remove(p->right,x);
//第一种情况:没有左儿子
    else if(p->left==NULL)
    {
        node *q=p->right;//提右儿子
        delete p;//删
        return q;//返回更新后的节点
    }
//第二种情况:左儿子没有右儿子
    else if(p->left->right==NULL)
    {
        node*q=p->left;
        q->right=p->right;//提上去,原来的右儿子接上来
        delete p;
        return q;//返回更新后的节点
    }
//其余情况
    else
    {
//p为要删除的点,r为左儿子子孙的最大节点,q为r的父节点 
        node *q;
        for(q=p->left;q->right->right!=NULL;q=q->right);//左子孙中最大的节点一定是从右节点一直往下走 
        node *r=q->right;//r为左儿子子孙的最大节点 
        q->right=r->left;//r被拿走了,把r的左节点接到p的右边(p的左节点没动过,r没右节点) 
        r->left=p->left;//r继承p的左和右节点 
        r->right=p->right;
        delete p;
        return r;//返回更新后的节点
    }
    return p;//返回原有地址值,最后回到根 
}

喜闻乐见的STL

set

关于迭代器的使用:set的各个函数的返回值都为指针(地址),所以一定要迭代器来接收返回值。
直接上实验代码,具体使用方式在代码中表示:

#include <iostream>
#include <set>
using namespace std;
int n,i,a,t;
int main()
{
    set<int> s;
    cout<<"请输入个数:"; 
    cin>>n;
    cout<<"请输入"<<n<<"个数字:"; 
//插入操作insert
    for(i=0;i<n;i++) 
    {
        cin>>a;
        s.insert(a);
    }
    set<int>::iterator it;//迭代器 
    cout<<"排序后为:";
//首尾地址begin(),end() 
    for(it=s.begin();it!=s.end();it++)
        cout<<*it<<" ";
//寻找find   
    cout<<endl<<"寻找:"; 
    cin>>t;
    it=s.find(t);
    cout<<"find结果:";
    if(it==s.end()) cout<<"Not found"<<endl;
    else cout<<*it<<" "<<"Found"<<endl;
//计数count  
    cout<<"count结果:";
    if(s.count(t)) cout<<"Found"<<endl;
    else cout<<"Not Found"<<endl;
//长度size 
    cout<<"现有长度:"<<s.size()<<endl;
//删除erase  
    cout<<"消去"<<t<<"后长度:";
    s.erase(t); 
    cout<<s.size()<<endl;
//清空clear
    s.clear(); 
    cout<<"清空后长度:"<<s.size()<<endl;
//空empty,已空则返回true,反之false 
    cout<<"结果:";
    if(s.empty()) cout<<"已空"<<endl;
    else cout<<"未空"<<endl; 
    return 0;
} 

注意end()并不指向容器的任何元素,而是指向容器的最后元素的下一位置,称为超出末端迭代器。且此时end()不为NULL。
实测结果:
在这里插入图片描述
在这里插入图片描述
可知:set中依顺序是从小到大的,且不可有重复数据。

map

map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字key,每个关键字只能在map中出现一次,第二个可能称为该关键字的值value)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。
按我自己看别人的讲解,map好像是事先已知数据间的对应关系,在编程的时候直接输入,而不是运行时输入。map与set的不同之处在于map有key和value,key不可以重复,但value可以。
还是直接上代码:

#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
    int t;
    map<int,string>m;//键和值,key和value
//多种插入方式 
    m.insert(make_pair(1,"one"));
    m.insert(make_pair(9,"nine"));
    m.insert(pair<int,string>(3,"three"));
    m.insert(pair<int,string>(6,"six"));
    m.insert(map<int,string>::value_type(4,"four"));
    m.insert(map<int,string>::value_type(5,"five"));
    m[2]="two";//此种输入可以覆盖之前的值 
    m[7]="seven";
    map<int,string>::iterator it;
    cout<<"当前顺序:"<<endl; 
    for(it=m.begin();it!=m.end();it++)
        cout<<it->first<<" "<<it->second<<endl;
//寻找find 
    cout<<"寻找:"; 
    cin>>t;
    it=m.find(t);
    cout<<"find寻找结果:";
    if(it==m.end()) cout<<"Not found"<<endl;
    else cout<<it->first<<" "<<it->second<<" "<<"Found"<<endl;
//计数count 
    cout<<"count寻找结果:"; 
    if(m.count(t)) cout<<"Found"<<endl;
    else cout<<"Not found"<<endl;
//长度size 
    cout<<"当前长度:"<<m.size()<<endl;
//删去erase 
    m.erase(t);
    cout<<"删去"<<t<<"后长度:"<<m.size()<<endl;
//清空clear 
    m.clear();
    cout<<"清空后长度:"<<m.size()<<endl; 
    return 0;
} 

实现效果:
在这里插入图片描述
由图可知:在不做任何干涉的情况下,map按key升序排列(数值升序,字符串字典序升序)。当然我们也可以对map的key降序排列:

#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
    map<int,string,greater<int> >m;//greater<int>是降序,less<int>是升序
    m.insert(make_pair(1,"one"));
    m.insert(make_pair(9,"nine"));
    m.insert(pair<int,string>(3,"three"));
    m.insert(pair<int,string>(6,"six"));
    m.insert(map<int,string>::value_type(4,"four"));
    m.insert(map<int,string>::value_type(5,"five"));
    m[2]="two"; 
    m[7]="seven";
    map<int,string>::iterator it;
    cout<<"当前顺序:"<<endl; 
    for(it=m.begin();it!=m.end();it++)
        cout<<it->first<<" "<<it->second<<endl;
    return 0;
}

实现结果:
在这里插入图片描述
此外,我们也可以实现对value的升序与降序排列,但我有一点点菜,深感可能暂时用不到看不懂看不懂,就直接附上链接供参考:
c++stl中Map的按Key排序和按Value排序
(其实感觉把key和value反着输进去排序,再反着输出说不定也可以,value有重复的情况下还是算了吧)。

当然map还有好多可用的函数:
1.m1.swap(m2);可用于交换两个map;
2.it=m.lower_bound(3);返回键值>=给定元素的第一个位置。it=m.upper_bound(3);返回键值>给定元素的第一个位置。(注:在数组或vector中用到时要加始末位置,如it=lower_bound(a,a+n,1);或it=lower_bound(v.begin(),v.end(),1)。)在set里也可以用这个。这两个函数也可以用于判断某一值是否存在。if(*it==t)啥的……
3.以后再补,查到了暂时也不知道怎么用,还是不写了。

虽说有了总结与大致的理解,但我还还还还还没实际用过,总之,实践才是检验真理的唯一标准(雾),未来有了更多的理解会对此篇进行进一步的补充。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值