STL学习指南(一)

STL学习指南

STL 指的就是C++的 标准模板库(Stand Template Library),所谓模板,是指不必预先制定类型的函数或类,STL 为用户提供了多种名为容器( Container ) 的类包括 动态数组 、 表 、 栈 、 队列等数据结构,可以说它是我们平时刷题的利器,本篇针对于算法刷题,总结自己常用到的一些简单的使用教程以及和 使用C的不同之处,由C到C++的一个完整的过渡。主要是针对学完C想使用C++的小伙伴~省的去看一堆厚厚的,而且都写的都是关于C++特性的东西,顺便自己做一个总结。

由C到C++过渡

由C使用到C++我们需要了解,C与C++有什么区别, C++中有什么好用的特性,下面一一具体说到

头文件

从使用C到C++的这个过渡,那么我们就从程序的最起始的部分开始,看看C和C++区别在那里 以一个简短的a+b问题为例:

#include <cstdio>
int main(){
    int a, b;
    while(scanf("%d%d", &a, &b) == 2) printf("%d\n", a + b);
    return 0;
}

??? 这么一看? 貌似没什么区别呀? 注意头文件,之前在C中使用的头文件是stdio.h,而现在换成了stdio,这就是使用C++的区别之一,实际上stdio.h是依旧存在,但是在C++中推荐是用的是cstdio,同理,string.h就变成了cstringmath.h变成了cmathctype.h变成了cctype, 所用以后使用原来C的头文件的换成一下的写法:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cctype>
C++中的特有的输入输出

在C++简化了输入输出的形式 ,不用去考虑输入输出的信息的类型 ,我把刚刚 的那个程序用C++的格式写一下看看区别

#include <iostream>
using namespace std;
int main(){
    int a, b;
    while(cin >> a >> b) cout << a + b << endl;
    return 0;
}

这样一看,代码清爽了许多,但是可能也有不熟悉C++的小伙伴不太清楚,#include <iostream>using namespace std 是个啥?iostream 用中文解释就是输入输出流(output input stream) 对应了C中的stdio.h标准输入输出(Standard input output) ,其实他们就是同样的意思,用来做输入输出的,using namespace std留到一会再详细解释 ~

scanfprintfstdio.h中,cincoutiostream中,cincout简化了scanfprintf 输入输出方式, cin >> a 对应了scanf("%d", &a)cout << a对应了printf("%d", a), 而且使用cincout 不用像在C中那样去写出对应输入输出%d%f%c这样的数据的类型。

输出时的endl表示此行结束(end of line),其实就是换行和C中\n效果一样 为了简洁一点出现变量时使用 cout << a << endl ,字符串就是使用cout << " hello\n"

注意 :

  1. cincout 的速度是比scanfprintf 慢的,具体想了解的话可以看看这道题就能体现的出来 ,在某些题上的特定情况会出现超时的现象
  2. 使用C++以后,并不是使用cincout 一直都很好,这个要根据具体的情况决定,如果有的题目需要输出带几位特定小数的的情况,此时cout貌似就没有那么好用啦~例如某个数字前特定的有几个零时printf("%04d",15), 或者输出有多个空格等其他特殊格式时,也使用printf
解释using namespace std

其实翻译过来就是使用名称空间 std ,因为在C++中都会出现cincout这样的语句 输入,输出的本身的写法的是std::cinstd::cout 如果的函数有多个输入输出的话这样写就非常繁琐,所有我们会加上using namespace std 来去简化写法,当然啦~ 你不加上那句话可以的,那天它就是这个样子的~

#include <iostream>
int main(){
    int a, b;
    while(std::cin >> a >> b) std::cout << a + b << std::endl;
    return 0;
}
C++中特有的bool变量

C++中有出现了一个C中没有的变量bool 它叫做布尔,其实就是和java中 的boolean是一个东西啦~ 它只有两个值——trueflase ,不同的就是bool也可以用数字来表示,非零的数字表示true,零表示false

C++中特有的string

string的出现可以说是大大简化了输出字符的过程,在C中只有char类型的数据,如果你想使用输出string这样的格式,那么你就是得使用指针(char *)或者二维数组表示,非常麻烦, 我们看看在C++中使用的吧~

string的赋值

string的赋值其实和其他的变量赋值没有什么区别,但是它也可是使用构造的方式给他赋值(虽然这样的方法用的少)

string s ("hello"), s1 = " hello~~";
cout << s << s1;
string的拼接
//直接使用 + 就可以拼接字符串非常简洁
string s1 = "hello", s2 = "world";
string s = s1 + s2;
cout << s;
string的长度
//通过size() 和length()都能获取长度
string s1 = "hello", s2 = "world";
string s = s1 + s2;
cout << s.size() << s.length();
string的比较

