> ("从...获取") 3 用户...">
美文网首页
第4章: 容器与算法

第4章: 容器与算法

作者: my_passion | 来源:发表于2022-06-20 23:05 被阅读0次
    4.0 建议
        1 用 push_back() 和 back_inserter() 给 容器 添加元素
        2 在 vector 上用 push_back() 优于 数组上用 realloc()

4.1 字符串

1 6大操作

    (1/2) 连接 + += 
        将 string对象 / 字符串字面常量 / C 风格字符串 / 1个 字符 连接到 string(左操作数) 上
        
        void f(string& s1, string& s2)
        {
            s1 = s1 + '\n';
            s2 += '\n';
        }
    
    (3) 下标
        
    (4) 子串
        substr(startPos, len)
        
        len: 不算 编译器 自动添的 '\0'
        
    (5) 替换
        replace(startPos, len, targetStringObj)
        
        替换的内容 与 被替换的内容 不必一样长
        
    (6) 比较
        string 与 string/字符串字面常量
    
    string str = "hello Xi'an";
    
    void f()
    {
        string s = str.substr(6, 5); // s = "Xi'an"
        str.replace(0, 5, "how are you"); // str = "how are you Xi'an"
        str[0] = toupper(name[0]); // str = "How are you Xi'an"
    }
    
    void f(const string& answer)
    {
        if(answer == "yes") { /*  */ }
    }       

4.2 I/O 流

1 << ("放到")

    默认下, 放到 std::cout 的 `值(如 10) 被转换为 字符序列` (先后将 字符'1' 和 '0' 放到 cout )

        int b = 'b';
        cout << 'a' << b; // 输出: a98

2 >> ("从...获取")

    (1) 读 字符序列
        最简单
            读入 1个 string

    (2) 默认下, 空白符 会终止输入
    
    (3) 可用 getline() 
        读取一整行(包括 尾部 换行符)
        尾部换行符 被丢弃, 接着 再 cin 的话, 从下一行开始
        
        string str;
        cin >> str;
        
        getline(cin, str);
        cout << str << "!\n";

3 用户自定义类型 I/O

    struct Entry
    {
        string name;
        int number;
    };
    ostream& operator<<(ostream& os, const Entry& e)
    {
        return os << "{\"" << e.name << "\", " << e.number << "}"; //  {\"...\", ...}
    }

4.3 容器

1 vector
    
    (1) vector 所有元素 构成1个 范围 -> 可对其用 `范围 for 循环`

    (2) 初始化/赋值 时, vector 可被 copy
        vector<Entry> book2 = book1;
        
        要想用 移动
            需 显式指定( std::move() ) / 隐式发生 (1侧为 右值)
    
2 list

    list<Entry> phone_book = {
        {"book1", 123},
        {"book2", 456}
    }
    
    (1) 添加/删除 时 无须移动 other 元素
    
    (2) list 是 序列 => 在 list 中 `搜索` 具有给定值的元素
    
        int getNum(const string& s)
        {
            for(const auto& x: phone_book)
                if(x.name == s)
                    return x.number;
            return 0; 
        }   
        
        // <=> 
        
        int getNum(const string& s)
        {
            for(auto= = phone_book.begin(); p!= phone_book.end(); ++p)
                if(p->name == s)
                    return p->number;
            return 0; 
        }   
        
    (3) 在 list 中 定位 元素
        用 迭代器 begin() end()
    
    (4) 添加/删除
        void f(const Entry& e, list<Entry>::iterator p, list<Entry>::iterator q)
        {
            phone_book.insert(p, e); // e 插入 p 所指元素前
            phone_book.erase(q); 
        }
    
    (5) 上述例子 都可以写成 等价的使用 vector 的版本
    
        且 出乎意料 的是, 数据量较小时, vector 版本的性能 由于 list
    
3 map / 关联数组 / 字典

    (名字, 数值) 对
      key   值/映射类型
      
    map<string, int> phone_book = {
        {"book1", 123},
        {"book2", 456}
    }
    
    (1) 下标 []           
        搜索 
            找到
            没找到 -> 插入: 给定 key, 关联的值为 value 类型 的 默认值(整型: 0)
            
    (2) 避免插入无效元素
        find() 和 insert() 代替 下标
    
    (3) T(n) = O(log (n) )
    
4 unordered_map

    (1) 比 map 性能更好
        基于 (某种) 序函数 的 比较                
        
    (2) 哈希容器
        `无序` 容器: 无需 `序函数`
        
        针对 key(通常是 字符串) 搜索 进行 优化
            用 哈希表 
    
5 容器

    (1) 容器 `接口 的 一致性` 便于 设计与 容器类型无关的 `算法`
    
    (2) 下标 和 遍历 vector 高效简单
        但 插/删 时, 每次都移动元素, 效率低
        
        list 恰好有 相反特性
        
    (3) 序列较短 且 元素尺寸较小 时, vector 通常比 list 高效 (insert() erase() 也是如此 )
        默认选 vector, 除非有充分的理由, 再选其他容器

