STL学习笔记

这篇笔记暂时只是一部分,后续的部分会慢慢贴出来。有错误之处还望大神指教

1,容器

(1)vector

vector要求<vector>的头文件包含,实际的实现是在<stl_vector.h>中。

vector的初始化方式:

1,直接初始化空:

vector():start(0),end(0),end_of_storage(0){};

ex: vector<int> vec;

2,初始化并赋值

vector<int/double/long/decimal/float etc.> vec(【int/long】num,【value】);

表示初始化vector含有一定num数量的元素,后面的value指的是这些元素同时赋予相应的值,此时的value是一个const常量。

3,直接初始化个数

vector<int> vec(num);

这样就初始化了一个一定num数量的vector

vector的操作:

*.front();//返回 *begin();

*.back();//返回*(end()-1);

*.push_back(const T& x);//最常用的尾部插入操作;

*.pop_back();//删除最后一个元素

*.erase(iterator position);//删除迭代器指向对象,然后返回指向下一个元素的迭代器对象;

*.resize(size_type new_size,const T& x);//重新调整vector的大小,如果new_size大于原来的空间,就在保留原来数据的基础上扩展相应大小的空间并在新拓展的空间内赋予x的值;如果new_size的大小小于原来的空间,就从begin()开始erase这new_size个空间的值,剩下的保持不变。

*.resize(size_type new_size);//调用上面的函数;

*.clear();//擦除begin到end的所有的值;

vector的属性:

begin()和end()是最常用的vector的属性;

这里要注意一个问题,vector的capacity()和end()是不一样的。capacity是指的所有的空间,包括未赋值的空间,end()则指的是所有已经赋值的空间的末尾。

还有,vector的迭代器在进行插入/删除之后可能失效,这一点非常重要。

vector增加空间大小并不是另开辟一块大于等于已使用空间大小的空间,而是另找一个大于等于原来空间的2倍的空间来存放元素。在找到这个空间之后,将原来的数据拷贝过去。所以,vector进行多数据的频繁的插入和删除操作时候效率更低一些。这就是造成迭代器失效的根本原因所在。但是vector进行查询的效率更高。

2,list

STL的list不同于普通的list,它直接就是一个双向链表。list的节点定义是这样的:

template <class T>

struct __list_node{

typedef void * void pointer;

void_pointer prev;

void_pointer next;

T  data;

};

所以list在这里是一个双向链表,而且是一个循环链表。用一个指针就能同时表示出链表的头和尾,因为有next和prev指针;

list的元素操作:

push_front(const T& x);//从头部插入

push_back(const T& x);//从尾部插入、

erase(iterator position);//擦除制定位置的值

pop_front();

pop_back();

clear();//清楚整个链表

remove(const T& value);//将数值为value的所有元素移除,这个比较nice

unique();//移除【连续而相同的】元素,只剩下一个,一定要满足相应的条件

transfer(iterator position,iterator first,iterator last);//将first和last之间的所有元素(包含first,但不含last)移动到position之前;

splice(iterator position, list & x);//将x这个list中的所有元素移动到position之前,这个操作是基于上面的transfer操作的;

splice(iterator positon , list & x , iterator i);//将i所指的元素结合于position之前;

最后有 merge() , reverse() , sort()操作,这些操作明显是基于transfer()的。

从list的一系列特性可以看出,list对于频繁增删的数据存储是有着明显的优势的,其各种元素操作的方法应有尽有,操作元素非常方便。这是相对于vector来讲的一个方面。此外,list的迭代器不会像vector那样很容易失效,只有在erase某个元素的时候,这个迭代器才可能失效。不过,相对于vector来讲,list在查询元素方面效率就比较低了,因为其本质上还是一个链表,需要遍历链表来查询。

3,deque

deque是这样一个容器:其容器是可增长的,可push/pop_front()也可push/pop_back(),但是其在头端的push和pop的效率奇差。