一般情况下:

    /**
    string的比较是使用 >, <, <=, >=, ==, != 这样的符号来表示的
    string通过字典的顺序从前到后比较,遇到不同的字符比较字典序,
    字典序靠前的的字符小,靠后的就是字符大(理解为按照ASCII),出现不同时通过本次的不同比		较出当前字符串的大小
    **/
    string s1 = "ab", s2 = "aca";
    if(s1 > s2) cout << s1;
    else cout << s2; //明显的输出的就是aca

出现字符一致长度不同时

   string s1 = "aa", s2 = "aaa";
   if(s1 > s2) cout << s1;
   else cout << s2; //输出aaa

出现大小写时

	 //出现大小写通过ASCII比较,A的ACSII码为65 a为97 
    string s1 = "Acz", s2 = "acZ";
    if(s1 > s2) cout << s1;
    else cout << s2; //输出acZ

string字符串的比较用的还是比较多的,例如如果出现学生的成绩相同, 通过他们的名称自典顺序比较~~,以后刷题上会遇到类似的题目。

string的子串

在处理 字符串的时候常常会用到字符串的子串,下面看看字符串的子串是怎么使用的吧

截取子串的一般是有两种用法
    string t, s = "hello!";
    //截取字符串的下标为0开始,2个字符 
    t = s.substr(0,2);
    cout << t  << endl; // he
    //下标为1到字符串尾
    t = s.sbustr(1);
    cout << t << endl; // ello!
string的大小写转换

C++中大小写通过tolower, toupper方式转化大小写,目前提供一种基本的方式,其他方法后续提到。

#include <iostream>
#include <cstdio>
using namespace std;

int main(){
    string s = "hello";
    for(int i = 0; i < s.size(); i++){
        //转化为大写 对于单个字符
        s[i] = toupper(s[i]);
        //转化为小写
        s[i] = tolower(s[i]);
    }
    cout << s;
    return 0;
}
string的输入

使用输入时大概三种情况

  • 使用cin

       /*对于不同的情况输入字符,使用`cin`的方式中间出现空格就会分隔 ,即空格为分隔符,前后表示 s1, s2 */
    	 string s1, s2;
        cin >> s1 >> s2;
        cout << s1 << s2;
    
  • 使用getline

    /*对于某种特殊的情况你也可能使用读入一整行的字符,这个方法通`getline`实现*/
        string s;
        getline(cin, s);
        cout << s;
    
  • 同时使用时

    注意:同时使用cingetline时刚刚接触容易出现错误,使用cin后通过回车读入字符,在getline中第4行相当于无效了,通过添加getchar() 出去回车,这个地方不细心的话就常常出现错误,尤其实在作题的时候,可以把getchar()去掉看看输出是什么结果

        string s1, s2, s3;
        cin >> s1 >> s2;
        getchar();
        getline(cin,s3);
        cout << s1 << s2 << s3;
    
string的输出

string的输出主要两种方式

  • 使用cout

        string s = "hello";
        cout << s;
    
  • 使用printf

    /*使用printf ? C中并没有string这个类型啊 那么他输出
    的类型是什么呢? 猜测按照之前的方式就应该是%吧*/
    	   string s = "hello";
        	printf("%s\n", s);
    //按照这样的输出方式,其实是错的,不知道的这个细节的话可以亲自试试
    //正确做法是
        string s = "hello";
        printf("%s\n", s.c_str());
    /*可能会有疑问为甚么 要用这么繁琐的方式呢?本来不就有
    简单的方法吗?原因和使用scanf一样 主要还是因为遇到特
    殊格式时使用,比如多种同时输出多种格式的数据时添加空格
    使用cout在一行中写的太长~ ,其次是速度快
    */
    
C++中的引用与传值

