C++学习笔记汇总

来源:互联网 发布:网页信息抓取软件 编辑:程序博客网 时间:2024/05/21 19:51

    算法课上完了,整理一下C++的一些使用上的经验技巧,该总结主要参考了以下文章/网站,首先对他们的工作成果表示感谢!

1、  @mannhello的文章:c++容器简介与比较

2、  原作者未知的文章:C++中几个容器的比较

3、  神一般的C++库函数资料网站:www.cplusplus.com,总结超级全面的,很值得一看

    再次对他们的工作成果表示感谢!

    如果发现任何错漏之处还请告知,感激不尽!

    转载请注明出处,谢谢!

1      概述

    首先是一些有关C++的使用技巧:

(1)      做算法题的时候切记换行空格,避免低级错误;

(2)      一旦某道题出现超时,如果你是用的是cin/cout,首先在main()函数第一行添加std::ios::sync_with_stdio(false);取消同步,如果还不能解决则把所有的cin/cout转换成C风格的scanf/printf;

(3)      做算法题的时候但凡排序都可以使用库函数:#include<algorithm> sort();,其头文件是 #include <algorithm>,对于结构体的大小关系你可以为其编写专门的cmp方法传入即可;具体来说,可以重载操作符,也可以编写cmp方法:

    a)重载操作符(调用 void sort( iterator start, iterator end );):

        有两张方法,一种是写在结构体之中:

    typedef struct Node{

        ……

        bool operator<(const Node& in) const{

            return data < in.data;   //大根堆(如果应用于sort则是从小到大排序)

            return data > in.data;   //小根堆(如果应用于sort则是从大到小排序)

        }

    };

        另一种是写在结构体之外:

    bool operator<(const Node& a, const Node& b){

         return a.data < b.data;  //大根堆(如果应用于sort则是从小到大排序)

         return a.data >b.data;  //小根堆(如果应用于sort则是从大到小排序)

    }

        具体调用的时候,如有vector<Node> v;,则:sort(v.begin(), v.end());;如有数组Node[] a;,则:sort(a, a+length);

    b)编写cmp方法(调用   void sort( iterator start, iterator end, StrictWeakOrdering cmp );):

    int cmp(AA x,AA y){
        retrun x.a > y.a;    //按a降序

        return x.a < y.a;    //按a升序
    }

        具体调用的时候,需要传入cmp函数:sort(v.begin(), v.end(), cmp); 以及 sort(a, a+length, cmp);

(4)      数组初始化可以这样:

    #include <memory.h>

    int a[100];

    memset(a, 0, sizeof(int)*100);  //把数组a的每个字节都赋值为“0”

    memset(a, 1, sizeof(int)*100);  //这样是不行的,会把一个int的每个字节都赋值为“1”,最后数组里存储的是16843009(1 00000001 00000001 00000001);

(5)      对于浮点数比较,需要用一点技巧:

    a > b:a > b + (1e-6)

    a >=b:a > b – (1e-6)

    a < b:a < b – (1e-6)

    a <= b:a < b + (1e-6)

    a == b:a > b – (1e-6) && a < b + (1e-6)

(6)      对精度要求比较高的时候,你可以这样获得 :#include <math.h> pi=acos(-1, 0);

(7)      如果需要自定义输入输出测试数据,可以重定向数据流到文件:

    freopen("in.txt", "r", stdin);

    freopen("out.txt", "w", stdout);

    之后使用的cin/cout都会从文件读取了;

(8)      对于大数的模,为了避免溢出可以用这个公式:(X*X % M) = (X%M * X%M) % M;

(9)      字符串转换数字

    #include <stdlib.h>

    char c[5];

    string s;

    int n = atoi(c);    int n =atoi(s.c_str());  //atoi()会扫描参数nptr字符串,如果第一个字符不是数字也不是正负号返回零,否则开始做类型转换,之后检测到非数字或结束符 /0 时停止转换,返回整型数。

    long n = atol(c);   long n =atol(s.c_str);  //atol()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时('/0')才结束转换,并将结果返回。

    double n = atof(c); double n = atof(s.c_str);  //atof()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时 ('/0')才结束转换,并将结果返回。参数nptr字符串可包含正负号、小数点或E(e)来表示指数部分,如123.456或123e-2。

