C++学习记录 实验4 StL 应用

一、STL

1.1 STL简介

长久以来,软件界一直希望建立一种可重复利用的东西,以及一种得以制造出”可重复运用的东西”的方法,从函数(functions),类别(classes),函数库(function libraries),类别库(class libraries)、各种组件,从模块化设计,到面向对象(object oriented ),为的就是复用性的提升。

复用性必须建立在某种标准之上。但是在许多环境下,就连软件开发最基本的数据结构(data structures) 和算法(algorithm)都未能有一套标准。大量程序员被迫从事大量重复的工作,竟然是为了完成前人已经完成而自己手上并未拥有的程序代码,这不仅是人力资源的浪费,也是挫折与痛苦的来源。为了建立数据结构和算法的一套标准,并且降低他们之间的耦合关系,以提升各自的独立性、弹性、交互操作性(相互合作性,interoperability),诞生了STL。

STL(Standard Template Library,标准模板库),是惠普实验室开发的一系列软件的统称。现在主要出现在 c++中,但是在引入 c++之前该技术已经存在很长时间了。

STL 从广义上分为: 容器(container) 算法(algorithm) 迭代器(iterator)。
容器和算法之间通过迭代器进行无缝连接。STL 几乎所有的代码都采用了模板类或者模板函数,这相比传统的由函数和类组成的库来说提供了更好的代码重用机会。

STL标准模板库,在我们 c++标准程序库中隶属于 STL 的占到了 80%以上。

1.2 STL三大组件

1.2.1 容器

几乎可以说,任何特定的数据结构都是为了实现某种特定的算法。STL容器就是将运用最广泛的一些数据结构实现出来。
常用的数据结构:数组(array) , 链表(list), tree(树),栈(stack), 队列(queue), 集合(set),映射表(map), 根据数据在容器中的排列特性,这些数据分为序列式容器和关联式容器两种。

  • 序列式容器强调值的排序,序列式容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。Vector容器、Deque容器、List容器等。
  • 关联式容器是非线性的树结构,更准确的说是二叉树结构。各元素之间没有严格的物理上的顺序关系,也就是说元素在容器中并没有保存元素置入容器时的逻辑顺序。关联式容器另一个显著特点是:在值中选择一个值作为关键字key,这个关键字对值起到索引的作用,方便查找。

1.2.2 算法

算法即是问题的解法,以有限的步骤,解决逻辑或数学上的问题。

我们所编写的每个程序都是一个算法,其中的每个函数也都是一个算法,毕竟它们都是用来解决或大或小的逻辑问题或数学问题。STL收录的算法经过了数学上的效能分析与证明,是极具复用价值的,包括常用的排序,查找等等。特定的算法往往搭配特定的数据结构,算法与数据结构相辅相成。

算法分为:质变算法和非质变算法。

  • 质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
  • 非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等

1.2.3 迭代器

迭代器(iterator)是一种抽象的设计概念,现实程序语言中并没有直接对应于这个概念的实物。iterator模式定义如下:提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。

迭代器的设计思维-STL的关键所在,STL的中心思想在于将容器(container)和算法(algorithms)分开,彼此独立设计,最后再一贴胶着剂将他们撮合在一起。

从技术角度来看,容器和算法的泛型化并不困难,c++的class template和function template可分别达到目标,如果设计出两这个之间的良好的胶着剂,才是大难题。

1.3 实例(1)——序列变换

函数:

void transInv(int a[],int b[],int num)//取反操作
{
    for(int i=0;i<num;i++)
    {
        b[i]=-a[i];
    }
}
void transSqr(int a[],int b[],int num)//平方操作
{
    for(int i=0;i<num;i++)
    {
        b[i]=a[i]*a[i];
    }
}
void transCubic(int a[],int b[],int num)//立方操作
{
    for(int i=0;i<num;i++)
    {
        b[i]=a[i]*a[i]*a[i];
    }
}
template <typename inputIter,typename outputIter,typename Myoperator>
void transInvT(inputIter begInput,inputIter endInput,outputIter begOutput,Myoperator op)
{
    for(;begInput!=endInput;begInput++,begOutput++)
    {
//        *begOutput = -(*begInput);//取反
        *begOutput = op(*begInput);//运算符操作
    }
}

template <typename T>
void outputCont(string strName,ostream &os,T begin,T end)//输出函数
{
    os<<strName<<":";
    for(;begin!=end;begin++)
    {
        os<<*begin<<"\t";
    }
    os<<endl;
}