4.4 算法

    (1) 排序 vector + 将 不重复的 vector 元素 copy 到 list
        bool operator<(const Entry& x, const Entry& y)
        {
            return x.name < y.name; // Entry 对象的 序 由其 名字确定
        }
        
        void f(vector<Entry>& vec, list<Entry>& lst)
        {
            sort(vec.begin(), vec.end() ); // 用 < 确定元素的 序
            unique_copy(vec.begin(), vec.end(), lst.begin() );
        }
    
    (2) 把 不重复元素 存入 新容器, 而不是覆盖 容器中旧元素
    
        1) back_insert(标准库容器) 
        
            把元素 追加到 容器末尾, 追加过程中 `扩容`
            
            不必再用 C风格显式内存管理 realloc()
            
        2) 标准库容器 
            具有 `移动` 语义
            => `传值返回` 高效
            
        list<Entry> f(vector<Entry>& vec)
        {
            list<Entry> res;
            sort(vec.begin(), vec.end() ); 
            unique_copy(vec.begin(), vec.end(), back_inserter(res) ); // 1) 追加到 res
            return res; // 2) 移动 
        }

1 迭代器

(1) 作用
    
    分离 算法 和 容器, 使 两者 对对方 一无所知
    算法 通过 迭代器 处理 (容器中的) 数据
    
    => `数据存储 和 算法分离 的 模型` 催生出 通用、灵活的软件
    
    1) 算法 向 容器 请求 迭代器
    
    2) 容器 给 算法 提供 迭代器 begin()/end()
    
    3) 算法 通过 操作 迭代器 -> 间接 操作 容器中元素
    
(2) 在 字符串 中 查找 1个字符 出现的所有位置
        |
        | 泛化
        |/
    在 容器 c 中搜索 值 v 出现的所有位置  
    
    vector<sring::iterator> find_all(string& s, char c)
    {
        vector<sring::iterator> res;
        for(auto p = s.begin(); p!= s.end(); ++p)
            if(*p == c)
                res.push_back(p);
        return res;
    }
    
    void test()
    {
        string m{"Mary had a little lamb"};
        for(auto p: find_all(m, 'a') )
            if(*p != 'a')
                cerr << "a bug!\n"
    }
        |
        | 泛化 find_all
        |/
    template<typename C, typename V>
    vector<typename C::iterator> find_all(C& c, V v) 
    {   
        vector<typename C::iterator> res;
        for(auto p = c.begin(); p!= c.end(); ++p)
            if(*p == v)
                res.push_back(p);
        return res;
    }
            |
            |   引入 类型别名
            |/
    
    #include <vector>
    using namespace std;

    template<typename C>
    using Iterator = typename C::iterator;

    template<typename C, typename V>
    vector<Iterator<C> > find_all(C& c, V v)
    {
        vector<Iterator<C> > res;
        for (auto p = c.begin(); p != c.end(); ++p)
            if (*p == v)
                res.push_back(p);
        return res;
    }

    int main()
    {
        vector<int> vec{1, 2, 3, 3, 5};

        vector< Iterator<vector<int> > > iteVec = find_all(vec, 3);
    }

2 迭代器 类型

    每种迭代器 都与 特定类型的容器 关联

3 流 迭代器

    容器 并非容纳 元素序列 的 唯一场所
    
    1个 输入流/输出流 `产生/可放` 1个 值的序列  
        ifstream/ofstream: 可 绑定到 文件的 istream/ostream
        
    istream_iterator/ostream_iterator 允许我们将 输入流/输出流 当作 只读容器/可写容器
        istream_iterator<string> ii{cin};
        istream_iterator<string> eos{}; // 输入哨兵
        ostream_iterator<string> oi{cout};      
    
    (1) 向 标准输入 写入 的 新方法
        *oi = "hello"; // <=> cout << "hello";
        ++oi; 
    
    (2) 从文件 读 数据 -> 排序 读入的 单词 -> 对单词 去重 -> 结果写到 另一文件

        note: 认为 (文件)输入流 由 `空白符` 分隔出 各 单词
    
        1) 方法 1
            文件流 + 文件流迭代器 + vector 存 输入流中 的 `各 单词(string)`
            
        2) 方法 2
            将 string 保存到 set
            
            set 
                1> 不保留 重复值, 
                2> 自动维护 值的 顺序
            
    set<string> s{ii, eos};
    copy(vec.begin(), vec.end(), oi);
    
    #include <iostream>
    #include <fstream> // 
    #include <string>
    #include <vector>
    #include <algorithm>

    using namespace std;

    // 1) 
    int main()
    {
        string fromFileName, toFileName;
        cin >> fromFileName >> toFileName;

        // (1) 流 绑定到 文件 
        ifstream is{ fromFileName };
        ofstream os(toFileName);

        // (2) 流迭代器 初始化
        istream_iterator<string> ii(is);
        istream_iterator<string> eos{};
        ostream_iterator<string> oi{ os, "\n" };

        // (3) 算法
        vector<string> vec(ii, eos);
        sort(vec.begin(), vec.end() );
        unique_copy(vec.begin(), vec.end(), oi);

        return !is.eof() || !os; 
    }

    /* 
    D:\in.txt
    hello world
    ni hao!

    D:\out.txt
    hao!
    hello
    ni
    world
    */