(10)  对于一个字符集合,你可以这样获得他的全排列

    #include<algorithm>

    string str;

    do{

        …..//取得当前str在全排列中的下一个字符串,直接使用str即可

    }while(next_permutation(str.begin(), str.begin() + str.length()));

(11)  对于两个案例之间换行或空格的要求,可以这样子:

    bool ok = false;

    while(case){

        if(ok) cout << endl;

        ok = true;

        ……

    }

(12)  当题目出现一些类似“N个点,N-1条边,无环”“N个点,N-1条边,任意两点联通”“N个点,任意两点简单连通”,说明题目中的N就是一棵

(13)  如果一道题的复杂度在10^8以内,用暴力枚举也没关系;

(14)  对于搜索,如果已知最多步数,则可以使用DFS获得所有路径(递归);如果要求最少步数,则可以使用BFS(队列);

2      容器

2.1         所有容器共有函数及属性

2.1.1   容器中常用typedef

    以下这些typedef常用于声明变量、参数和函数返回值:

(1)          value_type     容器中存放元素的类型

(2)          reference     容器中存放元素类型的引用

(3)          const_reference 容器中存放元素类型的常量引用,这种引用只能读取容器中的元素和进行const操作

(4)          pointer     容器中存放元素类型的指针

(5)          iterator     指向容器中存放元素类型的迭代器

(6)          const_iterator     指向容器中存放元素类型的常量迭代器,只能读取容器中的元素

(7)          reverse_iterator     指向容器中存放元素类型的逆向迭代器,这种迭代器在容器中逆向迭代

(8)          const_reverse_iterator     指向容器中存放元素类型的逆向迭代器,只能读取容器中的元素

(9)          difference_type     引用相同容器的两个迭代器相减结果的类型(list和关联容器没有定义operator-)

(10)      size_type     用于计算容器中项目数和检索顺序容器的类型(不能对list检索)

2.1.2   所有标准库共有函数(除适配器外):

(1)      构造器及析构器

a)        C<T> a;      

    //默认构造函数,初始化为空

b)        C<T> a(a0);   

    //复制构造函数,初始化微现有同类容器副本

c)        C<T> a(iter1,iter2);       

    //复制构造函数,初始化为现有同类容器的一部分

d)        ~C<T>();      

    //析构函数

(2)      迭代器

a)                   iterator begin() noexcept;

            const_iterator begin() const noexcept;

    //返回C<T>::iterator或者C<T>::const_iterator,引用容器第一个元素

b)                   iterator end() noexcept;

            const_iterator end() const noexcept;

    //返回C<T>::iterator或者C<T>::const_iterator,引用容器最后一个元素后面一位

    c)        reverse_iterator rbegin()nothrow;

            const_reverse_iterator rbegin() const nothrow;

    //返回C<T>::reverse_iterator或者C<T>::const_reverse_iterator,引用容器最后一个元素

d)                   reverse_iterator rend() nothrow;

            const_reverse_iterator rend() const nothrow;

    //返回C<T>:: reverse_iterator或者C<T>::const_reverse_iterator,引用容器第一个元素前面一位

e)        const_iterator cbegin() constnoexcept;

    //仅用于C++11,返回C<T>::const_iterator,引用容器第一个元素

f)         const_iterator cend() constnoexcept;

    //仅用于C++11,返回C<T>::const_iterator,引用容器最后一个元素后面一位

g)        const_reverse_iterator crbegin() const noexcept;

    //仅用于C++11,返回C<T>:: const_reverse_iterator,引用容器最后一个元素

h)        const_reverse_iterator crend()const noexcept;

    //仅用于C++11,返回C<T>:: const_reverse_iterator,引用容器第一个元素前面一位

(3)      功能函数

a)        bool empty() const noexcept;

    //如果容器为空,则返回true,否则返回false