此外,deque的增长空间是连续的--在头端或者尾端插入元素的时候,如果超出了原来的空间,那么久分配新的连续空间接上原来的空间。实际上这是一个伪连续的空间,真正分配的新空间并不是连续的。之所以看起来是连续的,是因为每一个deque都有一个map来维持,map一般是一个512byte大小的空间,蛋疼的是这个空间也是变化的。这个map里面保存的就是一段段不连续空间的地址。就是这个map让deque看起来是空间连续的。

这样就有一个问题,就是在对deque进行操作的时候,迭代器的复杂度非常高。虽然看起来deque拥有了vector和list的共同的优点,但是,其效率还是比较低下;

对元素的操作:

deque有一些列对元素的操作,和list大同小异。鉴于效率的原因,可能我们并不需要考虑使用deque;

4,priority_queue

其实就是一个只准从头出,从尾进的一个容器。而且从头出来的元素,要么是所有元素中最小的,要么是所有元素中最大的。这里就用的是最大顶堆和小顶堆来实现的。

所以这里一定要用到堆heap。

heap最常用的就是在于堆排序里面。

堆排的基础就是要构建大顶堆(max tree)和小顶堆(min tree),这里的堆是一个complete binary tree(完全二叉树)。

大顶堆的特点就是父节点的值总是大于等于子节点的值,这样就保证了在整个堆中,根节点的值是最大的。每次取出根节点的值,然后将剩下的元素继续构建大顶堆,这样就完成了降序的排序。

STL中,构建heap需要的是一个存放元素的vector,和一个构建heap的算法。(使用vector的原因是其可以动态改变大小)

例如:见图

heap算法:

1,构建heap(每个左子节点的序号都是2i,每个右子节点的序号都是2i+1,其相应的父节点的序号是i/2,以此来构建二叉树);

2,维持heap;

在每次插入或者pop() heap中的元素时(插入一定在末尾,然后再调整。pop一定在堆顶,然后再调整),可能heap的形态就要发生变化,此时就要重新调整heap到正常状态,使之符合大顶堆的特性。

使用 _ajust_heap算法来维持大顶堆的形态。

5, RB-tree

红黑树是一种特殊的数据结构,不同于BST和AVL,红黑树在除了具有以上两者的特点的同时,还具有不同颜色背景的特点。

(1),RB-tree的根节点一定为黑色;

(2),节点只有黑色和红色两个颜色;

(3), 父子结点不能同时为红色;

(4),任意结点到达NULL结点之任一路经中所包含的黑色结点的数量必须相同;

红黑树在插入数据时,新增的结点一定是叶子结点,默认为红色。一般来说,插入数据都会破坏树的规则,所以必须使用旋转树形并调节结点的颜色;

插入方式分为外侧插入和内侧插入,针对不同的插入方式,在调整树形的时候需要选择单旋转和双旋转来调整树形。

RB-tree的结点定义如下:

typedef  bool _rb_tree_color_type;

const _rb_tree_color_type   _rb_tree_red = false; //red is 0

const  _rb_tree_color_type  _rb_tree_black=true;//black is 1

struct _rb_tree_node_base{

typedef _rb_tree_color_type color_type;

typedef _rb_tree_node_base *base_ptr;

color_type color;//结点颜色,非红即黑

base_ptr parent;//RB树的许多操作,必须知道父亲结点

base_ptr left;

base_ptr right;

static base_ptr mimimum(base_ptr x){

while(x->left!=0)

x=x->left;

return x;

};

static base_ptr maxmum(base_ptr x){

while(x->right!=0)

x=x->right;

return x;

};

template <class Value>

struct _rb_tree_node:public _rb_tree_node_base{

typedef _rb_tree_node<Value>* link_type;

Value value_field;//节点值

};

};

RB-tree作为一个基础的容器,是实现其他容器的基础。基本的操作就是插入和查操作。

从上面的结点可以看出,RB-tree寻找最大值和最小值非常方便。

6,set

