红黑树介绍
红黑树就是满足以下特性的二叉树
所有节点只有红色和黑色
红色节点的孩子都是黑色(红色节点不能相邻)
黑色节点到任意叶子节点的简单路径上经过的黑色节点数量相同
叶子节点不存储值,而且叶子节点都是黑色的 满足这样特性的二叉树高度一定不超过2log(n+1) 下面是证明 如果把红色节点全部删掉,把下面的剩下的所有节点都连接在一起,那么新产生的树就是一颗每个节点最多有四个子节点的树,计算高度可以采用满四叉树的高度来近似计算,即log(n),而把删除的红色节点加上就是2倍
红黑树相较于AVL,其查找效率略低一点,而插入删除效率高出AVL很多,所以当插入删除操作很多时可以使用红黑树
docker容器使用初体验
我们写程序时,都会搭建相关的环境,比如写了一个web,使用了tomcat、nginx等,现在想要把程序部署到云服务器或者在其他电脑上运行,就需要重新部署一遍环境,尤其是项目开源后,上手成本大。
docker介绍Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。 容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。 (来源于菜鸟教程)
安装docker依赖于linux内核,因此在windows系统中需要安装Hyper-V(类似于 VMWare 或 VirtualBox)或者WSL,然后进入docker desktop官网下载安装程序,双击运行即可。安装完成后可以在虚拟机中运行命令docker —version检查是否成功安装。
镜像和容器的区别Docker 中镜像(Image)和容器(Container)是两个核心概念,它们有以下主要区别:
定 ...
explicit关键字
explicit
作用: 防止隐式类型转换
1234567891011121314151617class Complex { private: double real, imag; public: Complex(){ } Complex(double _r, double _i): real(_r), imag(_i) { }};int main(){ Complex t = { 1, 2};}
以上代码不会报错,因为t = {1, 2}这里发生了隐式类型转换,将{1, 2}转化为了Complex(1, 2),产生了一个匿名对象,然后把匿名对象赋值给t(这种赋值只是简单的值拷贝操作,假如存在指针,这种操作有浅拷贝的隐患) 但如果在构造函数前面加上explicit关键字,以上代码无法通过编译,因为explicit不允许隐式类型转换,等号右边的类型与左边的类型不符,则编译失败
Qt 类似vscode和matlab的分屏显示效果
运行截图向右分屏 多分屏 全屏显示
介绍实现了一个类似vscode和matlab的标签页显示分屏效果,支持鼠标拖拽分屏、全屏显示,可自适应调整大小,程序把要显示的Widget独立出来,可随时替换为其他的用户自定义Widget,例如3d模型、二维画图等
存在的问题
加载图片会非常卡,猜测是paintEvent频繁调用而每次绘制图片复杂度高导致
分屏不能等分分屏,应该不难实现
源码 gitee源码
706. 设计哈希映射
706. 设计哈希映射处理冲突方法采用链表法,利用list
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960class MyHashMap { public: vector<list<pair<int, int>>> data; static const int base = 769; static int hash(int x) { return x % base; } MyHashMap(): data(base) { } void put(int key, int value) { for(auto it = data[hash(key)].begin(); it != data[hash(key)].end(); it + ...
Leetcode 48. 旋转图像
48. 旋转图像矩阵从外到内,是一圈一圈的,只要能把一圈旋转90度,内层圈找好数据关系,for循环就可以搞定每一圈 对于一圈:对于第一行(除了最后一个元素)而言,只要把这些元素旋转四次就回到了原来位置,所以只要循环四次,把中间遍历到的元素都移到下一次即将遍历的位置即可 如何把当前元素放到下一个即将访问的位置?如果只是简单的覆盖,下一个元素将丢失,容易想到的是用temp存储即将丢失的元素,但这样实现起来有许多细节很难写,用交换实现则特别简单,找一个临时变量,每次都把当前位置和临时变量进行交换,则实现了旋转操作
12345678910111213141516171819202122232425262728293031323334353637383940class Solution { public: static struct node { int x, y; node(int x, int y) { this->x = x; this->y = y; ...
operator的两种用法(重载和隐式类型转换)
重载略
隐式类型转换构造函数的隐式类型转换利用operator进行的隐式类型转换成为operator算子的隐式类型转换,讲这个之前先了解构造函数的隐式类型转换,请看以下代码
123456789101112131415class X{ public: int val; X(int _val) { //隐式类型转换 val = _val; }};int main(){ X m = 2; //等价于X m(2); return 0;}
传入一个参数初始化类的构造函数就是构造函数的隐式类型转换,可以理解为将int类型转换为X(class)类型
补充如果不想出现这种隐式类型转换,就可以用explict修饰 具体详解请看: explict详解
operator算子的隐式类型转换而operator算子的隐式类型转换则是相反的,例如以下代码
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484 ...
缓冲区浅析
缓冲区程序运行输入数据时,从键盘的输入先存储到缓冲区,只有当缓冲区满或者输入回车时程序才会真正地从缓冲区读入数据
1234567int main(){ int a, b; cin >> a >> b; return 0;}
in: 1 2\n 例如这里输入空格时程序没有输出,而是将空格也放入了缓冲区,只有输入回车时,程序才从缓冲区中拿出数据,而cin遇到回车、空格、tab时会停止读入,首先拿出1,遇到空格,停止读入,但空格并未被处理,依旧在缓冲区,所以下次读入时,先碰到空格,cin会舍弃 刚开始碰到的 空白字符(空格、tab、回车),读入2,遇到回车,停止读入,但回车依旧在缓冲区 对于上面话的理解可以看下面代码:
123456789int main(){ int a; cin >> a; char c = getchar(); cout << c; return 0;}
in_1:1空格回车 in_2:1回车 两种输入方式输出内容不一样,第二种多输出了一个空白行
这篇文章只是浅析缓冲区, ...
指针笔记(指针数组和指向数组的指针,数组中a和&a的区别等)
指针数组和指向数组的指针int p[4]和int (p)[4]有何区别? 前者是一个指针数组,数组大小为4,每一个元素都是一个指向int的指针 后者是指向int[4]类型数组的指针 以上代码若运行会报如下错误 main函数中定义的a数组本质是一个指向int[2]的指针,而函数形参则是一个指针数组,即形参的a是一个二维指针,所以会报错类型不匹配 若将形参改为int (*a)[2]则编译通过
数组中a和&a的区别假如a是一个一维数组,输出a和&a发现结果相同,都是一维数组首元素的地址,但两者虽然值相同,却是不同的 这段代码是没问题的,但若将第二行的&a改为a,则会报错 即编译器认为a是int 类型的,而&a则是int ( )[4]类型的,用大白话讲就是&a编译器可以认出a是一个数组,a则不行
123456789101112#include "stdio.h"int main(){ int a[5] = { 1,2,3,4,5 }; int *ptr = (int *)(&a ...
C++转载的文章
C++对象的内存布局 C++虚函数表解析 C++函数指针 C++动态绑定和静态绑定