b)        size_type max_size() const;

    //返回容器中最大元素个数

c)        size_type size() const;

    //返回容器当前元素个数

(4)      容器操作函数

a)        iterator insert (const_iteratorposition, const value_type& val);

    //插入数据到指定位置,返回新插入的数据中的第一个的迭代器,除这个插入函数外还有其他很多重载函数

b)        iterator erase (const_iterator position);

           iterator erase (const_iterator first, const_iterator last);

    //删除容器中指定的某个值或者一个范围,注意:范围的概念是[first,last)

c)        void swap (C<T>& x);

    //将当前容器与传入的容器x交换数据

d)        void clear() noexcept;

    //删除当前容器中的所有元素,也即将size清为0

2.2         顺序容器

2.2.1   所有顺序容器共有函数

    以下是所有顺序容器所共有的函数:

(1)      元素访问函数

a)                  reference front();

           const_reference front() const;

    //返回容器中第一个元素的引用,注意:和begin()方法返回迭代器不同,他返回的是元素的直接引用

b)                  reference back();

           const_reference back() const;

    //返回容器中最后一个元素的引用,注意:和end()方法返回迭代器不同,它返回的是元素的直接引用

(2)      容器操作函数

a)        void push_back (constvalue_type& val);

           void push_back (value_type&& val);

    //插入元素到容器末尾

b)        void pop_back();

    //将容器最后一个元素删除

2.2.2   vector

(1)      使用比较

    就是动态数组,它也是在堆中分配内存,元素连续存放,有保留内存,如果减少大小后内存也不会释放。如果需要的容量大于当前大小时才会再分配内存,对最后元素操作最快(在后面添加删除最快 )。访问方面,对任何元素的访问都是O(1),也就是是常数的。所以vector常用来保存需要经常进行随机访问的内容,并且不需要经常对中间元素进行添加删除操作。

    vector的迭代器在内存重新分配时将失效(它所指向的元素在该操作的前后不再相同)。当把超过capacity()-size()个元素插入 vector中时,内存会重新分配,所有的迭代器都将失效;否则,指向当前元素以后的任何元素的迭代器都将失效。当删除元素时,指向被删除元素以后的任何 元素的迭代器都将失效。

(2)      功能函数

a)        void resize (size_type n);

           void resize (size_type n, value_type val);

    //将容器的size变化成新的大小,如果n比当前size要小,则从容器后部开始删除元素;否则扩充size,可以指定填充的元素的初始值

b)        size_type capacity() constnoexcept;

    //返回分配给当前容器的存储空间大小,并不一定和size相等

c)        void reserve (size_type n);

    //为当前容器重新指定capacity的大小,如果n比当前capacity要大,则增加容量;否则什么事情都不做

d)        void shrink_to_fit();

    //仅用于C++11,硬性将容器的capacity调整为size的大小

(3)      元素访问函数

a)                  reference operator[] (size_typen);

           const_reference operator[] (size_type n) const;

    //相当于数组下标

b)                  reference at (size_type n);

           const_reference at (size_type n) const;

    //作用和数组下标一样,但是超出容量时会抛出out_of_range异常

c)                 value_type* data() noexcept;

           const value_type* data() const noexcept;

    //仅用于C++11,返回容器第一个元素的指针,类似于数组的头指针,用法也和数组头指针一样

2.2.3   deque

(1)         使用比较

    双端队列,也是在堆中保存内容的。它的保存形式如下: [堆1] [堆2] [堆3]…… 每个堆保存好几个元素,然后堆和堆之间有指针指向,看起来像是list和vector的结合品。deque可以让在前面或是在后面快速地添加删除元素,然后还可以有比较高的随机访问速度,但是随机访问速度比不上vector快,因为它要内部处理堆跳转。deque也有保留空间,由于deque不要求连续空间,所以可以保存的元素比vector更大,还有就是在前面和后面添加元素时都不需要移动其它块的元素,所以性能也很高。

    增加任何元素都将使deque的迭代器失效。在deque的中间删除元素将使迭代器失效。在deque的头或尾删除元素时,只有指向该元素的迭代器失效。

