剑指Offer
以后全都用java实现了,其实c++和java的基本语法也没差多少。
-
1.输入二叉树A和B,判断B是否是A的子结构。(空树不为任何树子结构)。
public class Solution {
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
if(root1 == null || root2 == null) return false;
if(isSub(root1, root2) || HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2))
return true;
return false;
}
public boolean isSub(TreeNode root1, TreeNode root2)
{
if(root2 == null) return true;
if(root1 == null) return false;
if(root1.val == root2.val)
if(isSub(root1.left, root2.left) && isSub(root1.right, root2.right))
return true;
return false;
}
}
java对逻辑值有一个布尔类型,布尔字面值只有两个逻辑值:true和false。true和false的值不转换为任何数字表示,java中true的真正字面值不等于1,false的字面值也不等于0。在java中,它们只能分配给声明为boolean的变量。
java.lang.NullPointerException是空指针错误,一般是对象为空而引发的错误。
java里函数叫方法,是真tm别扭啊。。。。。。
Redis跳跃表
1.跳跃表删除所有给定排位内的节点
/* Delete all the elements with rank between start and end from the skiplist.
*
* 从跳跃表中删除所有给定排位内的节点。
*
* Start and end are inclusive. Note that start and end need to be 1-based
*
* start 和 end 两个位置都是包含在内的。注意它们都是以 1 为起始值。
*
* 函数的返回值为被删除节点的数量。
*
* T = O(N)
*/
unsigned long zslDeleteRangeByRank(zskiplist *zsl, unsigned int start, unsigned int end, dict *dict) {
zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
unsigned long traversed = 0, removed = 0;
int i;
// 沿着前进指针移动到指定排位的起始位置,并记录所有沿途指针
// T_wrost = O(N) , T_avg = O(log N)
x = zsl->header;
for (i = zsl->level-1; i >= 0; i--) {
//从上往下检索,逐渐增加精度,搜索更快
while (x->level[i].forward && (traversed + x->level[i].span) < start) {
traversed += x->level[i].span;
x = x->level[i].forward;
}
update[i] = x;
}
// 移动到排位的起始的第一个节点
traversed++;
x = x->level[0].forward;
// 删除所有在给定排位范围内的节点
// T = O(N)
while (x && traversed <= end) {
//看清楚优先级,循环条件是x不为空并且节点还在给定范围内
// 记录下一节点的指针
zskiplistNode *next = x->level[0].forward;
// 从跳跃表中删除节点
zslDeleteNode(zsl,x,update);
// 从字典中删除节点
dictDelete(dict,x->obj);
// 释放节点结构
zslFreeNode(x);
// 为删除计数器增一
removed++;
// 为排位计数器增一
traversed++;
// 处理下个节点
x = next;
}
// 返回被删除节点的数量
return removed;
}
2.查找给定score和obj的节点的排位
/* Find the rank for an element by both score and key.
*
* 查找包含给定分值和成员对象的节点在跳跃表中的排位。
*
* Returns 0 when the element cannot be found, rank otherwise.
*
* 如果没有包含给定分值和成员对象的节点,返回 0 ,否则返回排位。
*
* Note that the rank is 1-based due to the span of zsl->header to the
* first element.
*
* 注意,因为跳跃表的表头也被计算在内,所以返回的排位以 1 为起始值。
*
* T_wrost = O(N), T_avg = O(log N)
*/
unsigned long zslGetRank(zskiplist *zsl, double score, robj *o) {
zskiplistNode *x;
unsigned long rank = 0;
int i;
// 遍历整个跳跃表
x = zsl->header;
for (i = zsl->level-1; i >= 0; i--) {
// 遍历节点并对比元素
while (x->level[i].forward &&
(x->level[i].forward->score < score ||
// 比对分值
(x->level[i].forward->score == score &&
// 比对成员对象
compareStringObjects(x->level[i].forward->obj,o) <= 0))) {
//注意这边的判断条件是小于等于0,所以循环结束后,如果表中存在包含给定分值和对象的节点,x即是要寻找的节点。
// 累积跨越的节点数量
rank += x->level[i].span;
// 沿着前进指针遍历跳跃表
x = x->level[i].forward;
}
/* x might be equal to zsl->header, so test if obj is non-NULL */
// 必须确保不仅分值相等,而且成员对象也要相等
// T = O(N)
if (x->obj && equalStringObjects(x->obj,o)) {
return rank;
}
}
// 没找到
return 0;
}
3.根据排位查找节点
/* Finds an element by its rank. The rank argument needs to be 1-based.
*
* 根据排位在跳跃表中查找元素。排位的起始值为 1 。
*
* 成功查找返回相应的跳跃表节点,没找到则返回 NULL 。
*
* T_wrost = O(N), T_avg = O(log N)
*/
zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) {
zskiplistNode *x;
unsigned long traversed = 0;
int i;
// T_wrost = O(N), T_avg = O(log N)
x = zsl->header;
for (i = zsl->level-1; i >= 0; i--) {
// 遍历跳跃表并累积越过的节点数量
while (x->level[i].forward && (traversed + x->level[i].span) <= rank)
{
traversed += x->level[i].span;
x = x->level[i].forward;
}
// 如果越过的节点数量已经等于 rank
// 那么说明已经到达要找的节点
if (traversed == rank) {
return x;
}
}
// 没找到目标节点
return NULL;
}
4.给定min和max,分析开闭,保存在spec中
Redis每个对象都是由一个redisObject结构表示,该结构中和保存数据有关二点三个属性是type属性、encoding属性和ptr属性。
typedef struct redisObject {
// 刚刚好 32 bits
// 对象的类型,字符串/列表/集合/哈希表
unsigned type:4;
// 未使用的两个位
unsigned notused:2; /* Not used */
// 编码的方式,redis 为了节省空间,提供多种方式来保存一个数据
// 譬如:“123456789” 会被存储为整数 123456789
unsigned encoding:4;
// 当内存紧张,淘汰数据的时候用到
unsigned lru:22; /* lru time (relative to server.lruclock) */
// 引用计数
int refcount;
// 指向底层数据结构的数据指针
void *ptr;
} robj;
- 编码:encoding属性主要是记录了对象所使用的编码,即这个对象用什么样的数据结构作为底层实现。每种类型的对象都至少使用了两种不同的编码。通过encoding属性来设定对象所使用的编码,而不是为特定类型的对象关联一种固定的编码,极大地提升了Redis的灵活性和效率 ,因为 Redis可以根据不同的使用场景来为一个对象设置不同的编码,从而优化对象在某一场景下的效率。
/* Populate the rangespec according to the objects min and max.
*
* 对 min 和 max 进行分析,并将区间的值保存在 spec 中。
*
* 分析成功返回 REDIS_OK ,分析出错导致失败返回 REDIS_ERR 。
*
* T = O(N)
*/
static int zslParseRange(robj *min, robj *max, zrangespec *spec) {
char *eptr;
// 默认为闭区间
spec->minex = spec->maxex = 0;
/* Parse the min-max interval. If one of the values is prefixed
* by the "(" character, it's considered "open". For instance
* ZRANGEBYSCORE zset (1.5 (2.5 will match min < x < max
* ZRANGEBYSCORE zset 1.5 2.5 will instead match min <= x <= max */
/* 如果给定的min或者max第一个字符是'('那么就是开区间,否则是闭区间*/
if (min->encoding == REDIS_ENCODING_INT) {
// min 的值为整数,开区间
spec->min = (long)min->ptr;
} else {
// min 对象为字符串,分析 min 的值并决定区间
if (((char*)min->ptr)[0] == '(') {
// T = O(N)
spec->min = strtod((char*)min->ptr+1,&eptr);//strtod是把第一个参数指向的字符串转换为一个浮点数。如果&eptr不为空,则指向转换中最后一个字符后的字符的指针会存储在其引用的位置。
if (eptr[0] != '\0' || isnan(spec->min)) return REDIS_ERR;
spec->minex = 1;
} else {
// T = O(N)
spec->min = strtod((char*)min->ptr,&eptr);
if (eptr[0] != '\0' || isnan(spec->min)) return REDIS_ERR;
}
}
if (max->encoding == REDIS_ENCODING_INT) {
// max 的值为整数,开区间
spec->max = (long)max->ptr;
} else {
// max 对象为字符串,分析 max 的值并决定区间
if (((char*)max->ptr)[0] == '(') {
// T = O(N)
spec->max = strtod((char*)max->ptr+1,&eptr);
if (eptr[0] != '\0' || isnan(spec->max)) return REDIS_ERR;
spec->maxex = 1;
} else {
// T = O(N)
spec->max = strtod((char*)max->ptr,&eptr);
if (eptr[0] != '\0' || isnan(spec->max)) return REDIS_ERR;
}
}
return REDIS_OK;
}
跳跃表的API算是都看完了。
SDSAPI
1.数据结构
/*
* 保存字符串对象的结构
*/
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
};