set作为一个关联容器,保存的主要是键-值对,set中所有的元素会根据键值自动被排序,set的元素不像map一样可以同时拥有实值(value)和键值(key),set的键值就是实值,实值就是键值。所以set不允许有相同的键值。

我们不能通过set的迭代器改变set的键值,因为set的键值和实值是一样的随意改变键值会破坏set的排列规则。set的iterator是一种const iterator。

set是以RB-tree二叉搜索平衡树为基本的数据结构,因此在对其进行insert和erase操作时 iterator 不会失效,除了删除的那个迭代器。

注意:

1,set的insert,是RB-tree的insert-unique()而不是insert-equal(),因此在向set中插入已有的数值的时候相当于没有对set采取任何操作。

2,在对关联容器使用STL算法时候,最好采用容器自带的算法,例如find(),reverse()算法等。

7,map

map是键-值对关联容器,map的值可以重复,但是键不能重复,必须保持键的唯一性。因为map是根据键值进行默认的递增排序的。

map在这里类似于hash table,还有一种基于hash的 hash_map,这里的map是基于RB-tree的。

根据map的定义可知map的定义方式:

template <class key, class T

class compare=less<key>,  //缺省情况下默认为递增排序

class Alloc=alloc> //缺省

由于map是基于RB-tree的,所以,其iterator 失效的方式和RB-tree是一样的。

8,multiset

multiset 于set特性都相同,不同之处在于multiset允许键值重复,所以其在插入数据时使用的是RB-tree的insert-equal()方法,而不是insert-unique()方法。

9,multimp

multimap与map的特性完全相同,不同之处在于它允许键值重复,所以,与multiset一样,它使用的是insert-equal()方法。

注意:以上的set,map,multiset 和 multimap 使用的底层都是RB-tree,而RB-tree的底层是平衡二叉搜索树。因为平衡二叉搜索树在查询、插入和删除操作上的复杂度比较低。

10,hashtable

哈希表是一个特殊的容器,主要是使用键-值的一一对应关系来解决查询和插入、删除的平均操作时间。在哈希表足够大的时候,元素的冲突必然不可避免。一般是采用线性探测和二次探测的方法。线性探测会产生主集团的影响,二次探测虽然消除了主集团的影响,但是会产生次集团的影响。

在STL中使用开链来解决hashtable中的元素冲突问题。

开链就是用一个list来存放hashtable空间的指引,指示哪里有空闲的空间可以插入。这个list会根据‘集团’的大小改变list中的元素位置指示。

STL中的hashtbale 比较复杂,初始化的方式也很麻烦,具体的定义方式如下:

template <class Value, class key , class HashFcn,

class ExtractKey , class EqualKey ,

class Alloc>]

其中,Value--结点的实值类型

Key--结点的键值类型

HashFcn--Hash function的函数类型

ExtractKey--从结点中取出键值的方法(函数或仿函数)

EqualKey--判断键值相同与否的方法(函数或仿函数)

Alloc--空间配置器,缺省使用std::alloc

一个初始化hashtable的例子:

hashtable<int,

int,

hash<int>,

identity<int>,

equal_to<int>,

alloc>

myhashtable<50,hash<int>(),equal_to<int>());//指定保留50个buckets

11,hashtable的派生

基于hashtable的派生容器有hash_set , hash_map , hash_multiset , hash_multimap

这些容器同非hash的有相同的方法,但是有不同的实现方式。

时间: 2024-12-07 10:09:05

STL学习笔记的相关文章

STL学习笔记--&gt;初识STL

“这里要用char类型”; “这里要用int类型”; “其实实现这个方法只需要把另一个方法的返回值的类型和传入参数的类型改成float类型就实现了”; “其实这个算法只需要把以前写的那个稍微改动一下就行了”; ……………… 学过面向对象语言的都知道GP这个概念,就是泛型程序设计,说的再明白点就是编写不依赖于具体数据类型的程序,C++作为一门面向对象语言,当然也有泛型这个概念,这就不得不提STL(Standard Template Library,标准模板库),是被融入C++标准程序库里面的一个高

