10.23学习笔记

剑指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[];
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值