模板:

template <typename T>
void transInvT(T a[],T b[],int num)//取反操作模板
{
    for(int i=0;i<num;i++)
    {
        b[i]=-a[i];
    }
}
template <typename T>
void transSqrT(T a[],T b[],int num)//平方操作模板
{
    for(int i=0;i<num;i++)
    {
        b[i]=a[i]*a[i];
    }
}
template <typename T>
void transCubicT(T a[],T b[],int num)//立方操作模板
{
    for(int i=0;i<num;i++)
    {
        b[i]=a[i]*a[i]*a[i];
    }
}
template <typename T>
T InvT(T a)
{
    return -a;
}
template <typename T>
T SqrT(T a)
{
    return a*a;
}
template <typename T>
T CubicT(T a)
{
    return a*a*a;
}

test:

void Test(){
	const int N = 5;
    int a[N]={1,2,3,4,5};
    outputCont("a",cout,a,a+N);
    int b[N];
    vector<double> vb(N);
    vector<double> vc(N);
    transInv(a,b,N);
    outputCont("InV a",cout,b,b+N);
    transSqr(a,b,N);
    outputCont("Sqr a",cout,b,b+N);
    transCubic(a,b,N);
    outputCont("cubic a",cout,b,b+N);
}

1.4 实例(2)——像素变换

二值化:

template <typename T>
class Mythreshold//图像二值化
{
public:
    Mythreshold(int n=128):_nThreshold(n){}
    int operator()(T val)
    {
        return val<_nThreshold?0:1;
    }
    int _nThreshold;
};

将数组a内的元素当作像元,对每一个像元进行二值化处理,若小于2,则置为0,反之则置为1.
灰度拉伸:

template <typename T>
class Mytrans
{
public:
    Mytrans(int n=128):c(n){}
    int operator()(T val)
    {
        return val+=10;
    }
    int c;
};

二、使用set存储学生信息

定义一个学生类,包括学生学号和姓名:

class studentInfo
{
public:
    studentInfo(string strNo,string strName)
    {
        _strNo = strNo;
        _strName = strName;
    }
    string _strNo;
    string _strName;
    friend ostream& operator<<(ostream& os,const studentInfo& info)
    {
        os<<info._strNo<<" "<<info._strName;
        return os;
    }
    friend bool operator<(const studentInfo& info1,const studentInfo& info2)
    {
        return info1._strNo<info2._strNo;
    }
};


2.1 增删改查操作

template <typename T>
void strNoSearch(string strNo,ostream &os,T begin,T end)//按学号查找
{
    for(;begin!=end;begin++)
    {
        if((*begin)._strNo==strNo)
        {
            os<<"find the student:"<<*begin<<endl;
        }
    }

}
template <typename T>
void strNameSearch(string strName,ostream &os,T begin,T end)//按姓名查找
{
    for(;begin!=end;begin++)
    {
        if((*begin)._strName==strName)
        {
            os<<"find the student:"<<*begin<<endl;
        }
    }

}
template <typename T>
void updatedata(string strNo,string afterNo,string afterName,ostream &os,T begin,T end);

void TestSet()
{
    vector<studentInfo> students;
    students.push_back(studentInfo("10021","zhang san"));
    students.push_back(studentInfo("10002","Li si"));
    students.push_back(studentInfo("10003","wang wu"));
    students.push_back(studentInfo("10011","wang liu"));
    students.push_back(studentInfo("10010","wu liu"));
    set<studentInfo> studentSet(students.begin(),students.end());
    studentSet.insert(studentInfo("10001","jack"));//添加学生信息
    outputCont("student set",cout,studentSet.begin(),studentSet.end());
    if(studentSet.find(studentInfo("10001","jack"))!=studentSet.end())//查找
    {
        cout<<"find the student."<<endl;
    }
    strNoSearch("10001",cout,studentSet.begin(),studentSet.end());
    strNameSearch("jack",cout,studentSet.begin(),studentSet.end());



    studentSet.erase(studentInfo("10001","jack"));//删除学生信息
    outputCont("student set",cout,studentSet.begin(),studentSet.end());

    updatedata("10011","10011","aaa",cout,studentSet.begin(),studentSet.end());//更改学生信息
//    outputCont("student set",cout,studentSet.begin(),studentSet.end());

}
template <typename T>
void updatedata(string strNo,string afterNo,string afterName,ostream &os,T begin,T end)//按学号更改
{
    vector<studentInfo> students;
    students.push_back(studentInfo("10021","zhang san"));
    students.push_back(studentInfo("10002","Li si"));
    students.push_back(studentInfo("10003","wang wu"));
    students.push_back(studentInfo("10011","wang liu"));
    students.push_back(studentInfo("10010","wu liu"));
    set<studentInfo> studentSet(students.begin(),students.end());
    for(;begin!=end;begin++)
    {
        if((*begin)._strNo==strNo)
        {
            studentSet.erase(*begin);
            break;

        }
    }
    studentSet.insert(studentInfo(afterNo,afterName));
    outputCont("student set",cout,studentSet.begin(),studentSet.end());

}