(2)         功能函数

a)        void resize (size_type n);

           void resize (size_type n, value_type val);

    //将容器的size变化成新的大小,如果n比当前size要小,则从容器后部开始删除元素;否则扩充size,可以指定填充的元素的初始值

b)        void shrink_to_fit();

    //仅用于C++11,deque的size一般来说可以比实际存放的元素数量要多,该方法强制调整内存使用量为已经存放元素的大小

(3)         元素访问函数

a)                  reference operator[] (size_typen);

           const_reference operator[] (size_type n) const;

//相当于数组下标

b)                  reference at (size_type n);

           const_reference at (size_type n) const;

    //作用和数组下标一样,但是超出容量时会抛出out_of_range异常

(4)         容器操作函数

a)        void push_front (constvalue_type& val);

            void push_front (value_type&& val);

    //插入元素到容器第一位

b)        void pop_front();

    //删除容器第一位的元素

2.2.4   list

(1)      使用比较

    双向环状链表,元素也是在堆中存放,每个元素都是放在一块内存中,但list没有空间预留,所以每分配一个元素都会从内存中分配,每删除一个元素都会释放它占用的内存。由于数据结构是链表,所以不能随机访问一个元素,但是在开头、末尾和中间任何地方增加或删除元素所需时间都为常量。

    增加任何元素都不会使迭代器失效。删除元素时,除了指向当前被删除元素的迭代器外,其它迭代器都不会失效。

(2)      容器操作函数

a)         void push_front (constvalue_type& val);

            void push_front (value_type&& val);

    //插入元素到容器第一位

b)         void pop_front();

    //删除容器第一位的元素

c)         void resize (size_type n);

            void resize (size_type n, value_type val);

    //将容器的size变化成新的大小,如果n比当前size要小,则从容器后部开始删除元素;否则扩充size,可以指定填充的元素的初始值

2.3         关联容器

2.3.1   所有关联容器共有函数

(1)      访问函数

a)         const_iterator find (constvalue_type& val) const;

                      iterator find (const value_type& val);

    //查找对应的值/key值,如果找到则返回其迭代器,如果找不到则返回end()

b)        size_type count (constvalue_type& val) const;

    //查找指定值/key值的数量,返回0或者1

c)                   iterator lower_bound(constvalue_type& val);

            const_iterator lower_bound (const value_type& val) const;

    或者

                      iterator lower_bound(const key_type& k);

            const_iterator lower_bound (const key_type& k) const;

    //返回容器中第一个值/key值大于或等于val的迭代器

d)                   iterator upper_bound(constvalue_type& val);

            const_iterator upper_bound (const value_type& val) const;

    或者

            iterator upper_bound (const key_type& k);

            const_iterator upper_bound (const key_type& k) const;

    //返回容器中第一个值/key值大于val的迭代器

e)        pair<const_iterator,const_iterator> equal_range (const value_type& val) const;

                                pair<iterator,iterator>equal_range(const value_type&val);

    或者

            pair<const_iterator,const_iterator> equal_range (constkey_type& k) const;

                                 pair<iterator,iterator>equal_range(const key_type& k);

    //返回容器中与传入值/key值相等的元素的范围,返回的是一个pair值,它的first值就是lower_bound,second值就是upper_bound

2.3.2   set

(1)      使用比较

    内部数据结构为红黑树的平衡二叉检索树,按照键进行排序存储, 值必须可以进行比较, 可以理解为set就是键和值相等的map,不允许键值重复,元素默认按升序排列。此外,它所有的操作都可以在O(log n)时间复杂度内完成,效率非常高。

    如果迭代器所指向的元素被删除,则该迭代器失效。其它任何增加、删除元素的操作都不会使迭代器失效。

2.3.3   multiset

(1)      使用比较

    除了可以存储重复键值以外,其他情况和set相同。

2.3.4   map

