常用数据结构
队列
循环队列,固定容器大小,实现如下。
1 | #include <iostream> |
在用数组实现的非循环队列中,队满的判断条件是 tail == n,队空的判断条件是 head == tail。那针对循环队列,如何判断队空和队满呢?
队列为空的判断条件仍然是 head == tail。但队列满的判断条件就稍微有点复杂了。
就像我图中画的队满的情况,tail=3,head=4,n=8,所以总结一下规律就是:(3+1)%8=4。多画几张队满的图,你就会发现,当队满时,(tail+1)%n=head。
protobuf通信协议
包完整性
- 以特殊符号来分界,如每个包都以特定的字符来结尾(如\r\n,http的header就是),当在字节流中读取到该字符时,则表明上一个包到此为止。
- 固定包头+包体结构,包头一般时一个固定字节长度的结构,并且包头中会有一个特定的字段指定包体的大小。收包时,先接收固定字节数的头部,解出这个包的完整长度,按此长度接收包体。header+body 目前应用最多的一种包格式。
- 在序列化后的buffer前面增加一个字符流的头部,其中有一个字段存储包总长度,根据特殊字符(比如\n或者\0)判断头部的完整性。这样通常比2麻烦些,http和redis($6\r\nfoobar\r\n)采用的这种形式,收包的时候,先判断已经收到的数据中是否包含结束符,收到结束符后解析包头,解出这个包完整长度,按此长度接收包体。
协议设计
序列号:tcp只能保证数据到达,不能保证数据是否处理。
type表示协议类型,如xml,json。
版本号尽量放在前面,读取版本号的时候可以少读一些字节,如下nginx。protobuf封装后是放在body里面的。header也是定长的,就不用序列化。body做序列化即可。
http请求头为文本,但是body如果传的是jpeg就是二进制。
xml、json序列化后都是文本,protobuf序列化后就是二进制,这里指的是将对象序列化,对象里面一些变长的字段不进行序列化不方便传输。
grpc框架
采用proto buffer作为idl语言。
采用http2进行通信,body采用protobuf序列化后的二进制传输,将字段名去掉,以数字代替。
四种模式:一个端口可以对应多个service
1 | syntax = "proto3"; |
1 | // ListFeatures |
grpc用于微服务所面临的问题:
- 用户鉴权问题,鉴权方案的具体实现还包括了多种场景下的解决方案,例如:基于 JWT 或 OAuth 认证、基于多种细粒度授权方案进行授权、支持 OIDC 等。开发者需要根据具体的业务需求和安全要求,选择合适的鉴权方案。
redis配置与开发
docker
1 | docker run -itd --name redis -p 6379:6379 --restart unless-stopped redis |
redis
内存数据库、kv数据库、数据结构数据库
对象类型有哪些?底层使用了哪些数据结构
- string
- int,字符串长度小于等于20且能转成整数,set teacher 1000000,type teacher,object encoding teacher
- raw,字符串长度大于44
- embstr,字符串长度小于等于44,cpu缓存中基本单位为cacheline 64字节,sdshdr头为9字节,加上’\0’,buf的最大长度为44,set teacher 1000000a,object encoding teacher
- list
- quicklist,双向循环链表
- ziplist,压缩链表
- hash,hmset role:1001 age 30 name mark sex 1, hgetall role:1001, hget role:1001 age,散列表:指针数组+数组长度+hash函数
- dict/hashtable,节点数量大于512,或字符串长度大于64
- ziplist,压缩列表,节点数量小于等于512(hash-max-ziplist-entries),且字符串长度小于等于64(hash-max-ziplist-value)
- set,sadd set 1 2 3 4 5,object encoding set,sadd set mark,object encoding set
- intset,整数数组,元素都为整数,且节点数量小于等于512(set-max-intset-entries)
- dict/hashtable,字典,元素有一个不为整数或者数量大于512
- zset,有序的结构
- skiplist,跳表,数量大于128或者有一个字符串长度大于64
- ziplist,压缩列表,节点数量小于等于128(zset-max-ziplist-entries)且字符串长度小于等于64(zset-max-ziplist-value)
leetcode-单调栈题型
单调栈分为单调递增栈和单调递减栈
- 单调递增栈即栈内元素保持单调递增的栈
- 同理单调递减栈即栈内元素保持单调递减的栈
操作规则(下面都以单调递增栈为例) - 如果新的元素比栈顶元素大,就入栈
- 如果新的元素较小,那就一直把栈内元素弹出来,直到栈顶比新元素小
柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
1 | stack<int> st; |
搞懂递归
什么是递归
周末你带着女朋友去电影院看电影,女朋友问你,咱们现在坐在第几排啊?电影院里面太黑了,看不清,没法数,现在你怎么办?
别忘了你是程序员,这个可难不倒你,递归就开始排上用场了。于是你就问前面一排的人他是第几排,你想只要在他的数字上加一,就知道自己在哪一排了。但是,前面的人也看不清啊,所以他也问他前面的人。就这样一排一排往前问,直到问到第一排的人,说我在第一排,然后再这样一排一排再把数字传回来。直到你前面的人告诉你他在哪一排,于是你就知道答案了。
这就是一个非常标准的递归求解问题的分解过程,去的过程叫“递”,回来的过程叫“归”。基本上,所有的递归问题都可以用递推公式来表示。刚刚这个生活中的例子,我们用递推公式将它表示出来就是这样的:
1 | f(n)=f(n-1)+1 其中,f(1)=1 |
线性排序
因为这些排序算法的时间复杂度是线性的,所以我们把这类排序算法叫作线性排序(Linear sort)。之所以能做到线性的时间复杂度,主要原因是,这三个算法是非基于比较的排序算法,都不涉及元素之间的比较操作。
桶排序(Bucket sort)
核心思想是将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序。桶内排完序之后,再把每个桶里的数据按照顺序依次取出,组成的序列就是有序的了。
如果要排序的数据有 n 个,我们把它们均匀地划分到 m 个桶内,每个桶里就有 k=n/m 个元素。每个桶内部使用快速排序,时间复杂度为 O(k * logk)。m 个桶排序的时间复杂度就是 O(m * k * logk),因为 k=n/m,所以整个桶排序的时间复杂度就是 O(n*log(n/m))。当桶的个数 m 接近数据个数 n 时,log(n/m) 就是一个非常小的常量,这个时候桶排序的时间复杂度接近 O(n)。
桶排序对要排序数据的要求是非常苛刻的。要排序的数据需要很容易就能划分成 m 个桶,并且,桶与桶之间有着天然的大小顺序。这样每个桶内的数据都排序完之后,桶与桶之间的数据不需要再进行排序。