三、使用map统计次数

3.1 map

map是STL的一个关联容器,它提供一对一的hash。

  • 第一个可以称为关键字(key),每个关键字只能在map中出现一次;
  • 第二个可能称为该关键字的值(value);

3.2 统计每个字符出现的次数

void TestMap()
{
//    map<string,int> stu;
//    stu["01"] = 100;
//    stu["02"] = 99;
//    stu["03"] = 98;
//    stu["02"] = 97;
//    for(map<string,int>::iterator it=stu.begin();it!=stu.end();it++)
//    {
//        cout<<it->first<<" "<<it->second<<endl;
//    }
    map<char,int> s;
    char c;
    do{
        cin>>c;//输入字符
        if(isalpha(c))//判断是否为字符
        {
            c=tolower(c);//将字符转为小写
            s[c]++;//统计字符出现次数
        }
    }while(c!='.');
    for(map<char,int>::iterator iter=s.begin();iter!=s.end();++iter)
    {
        cout<<iter->first<<" "<<iter->second<<" ";
    }
    cout<<endl;
}

3.3 输入一个字符串,用map统计每个字符出现的次数并输出字符及对应的次数

void TestMap()
{
//    map<string,int> stu;
//    stu["01"] = 100;
//    stu["02"] = 99;
//    stu["03"] = 98;
//    stu["02"] = 97;
//    for(map<string,int>::iterator it=stu.begin();it!=stu.end();it++)
//    {
//        cout<<it->first<<" "<<it->second<<endl;
//    }
    map<char,int> s;
    char c;
    do{
        cin>>c;//输入字符
        if(isalpha(c))//判断是否为字符
        {
            c=tolower(c);//将字符转为小写
            s[c]++;//统计字符出现次数
        }
    }while(c!='.');
    for(map<char,int>::iterator iter=s.begin();iter!=s.end();++iter)
    {
        cout<<iter->first<<" "<<iter->second<<" ";
    }
    cout<<endl;
}

四、实验总结

通过本次实验,对STL的应用有了进一步的了解,同时也学习了容器和迭代器方面的知识,另外也熟悉了与set和map相关的一些基本操作。

4.1 set总结

和vector、list不同,set、map都是关联式容器。set内部是基于红黑树实现的。插入和删除操作效率较高,因为只需要修改相关指针而不用进行数据的移动。
在进行数据删除操作后,迭代器会不会失效呢?删除set的数据时,实际的操作是删除红黑树中的一个节点,然后相关指针做相关调整。指向其他元素的迭代器还是指向原位置,并没有改变,所以删除一个节点后其他迭代器不会失效。list和map也是同样的道理。然而删除vector中的某个元素,vector中其他迭代器会失效,因为vector是基于数组的,删除一个元素后,后面的元素会往前移动,所以指向后面元素的迭代器会失效。
再稍微说一下迭代器的实现。迭代器是一个对象,vector的迭代器是封装了数组下标;list、map、set的迭代器是封装了元素节点的指针。
还有一点,从数学层面,set的一个集合,好比一个袋子里面装了好多个小球。但是红黑树是一种特殊的二叉搜索树,set中的元素根据其值的大小在红黑树中有特定的位置,是不可移动的。所以,1是search操作效率会很高O(log n),2是set中元素的值不可改变。

4.2 map总结

map以模板(泛型)方式实现,可以存储任意类型的数据,包括使用者自定义的数据类型。Map主要用于资料一对一映射(one-to-one)的情況,map內部的实现自建一颗红黑树,这颗树具有对数据自动排序的功能。在map内部所有的数据都是有序的,后边我们会见识到有序的好处。比如一个班级中,每个学生的学号跟他的姓名就存在著一对一映射的关系。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值