在C++中添加了一个引用型的数据类型符号表示为&, 你能你觉得这为什么和取地址的符号一样?其实引用是在变量前加上&符号,传出参数就像这样int &a,不知道它怎么使用也没关系我们从C中的swap的那个示例说起

  • 使用C交换两个变量的值(有误)

    #include <cstdio>
    void swap1(int a, int b){
        int t = a; a = b; b = t;
    }
    int main(){
        int a = 6, b = 10;
        swap1(a, b);
        printf("%d %d", a , b);
       //毫无疑问 ,输出为 6 , 10 我就不多解释啦~
        return 0;
    }
    
  • 使用C交换两个变量的值

    其实真正想要使用函数的方式交换了两个数的值,是要使用指针的,而使用指针的这种凡是刚刚接触还是不好理解的~

    #include <cstdio>
    					//这里理解为声明
    void swap1(int *a, int *b){
     //这里的*a可不是函数参数中的那个*a哦~,此处*a意思时解引用
        int t = *a; *a = *b; *b = t;
    }
    int main(){
        int a = 6, b = 10;
        swap1(&a, &b);
        printf("%d %d", a , b);
       //输出为10 6
        return 0;
    }
    
  • 使用C++的引用方式

    刚刚我们使用指针的方式完成了交换,但是多少还是有些麻烦的,接下来看看使用引用的方式怎么实现值的交换

    #include <iostream>
    #include <cstdio>
    using namespace std;
    void swap1(int &a, int &b){
        int t = a; a = b; b = t;
    }
    int main(){
        int a = 6, b = 10;
        swap1(a, b);
        printf("%d %d", a , b);// 10 6
        return 0;
    }
    

    通过C++中使用引用的方法,在参数名前添加&,表示这个参数按照传引用(call by reference)的方式, 而不是C中(call by value),其实类似于指针的功能,意思就是按照值传递他就是单向的的传递,指针双向传递,而引用代替了C中的指针,通过传引用的凡是改变了实参的值

C++中的结构体

C++中省略了结构的声明的写法,当结构题定义完,直接写名字,不用去写struct啦~ ,如果在C中想不写struct,你还用要用到typedef

#include <iostream>
#include <cstdio>
using namespace std;
struct stu{
    string name;
    int score;
};
int main(){
    stu  sarr[5];		//C++中的声明方式
    struct sarrt[5];	//C中的声明方式
    return 0;
}

STL初步学习

本篇首先引出基本vector,set,map,stack,queue这些作题中常常用到的集合及一些简单的使用,后续会在第二篇中总结全面点的使用方式,以及C++ 11中的特性。