STL学习笔记(算法概述)

算法头文件 要运用C++标准程序库的算法,首先必须包含头文件<algorithm> 使用STL算法时,经常需要用到仿函数以及函数配接器.它们定义域<functional>头文件中. 算法的分类 可以按以下分类方式描述各个STL算法: 非变动性算法(nonmodifying algorithms) 变动性算法(modifying algorithms) 移除性算法(removing algorithms) 变序性算法(mutating algorithms) 排序算法(sorting

【stl学习笔记】deques

deque双向队列是一种双向开口的连续线性空间,可以高效的在头尾两端插入和删除元素,deque在接口上和vector非常相似.deque的实现比较复杂,内部会维护一个map(注意!不是STL中的map容器)即一小块连续的空间,该空间中每个元素都是指针,指向另一段(较大的)区域,这个区域称为缓冲区,缓冲区用来保存deque中的数据.因此deque在随机访问和遍历数据会比vector慢. deque的各项操作只有以下两点与vector不同: 1.deques不提供容量操作(capacity()和re

C++STL学习笔记_(1)string知识

/*============================================ string是STL的字符串类型,通常用来表示字符串 = ============================================*/ #include <iostream> using namespace std; #include "string"//string的头文件 #include "algorithm" /*============

STL学习笔记(string)

动机 C++标准程序库中的string class使我们可以将string当做一个一般型别.我们可以像对待基本型别那样地复制.赋值和比较string, 再也不必但系内存是否足够.占用的内存实际长度等问题. 操作函数 1.构造函数和析构函数 下表列出string的所有构造函数和析构函数 2.大小和容量 size()和length():返回string中现有的字符个数. max_size():返回一个string最多能够包含的字符数,这个跟机器本身的限制有关系. capacity():重新分配内存之

STL学习笔记(非变动性算法)

辅助函数 本节跟以后几节将对所有STL算法逐一详细讨论.为了简化这些例子,我们使用了一些辅助函数,分别用于对容器进行输出跟插入操作. for_each()算法 for_each()算法非常灵活,它可以以不同的方式存取.处理.修改每一个元素 UnaryProc for_each(InputIterator beg,InputIterator end,UnaryProc op); 1.对与区间[beg,end)中的每一个元素调用:op(elem) 2.返回op(已在算法内部被变动过)的一个副本 3.

STL学习笔记(第五章 STL组件)

STL组件 若干精心勾画的组件共同合作,构筑起STL的基础.这些组件最关键的是容器.迭代器和算法. 下图演示了STL组件之间的合作 容器(Containers) 容器类别(简称容器)用来管理一组元素.为了适应不同需求,STL提供了不同类型的容器. 总的来说,容器可分为两类: 1.序列式容器Sequence containers,此乃可序群集,其中每个元素均有固定位置(取决于插入时机和地点,和元素值无关). STL提供三个定义好的序列式容器:vector.deque和list. 2.关联式容器As

STL学习笔记--2、空间配置器 allocator

2.1标准接口 allocator::value_type allocator::pointer allocator::const_pointer allocator::reference allocator::const_reference allocator::size_type allocator::difference_type allocator::rebind allocator::allocator()//默认构造函数 allocator::allocator(const allo

STL学习笔记--3、迭代器iterator与traits编程

iterator模式:提供一种方法,依次巡访某个聚合物(容器)所含的各个元素,而无需暴露该聚合物的内部表达式. 1.迭代器设计思维 STL在于将数据容器和算法分开,彼此独立,最后再以一帖粘合剂将它们撮合在一起.只要对算法给予不同的迭代器,就可以对不同容器进行相同的操作. 算法find():接受两个迭代器和一个搜寻目标. //摘自SGI<stl_algo.h> template <class InputIterator, class T> InputIterator find(Inp