(1)      使用比较

    内部元素以<键,值>对的形式存储,键值不能重复,元素默认按键的升序排列。可以这样赋值一个对:m.insert(map<string, int>::value_type(“count”, 100));,或者使用更为简单的数组下表符号“[]”。读取的时候,对于一个迭代器iter,可以这样指定:iter->first; iter->second,或者这样:*iter.first;*iter.second;。

    如果迭代器所指向的元素被删除,则该迭代器失效。其它任何增加、删除元素的操作都不会使迭代器失效。

(2)      访问函数

a)         mapped_type& operator[](const key_type& k);

            mapped_type& operator[] (key_type&& k);

    //就当他是数组这样用,但要注意,一旦使用了“[]”,如果此时没有对应的key值,将会产生一次插入操作

2.3.5   multimap

(1)      使用比较

    除了可以使用重复键值,其他情况和map一样。

2.4         容器适配器

2.4.1   所有容器适配器共有函数

(1)      功能函数

a)        bool empty ( ) const;

    //如果适配器为空,则返回true,否则返回false

b)        size_type size ( ) const;

    //返回适配器中已有的元素数量

2.4.2   stack

(1)      使用比较

    栈适配器,它可以将任意类型的序列容器转换为一个堆栈,一般使用deque作为支持的序列容器。元素只能后进先出(LIFO),不能遍历整个stack。

(2)      功能函数

a)                   value_type& top ( );

            const value_type& top ( ) const;

    //获取栈顶元素,并不弹出

b)        void push ( const T& x );

    //将元素压入栈

c)        void pop ( );

    //将栈顶元素弹出,并不返回

2.4.3   queue

(1)      使用比较

    队列适配器,它可以将任意类型的序列容器转换为一个队列,一般使用deque作为支持的序列容器。元素只能先进先出(FIFO),不能遍历整个queue。

(2)      功能函数

a)                  value_type& front ( );

            const value_type& front ( ) const;

    //获取队列首部元素,并不弹出

b)                  value_type& back ( );

            const value_type& back ( ) const;

    //获取队列尾部元素,并不弹出

c)                  void push ( const T& x );

    //添加新元素到队列中

d)                  void pop ( );

    //将队首元素弹出,并不返回

2.4.4   priority_queue

(1)      使用比较

    优先队列适配器,它可以将任意类型的序列容器转换为一个优先级队列,一般使用vector作为底层存储方式。只能访问第一个元素,不能遍历整个priority_queue,第一个元素始终是优先级最高的一个元素。

    其内部数据结构是堆,具体使用上来说,对于一个结构体,如果想要做一个堆,可以在结构体内部或者外部重载符号“<”即可:

    typedef struct Node{

        ……

        bool operator<(const Node& in) const{

            return data < in.data;   //大根堆(如果应用于sort则是从小到大排序)

            return data > in.data;   //小根堆(如果应用于sort则是从大到小排序)

        }

    };

    bool operator<(const Node& a, const Node& b){

         return a.data < b.data;  //大根堆(如果应用于sort则是从小到大排序)

        return a.data >b.data;  //小根堆(如果应用于sort则是从大到小排序)

    }

    但是如果想要同时构建最大堆以及最小堆,就需要传入定制的cmp结构:

    typedef struct Job{ int prio;int age};

    struct cmpBigHeap{ //建立大根堆(优先值高的队首)

        bool operator()(const Job& a, const Job& b){ return a.prio< b.prio;}

    };

    struct cmpSmallHeap{  //建立小根堆(优先值低的在队首)

        bool operator()(const Job& a, const Job& b){ return a.prio >b.prio;}

    };

    priority_queue<Job, vector<Job>, cmpBigHeap> bigHeap;

    priority_queue<Job, vector<Job>, cmpSmallHeap > smallHeap;

(2)      功能函数

a)        const value_type& top ( )const;

    //获取优先级队列中优先级最高的元素

b)        void push ( const T& x );

    //将新元素放入优先队列中

c)        void pop ( );

    //将优先级最高的元素弹出队列

3      输入输出流

【未完待续】