vector的使用
  • vector的理解

    vector意思就是矢量,其实vector就是动态数组,它的长度时不确定的,可以不用特意声明vector的长度,相比与 C中的数组我们必须要对它声明长度使用vector的时候需要引用头文件#include <vector>,using namepace std

  • vector主要的成员函数

    函数名功能复杂度
    size()返回向量的个数 O ( 1 ) {O(1)} O(1)
    push_back(n)在向量的末尾添加n O ( 1 ) {O(1)} O(1)
    begin()返回指向向量开头的迭代器 O ( 1 ) {O(1)} O(1)
    end()返回指向向量末尾的的后一个迭代器 O ( 1 ) {O(1)} O(1)
    insert(p, x)在向量P的位置插入x O ( n ) {O(n)} O(n)
    erase§删除向量中位置p的元素 O ( n ) {O(n)} O(n)
    clear()删除向量中所有的元素 O ( n ) {O(n)} O(n)
    pop_back()删除向量的最后一个元素 O ( 1 ) {O(1)} O(1)
    resize()给向量分配大小 O ( 1 ) {O(1)} O(1)
  • 具体使用方法

    1. 声明

        #include <iostream>
      #include <cstdio>
      #include <vector>
      using namespace std;
      int main(){
          //定义一个不定长度vector v1
          vector<int> v1;
          //定义v1并指定v2大小为5 ,没有被赋值的元素默认为0
          vector<int> v2(5);
          //定义v3的长度为3,默认值全部为 4
          vector<int> v3(3, 4);
          vector<int> v4;
           //将不定长的vector设置长度为6,默认初始化的值为0
          v4.resize(6);
          //这个要和v2区分一下 意思就是每个v5[i]都是一个vector
          //理解成二维数组 
          vector<int> v5[2];
          return 0;
      }
      
    2. 基本函数使用

       #include <iostream>
       #include <cstdio>
       #include <vector>
       using namespace std;
       void dispaly(vector<int> v){
           //遍历vector
           for(int i = 0; i < v.size(); i++){
               cout << v[i] << " ";
           }
           cout << endl;
       }
       int main(){
           //声明一个vector v1 没有分配大小
           vector<int> v1;
           v1.push_back(1);
           v1.push_back(2);
           dispaly(v1); // 1 2
           //使用数组的形式
           v1[1] = 3;
           dispaly(v1); // 1 3
           v1.insert(v1.begin() + 1, 9);
           //尾部插入2 个 1
           //v1.insert(v1.end(), 2, 1);
           dispaly(v1);// 1 9 3
           v1.erase(v1.begin() + 1);
           dispaly(v1); //1 3
           cout << v1.size() << endl;  // 2
           v1.resize(6);//没有被赋值的元素默认为0
           cout << v1.size() << endl; // 6
           dispaly(v1);//1 3 0 0 0 0
           return 0;
       }
       
      
    3. 遍历

      #include <iostream>
      #include <cstdio>
      #include <vector>
      using namespace std;
      int main(){
          vector< int> v;
          v.push_back(1);
          v.push_back(2);
          v.push_back(3);
          v.push_back(4);
          //1. 通过数组的形式遍历
          for(int i = 0; i < v.size(); i++){
              cout << v[i];
          }
          //2.通过迭代器的方式遍历后两种使用的少
          for(vector<int>::iterator it = v.begin(); it != v.end(); it++){
              cout << *it;
          }
          //3.简化迭代器的写法C++11中的特性 编译器根据初始值的类型直接判断变量的类型
          for(auto it = v.begin(); it != v.end(); it++){
              cout << *it;
          }
          //4. 使用ForEach
          for(int vt : v){
              cout << vt;
          }
          //使用引用的方式可以修改它的值
          for(int &vt : v){
              vt *= 2;
          }
          //5.同样的使用ForEach,C++11中的新特性
          for(auto it : v){
              cout << it;
          }
          return 0;
      }
      

      注意:如果之前没有了解C++的小伙伴看到第二种遍历的方法时,可能不知道vector<int>::iterator那个是什么东西,什么意思,iterator的意思就是迭代器,它就类似于指针,理解为指针就OK啦~不太清楚的就看看接下来时怎么写的 ,还有不小心遍历扯的有点多了刚刚接触的话以看遍历就这么多,那不是心态炸了其实用的最多就是第一个遍历方法,第二个在vector中不用就是为了后面的内容铺垫,后面方法也不用重点看知道有就行,第二篇我们重点谈这个内容,目前先理解 这个iterator, 用C写出遍历的意思如下

      #include <cstdio>
      //1. 用指针求和
      int getsum1(int* begin, int* end){
          int len = end - begin, sum = 0;
          for(int i = 0; i < len; i++){
              sum += begin[i];
          }
          return sum;
      }
      //2. 用指针求和
      int getsum2(int* begin, int* end){
          int sum = 0;
          for(int *it = begin; it != end; it++){
              sum += *it;
          }
          return sum;
      }
      // 这个就和那个iterator十分的类似,理解为指针就是这个意思
      void disply(int* begin, int* end){
          for(int *it = begin; it != end; it++){
              printf("%d ", *it);
          }
      }
      int main(){
          int a[5] = {1, 2, 3, 4, 5};
          printf("%d\n", getsum1(a, a + 5));
          printf("%d\n", getsum2(a, a + 5));
          disply(a, a + 5);
          return 0;
      }
      
set的使用
  • set的理解

    set 是根据元素值进行排序的集合,所插入的元素在集合中唯一,不存在重复元素

  • set的常用函数

    函数名功能复杂度
    size()返回set中元素的个数 O ( 1 ) {O(1)} O(1)
    clear()清空set O ( n ) {O(n)} O(n)
    end()返回指向末尾的迭代器 O ( 1 ) {O(1)} O(1)
    begin()返回指向开头的迭代器 O ( 1 ) {O(1)} O(1)
    erase(key)删除key O ( l o g n ) {O(log_{n})} O(logn)
    find(key)搜索key返回指向key的迭代器 O ( l o g n ) {O(log_{n})} O(logn)
    insert(key)向set中插入key O ( l o g n ) {O(log_{n})} O(logn)
  • 使用方法

    #include <iostream>
    #include <cstdio>
    #include <set>
    using namespace std;
    int main(){
        set<int> s;
        //插入
        s.insert(1);
        s.insert(2);
        s.insert(3);
        //查找元素4
        if(s.find(4) == s.end()){
            cout << "Not Exist!";
        }
        //查找 1 这个元素并输出, (s.find(1) != s.end())表示可以找到1这个元素
        cout << (s.find(1) != s.end()) << end;
        //删除2
        s.erase(2);
        //set集合的遍历,类比vector,看成指针,不理解可以看看我之前用C的那个遍历
        for(set<int>::iterator it = s.begin(); it != s.end(); it++){
            cout << *it;
        }
        //C++11新特性
        for(auto it = s.begin(); it != s.end(); it++){
            cout << *it;
        }
        //清空s中的元素
        s.clear();
        return 0;
    }
    
