C++ part4
红黑树
references:
红黑树详细分析,看了都说好
关于红黑树(R-B tree)原理,看这篇如何
性质:
1、节点是红色或黑色
2、根节点是黑色
3、叶子节点(叶子节点均为NULL)都是黑色
4、红色节点必有两个黑色子节点
5、从任一节点到其每个叶子节点的所有简单路径都包含相同数目的黑色节点
操作:
1、旋转
左旋:节点变成右孩子的左孩子
右旋:节点变成左孩子的右孩子
2、变色
改变节点颜色
5种插入情况
删除(未学)
map,set的区别与实现
references:
nowcoder
20190801-C++中map和set的使用与区别
底层实现都是红黑树。
区别:
(1)map中的元素是key-value(关键字—值)对;set只有关键字没有值(或者说关键字就是值)。
(2)set的迭代器是const的,不允许修改值;map能修改值,不能修改键。
(3)map支持下标操作,set不支持。map可以用key做下标,map的下标运算符[ ]将关键码作为下标去执行查找,如果关键码不存在,则插入一个具有该关键码和mapped_type类型默认值的元素至map中,因此下标运算符[ ]在map应用中需要慎用,const_map不能用,只希望确定某一个关键值是否存在而不希望插入元素时也不应该使用,mapped_type类型没有默认值也不应该使用。如果find能解决需要,尽可能用find。
介绍一下STL的allocaotr
STL的分配器用于封装STL容器在内存管理上的底层细节。在C++中,其内存配置和释放如下:
new运算分两个阶段:(1)调用::operator new配置内存(重载new);(2)调用对象构造函数构造对象内容
delete运算分两个阶段:(1)调用对象希构函数;(2)调用::operator delete释放内存(重载delete)。
为了精密分工,STL allocator将两个阶段操作区分开来:内存配置有alloc::allocate()负责;内存释放alloc::deallocate()负责;对象构造由alloc::construct()负责;对象析构由alloc::destroy()负责。
#include<cstdio>
#include<memory>
#include<iostream>
using namespace std;
int main(){
int n;
allocator<string> alloc;
auto const p = alloc.allocate(n); //分配内存,p是第一个的地址
auto q = p; //q与p相当于队列head和tail
alloc.construct(q++, "123456"); //第一个对象构造
alloc.construct(q++, "654321"); //第二个对象构造
cout << *p << endl; //只能访问到第一个
cout << p[0] << endl;
cout << p[1] << endl;
while(p != q) alloc.destroy(--q); //构造的全部析构
alloc.deallocate(p, n); //释放内存
return 0;
}
STL迭代器
这个主要考察的是迭代器失效的问题。
1.对于顺序容器vector,deque来说,使用erase(itertor)后,后边的每个元素的迭代器都会失效,但是后边每个元素都会往前移动一个位置,但是erase会返回下一个有效的迭代器;
2.对于关联容器map,set来说,使用了erase(iterator)后,当前元素的迭代器失效,但是其结构是红黑树,删除当前元素的,不会影响到下一个元素的迭代器,所以在调用erase之前,记录下一个元素的迭代器即可。【实际操作中在windows平台的STL里erase也会返回下一个迭代器】
3.对于list来说,它使用了不连续分配的内存,并且它的erase方法也会返回下一个有效的iterator,因此上面两种正确的方法都可以使用。
STL的基本组成
STL由六大组件构成,容器,算法,迭代器,仿函数,适配器,和配置器。
容器:STL提供的存放数据的类
算法:处理元素的方法和操作
迭代器:为容器提供一组公共接口。
仿函数:仿函数具有泛型编程强大的威力,是纯粹抽象概念的例证。(仿函数本质就是类重载了一个operator(),创建一个行为类似函数的对象。)
class Mul{
public:
int operator()(const int &a , const int &b) {
\(\quad\) return a * b;
}
};
int main(){
\(\quad\) cout << Mul()(1, 2);
\(\quad\) return 0;
}
配接器:实现不同类之间的数据转换。(stack和queue就是从deque来的)
分配器:内存管理。
vector和list
vector底层实现是动态数组,所以有数组的特点。vector拥有一段连续的内存空间,因此支持随机访问,如果需要高效的随即访问,而不在乎插入和删除的效率,使用vector。
list底层是双向链表。list拥有一段不连续的内存空间,如果需要高效的插入和删除,而不关心随机访问,则应使用list。
迭代器的作用,为什么有了指针还需要迭代器
迭代器是访问容器的接口,他重载了很多类似指针的操作(++,--,*等),迭代器返回的是对象的引用而不是其本身。
出现的原因:
因为STL中容器都封装起来了,所以需要迭代器去访问,使得不用暴露容器的内部结构也能访问其数据。
resize和reserve
references:
STL方法中,resize()和reserve()的区别和用途
resize():改变当前容器内含有元素的数量。如果变大了,新增的为0。
reserve():指定容器能存储多少个元素。如果大于capacity,那么新找一块能分配那么大的内存空间,并复制之前的内容。如果reverse小于当前的size,那么他什么都不会做。
capacity:当前容器能存储多少个元素
n个整数的无序数组,找到每个元素后面比它大的第一个数,要求时间复杂度为O(N)
维护一个递减的单调栈。