剑指Offer二刷
- 1.顺时针一圈圈的打印矩阵
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
int lenC = matrix[0].size();
int lenR = matrix.size();
int left = 0, right = lenC-1, top = 0, bottom = lenR-1;
vector<int> res;
if(lenC==0 && lenR==0) return res;
while(left<=right && top<= bottom)
{
for(int i = left; i <= right; i++)
res.push_back(matrix[top][i]);
for(int i = top+1; i <= bottom; i++)
res.push_back(matrix[i][right]);
if(top!=bottom)
for(int i = right-1; i >= left; i--)
res.push_back(matrix[bottom][i]);
if(left!=right)
for(int i = bottom-1; i >= top+1; i--)
res.push_back(matrix[i][left]);
left++; right--; top++; bottom--;
}
return res;
}
};
果然看懂和自己敲还是敲,自己一敲总是发现各种各样的问题。我又把矩阵的横纵坐标写成x,y坐标了,又反了!!!
-
2.定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
class Solution {
public:
void push(int value)
{
s1.push(value);
if(s2.empty()) s2.push(value);
else if(value<=s2.top()) s2.push(value);
}
void pop()
{
if(s1.empty()) return;
if(s1.top() == s2.top()) s2.pop();
s1.pop();
}
int top()
{
return s1.top();
}
int min()
{
return s2.top();
}
private:
stack<int> s1, s2;
};
这个有点意思,主要是栈是动态变化的,要时刻输出最小值就必须专门再建一个栈。
- 3.给定入栈顺序和出栈序列,判断是否是出栈序列。
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
stack<int> st1;
int pos = 0;
if(pushV.size() == 0 && popV.size() == 0) return true;
for(int i = 0; i < pushV.size(); i++)
{
st1.push(pushV[i]);
while(!st1.empty() && pos < pushV.size() && st1.top() == popV[pos])
{
pos++;
st1.pop();
}
}
if(st1.empty()) return true;
return false;
}
};
模拟出栈即可。又碰上老问题了,加加还是慎用啊,千万不能用在逻辑复杂的语句里。
Redis
static inline内联函数
内联函数的代码会被直接嵌入到它被调用的地方,调用几次就嵌入几次,没有使用call指令。这样省去了函数调用时的一些开销,比如保存和恢复函数返回地址等,可以加快速度。不过调用次数多的话,会使可执行文件变大,这样会降低速度。stactic inline的内联函数,一般情况下不会产生函数本身的代码,而是全部被嵌入在被调用的地方。如果不加static,则表示该函数有可能被其他编译单元所调用,所以一定会产生函数本身的代码。加了static,一般可以令可执行文件变小。linux内核里使用的inline函数大多被定义为static inline类型。
size_t类型
在标准c库中的许多函数使用的参数或者返回值都是表示的用字节表示的对象大小,比如说malloc(n)函数的参数n指明了需要申请的空间大小,还有memcpy(s1,s2,n)的最后一个参数,表明需要复制的内存大小,strlen(s)函数的返回值表明了以'\0'结尾的字符串的长度(不包括'\0')。这些参数和返回值在C标准中都被定义为size_t。C++库中经常会使用一个相似的类型size_type,用的可能比size_t还多。最主要是size_t的大小足以保证存储内存中对象的大小。
SDS调用属性
/*
* 返回 sds 实际保存的字符串的长度
*
* T = O(1)
*/
static inline size_t sdslen(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
return sh->len;
}
/*
* 返回 sds 可用空间的长度
*
* T = O(1)
*/
static inline size_t sdsavail(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
return sh->free;
}
SDS 数据结构
/*
* 类型别名,用于指向 sdshdr 的 buf 属性
*/
typedef char *sds;
/*
* 保存字符串对象的结构
*/
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
};
像SDS这种有一个成员以上的的结构体的最后一个元素如果为不完整的数组类型,称为灵活数组成员。一般在计算结构体空间大小的时候,会忽略灵活数组成员的大小。所以sizeof(struct sdshdr)的大小即len和free的类型大小和,即sizeof(unsigned int) + sizeof(unsigned int)。
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
使用sdsnewlen函数创建一个新的sds时,Redis会创建一个struct sdshdr sh结构体,并令结构体的最后一个字符数组,即sh->buf返回。这里的s保存的即buf数组头的地址,所以s-(sizeof(struct sdshdr))即sh这个sdshdr*类型的结构体的首地址,在经过struct sdshdr*类型转换,就可以得到sds对应的struct sdshdr结构体了。
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
// 根据是否有初始化内容,选择适当的内存分配方式
// T = O(N)
if (init) {
// zmalloc 不初始化所分配的内存
sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
} else {
// zcalloc 将分配的内存全部初始化为 0
sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
}
// 内存分配失败,返回
if (sh == NULL) return NULL;
// 设置初始化长度
sh->len = initlen;
// 新 sds 不预留任何空间
sh->free = 0;
// 如果有指定初始化内容,将它们复制到 sdshdr 的 buf 中
// T = O(N)
if (initlen && init)
memcpy(sh->buf, init, initlen);
// 以 \0 结尾
sh->buf[initlen] = '\0';
// 返回 buf 部分,而不是整个 sdshdr
return (char*)sh->buf;
}