4 谓词

    操作 作 算法 参数
    
    查找 满足特点要求元素 
        |
        |   更通用 的 方法
        |/
      谓词
        
        map<string, int> 中 搜索 第1个 大于42 的值
            |
            |   转化为
            |/
        在 map<string, int> 中 搜索 特定的 pair<const string, int>, 并要求其 int 部分 >  42
        
        void f(map<string, int>& m)
        {
            auto p = find_if(m.begin(), m.end(), Greater_than(42) );
            // ...
        }
        
        struct Greater_than
        {
            int val;
            Greater_than(int v): val(v) {}
            bool operator()(const pair<string, int>& p) { return p.second > val; }  
        };

5 算法

    (1) 是 操作 元素序列的 函数模板
    
    (2) <algorithm>
    
    (3) 7种 常用算法
    
    总结:
        (1) 相对于 `有/无 基准值` v 的版本, 策略版本 的 `策略对象 f 放 v 位置/最后`
        (2) 
            替换/排序 返回   = void
            
            搜索    返回值 = 目标 iterator
            
            copy/合并 返回值 = 尾   iterator, 构成结果的 迭代器区间[out, p)
    
        // 搜索: 第1个满足 *p == v / f(*p) == true 的 iterator
        p =    find(b, e, v)
        p = find_if(b, e, f)
        
        // 计数
        n =    count(b, e, v)
        n = count_if(b, e, f)
        
        // 替换: v2 时 dst
           replace(b, e, v, v2) 
        replace_if(b, e, f, v2)
        
        // copy
        p =        copy(b, e, out)
        p =     copy_if(b, e, out, f)
        p = unique_copy(b, e, out)
        p = unique_copy(b, e, out, f) // 连续重复元素, 只 copy 第1个
        
        // 排序
        sort(b, e)    // 默认 < 作 序函数
        sort(b, e, f)
        
        // 合并: 结果放 [out, p)
        p = merge(b, e, b2, e2, out)
        
        // 对 已排序序列 二分搜索, 子序列 元素值 都等于 v
        (p1, p2) = equal_range(b, e, v)

6 容器 算法

(1) 序列 
    用一对 迭代器 [begin, end) 定义
    
(2) 为 标准算法 加1层接口, 更简单

    算法 操作的序列 时 容器本身的内容 时
        sort(v.begin(), v.end() )
            |   冗长
            |
            |   简化
            |       将 容器版本 的 sort() 放到 自己的 namespace( Estd: 扩展std)
            |/
        sort(v)
        
    #include <algorithm>
    #include <vector>

    namespace Estd
    {
        using namespace std;

        template<typename C>
        void sort(C& c)
        {
            sort(c.begin(), c.end());
        }

        template<typename C, typename Pred>
        void sort(C& c, Pred p)
        {
            sort(c.begin(), c.end(), p);
        }
    }

    int main()
    {
        std::vector<int> vec{1, 3, 2};

        Estd::sort(vec);
    }
1.jpg 2.jpg 3.jpg

相关文章

  • 容器与算法

    顺序容器 vector、list、deque 容器内元素类型的约束:必须支持赋值和复制操作,故引用类型和IO对象无...

  • 第4章: 容器与算法

    4.1 字符串 1 6大操作 4.2 I/O 流 1 << ("放到") 2 >> ("从...获取") 3 用户...

  • 数据结构--容器汇总(java & Android)

    数据结构与算法容器概览(java)容器类框架分析(1)(java)ArrayList源码分析容器类框架分析(2)(...

  • 谐云科技|Docker容器与容器云第2版全新上线

    谐云科技 谐云科技|Docker容器与容器云第2版全新上线 自Docker容器与容器云第1版出版以来,销量达到10...

  • GeekBand STL与泛型编程 Third Week

    GeekBand STL与泛型编程 Third Week 变易算法 变易算法是指那些改变容器中对象的操作。具体包括...

  • STL漫谈

    TraitsIterator是STL的核心思想。Iterator是把针对容器的算法与容器的具体实现分离、解耦的设计...

  • 第10章:泛型算法

    #1.概述 #2.初始泛型算法只读算法写容器元素的算法重排容器元素的算法 #3.定制操作向算法传递函数lambda...

  • [算法] STL

    标准模板库STL的核心设计思想是将容器(类模板)与算法(函数模板)分开,使用迭代器封装访问容器元素的方法,容器所支...

  • Java并发编程之并发容器 CopyOnWrite,Concur

    前言 JUC 高并发容器是基于非阻塞算法(或者无锁编程算法)实现的容器类,无锁编程(Lock Free)算法主要通...

  • C++ algorithm

    非修正序列算法 非修正算法不修改容器的数值,一般进行搜索和取值操作 修正序列算法 修正算法会修改容器的内容,进行增...

网友评论

      本文标题:第4章: 容器与算法

      本文链接:https://www.haomeiwen.com/subject/owubmrtx.html