map的使用
  • map理解

    map就是从键(key)到值(value)的映射, map 集合以键与值的组合为元素,每个元素拥有 1 个键和 1 个值,集合以值作为排序标 准。集合中各元素的键唯一,不存在重复。map很好用,他就像增强版的数组一样,例如我这么定义map<string,int> m(string 是key, int是value),赋值的时候就可以这样m["student"] = score ,其他类型同理~

  • map的常用函数

    函数名功能复杂度
    size()返回map中元素的个数 O ( 1 ) {O(1)} O(1)
    clear()清空map O ( n ) {O(n)} O(n)
    end()返回指向末尾的迭代器 O ( 1 ) {O(1)} O(1)
    begin()返回指向开头的迭代器 O ( 1 ) {O(1)} O(1)
    erase(key)删除key O ( l o g n ) {O(log_{n})} O(logn)
    find(key)搜索key返回指向key的迭代器 O ( l o g n ) {O(log_{n})} O(logn)
    insert(key,value)向map中插入(key,value) O ( l o g n ) {O(log_{n})} O(logn)
  • map的基本使用

    #include <iostream>
    #include <cstdio>
    #include <map>
    using namespace std;
    int main(){
       //定义
        map<string,int> m;
        //添加元素
        m["steves"] = 85;
        m["alan"] = 95;
        //插入元素
        m.insert(pair<string, int>("emma", 90));
        //删除
        m.erase("emma");
        //查找
        pair<string, int> t = *m.find("alan");
        cout<< t.first << " " << t.second << endl;
        //遍历
        for(map<string, int>::iterator it = m.begin(); it != m.end(); it++){
            cout << it->first << " " << it->second << endl;
        }
        for(auto it = m.begin(); it != m.end(); it++){
            cout << it->first << " " << it->second << endl;
        }
        for(auto mt : m){
            cout << mt.first << " " << mt.second << endl;
        }
        return 0;
    }
    
stack的使用
  • stack理解

    stack 简单来说就是一个先进后出的数据结构 ,c实现栈的话就要手写,C++中引入#include <stack>即可

  • stack中常用的方法

    函数名功能复杂度
    size()返回栈的大小 O ( 1 ) {O(1)} O(1)
    top()返回栈顶元素 O ( 1 ) {O(1)} O(1)
    pop()栈顶元素弹栈 O ( 1 ) {O(1)} O(1)
    push(x)x入栈 O ( 1 ) {O(1)} O(1)
    empty()判空 O ( 1 ) {O(1)} O(1)
  • 使用方法

    stack的使用的还是非常广泛的,比如经典的图的深度遍历,我用一个简单10 转 2概括一下stack使用

    #include <iostream>
    #include <cstdio>
    #include <stack>
    using namespace std;
    int main(){
        //声明
        stack<int> s;
        //压栈
        s.push(1);
        //栈的长度
        cout << s.size() << endl;
        //弹栈
        s.pop();
        int n = 105;
        while(n > 0){
            s.push(n % 2);
            n /= 2;
        }
        // 判空
        while(!s.empty()){
            //输出栈顶元素
            cout << s.top();
            s.pop();
        }
        return 0;
    }
    
queue的使用
  • 队列的理解

    队列就是一种先进先出的数据结构,C中也需要手写,C++中引入#include <queue>即可

  • 队列的常用方法

    函数名功能复杂度
    size()返回对列的大小 O ( 1 ) {O(1)} O(1)
    front()返回队头元素 O ( 1 ) {O(1)} O(1)
    pop()取出队头元素 O ( 1 ) {O(1)} O(1)
    push(x)x入队 O ( 1 ) {O(1)} O(1)
    empty()判空 O ( 1 ) {O(1)} O(1)
    back()对位元素 O ( 1 ) {O(1)} O(1)
  • queue中的使用

    同样的queue的使用也是非常广泛的,比如图的广度遍历有兴趣可以看看,我在这里就简单演示一下啦

    #include <iostream>
    #include <cstdio>
    #include <queue>
    using namespace std;
    int main(){
        queue<int> q;
        for(int i = 0; i < 10; i++){
            //入队
            q.push(i);
        }
        cout << q.size() << endl;
        for(int i = 0; i < 10; i++){
            //队头 队尾
            cout << q.front() << " " <<q.back() << endl;
            q.pop();
        }
        if(q.empty()) cout << "Not exist any elements\n";
        return 0;
    }
    

总结

本篇主要写了由C到C++的过渡之间存在的差异,以及刷题中常常用到STL集合的基本使用,不是很全面。第二篇将对vector、set、 map的使用做个补充,以及其他类型做个全面补充例如 list unorderedmap,STL其他实用的库 例如algorithm等,还有C++11中好用的特性做一个全面的补充。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值