zoukankan      html  css  js  c++  java
  • 临摹帖

    素材

    https://github.com/wangzhione/temp/tree/master/code/skiplist

    https://github.com/redis/redis/blob/unstable/src/t_zset.c

     

    临摹帖

    server.h

    #pragma once
    
    #define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^64 elements */
    #define ZSKIPLIST_P 0.25      /* Skiplist P = 1/4 */
    
    /* ZSETs use a specialized version of Skiplists */
    typedef struct zskiplistNode {
        char * ele;
        double score;
        struct zskiplistNode * backward;
        struct zskiplistLevel {
            struct zskiplistNode * forward;
            unsigned long span;
        } level[];
    } zskiplistNode;
    
    typedef struct zskiplist {
        struct zskiplistNode * header, * tail;
        unsigned long length;
        int level;
    } zskiplist;

    t_zset.c

    /*
     * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
     * Copyright (c) 2009-2012, Pieter Noordhuis <pcnoordhuis at gmail dot com>
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *
     *   * Redistributions of source code must retain the above copyright notice,
     *     this list of conditions and the following disclaimer.
     *   * Redistributions in binary form must reproduce the above copyright
     *     notice, this list of conditions and the following disclaimer in the
     *     documentation and/or other materials provided with the distribution.
     *   * Neither the name of Redis nor the names of its contributors may be used
     *     to endorse or promote products derived from this software without
     *     specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     * POSSIBILITY OF SUCH DAMAGE.
     */
    
    /*-----------------------------------------------------------------------------
     * Sorted set API
     *----------------------------------------------------------------------------*/
    
    /* ZSETs are ordered sets using two data structures to hold the same elements
     * in order to get O(log(N)) INSERT and REMOVE operations into a sorted
     * data structure.
     *
     * The elements are added to a hash table mapping Redis objects to scores.
     * At the same time the elements are added to a skip list mapping scores
     * to Redis objects (so objects are sorted by scores in this "view").
     *
     * Note that the SDS string representing the element is the same in both
     * the hash table and skiplist in order to save memory. What we do in order
     * to manage the shared SDS string more easily is to free the SDS string
     * only in zslFreeNode(). The dictionary has no value free method set.
     * So we should always remove an element from the dictionary, and later from
     * the skiplist.
     *
     * This skiplist implementation is almost a C translation of the original
     * algorithm described by William Pugh in "Skip Lists: A Probabilistic
     * Alternative to Balanced Trees", modified in three ways:
     * a) this implementation allows for repeated scores.
     * b) the comparison is not just by key (our 'score') but by satellite data.
     * c) there is a back pointer, so it's a doubly linked list with the back
     * pointers being only at "level 1". This allows to traverse the list
     * from tail to head, useful for ZREVRANGE. */
    
    #include "server.h"
    #include "struct.h"
    
    /*-----------------------------------------------------------------------------
     * Skiplist implementation of the low level API
     *----------------------------------------------------------------------------*/
    
    /* Create a skiplist node with the specified number of levels.
     * The SDS string 'ele' is referenced by the node after the call. */
    zskiplistNode * zslCreateNode(int level, double score, char * ele) {
        zskiplistNode * zn = malloc(
            sizeof(struct zskiplistNode) + level * sizeof(struct zskiplistLevel)
        );
        if (!zn) {
            return NULL;
        } 
        zn->score = score;
        zn->ele = ele;
        return zn;
    }
    
    /* Create a new skiplist. */
    zskiplist * zslCreate(void) {
        int j;
        zskiplist * zsl = malloc(sizeof(struct zskiplist));
        if (!zsl) {
            return NULL;
        }
    
        zsl->level = 1;
        zsl->length = 0;
        zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL, 0, NULL);
        if (!zsl->header) {
            free(zsl);
            return NULL;
        }
        for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {
            zsl->header->level[j].forward = NULL;
            zsl->header->level[j].span = 0;
        }
        zsl->header->backward = NULL;
        zsl->tail = NULL;
        return zsl;
    }
    
    /* Free the specified skiplist node. The referenced SDS string representation
     * of the element is freed too, unless node->ele is set to NULL before calling
     * this function. */
    void zslFreeNode(zskiplistNode * node) {
        free(node->ele);
        free(node);
    }
    
    /* Free a whole skiplist. */
    void zslFree(zskiplist * zsl) {
        zskiplistNode * next;
        zskiplistNode * node = zsl->header->level[0].forward;
    
        free(zsl->header);
        while (node) {
            next = node->level[0].forward;
            zslFreeNode(node);
            node = next;
        }
        free(zsl);
    }
    
    /* Returns a random level for the new skiplist node we are going to create.
     * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL
     * (both inclusive), with a powerlaw-alike distribution where higher
     * levels are less likely to be returned. */
    int zslRandomLevel(void) {
        int level = 1;
        while ((random()&0xFFFF) < (ZSKIPLIST_P*0xFFFF) && ++level < ZSKIPLIST_MAXLEVEL)
            ;
        return level;
    }
    
    /* Insert a new node in the skiplist. Assumes the element does not already
     * exist (up to the caller to enforce that). The skiplist takes ownership
     * of the passed SDS string 'ele'. */
    zskiplistNode * zslInsert(zskiplist * zsl, double score, char * ele) {
        zskiplistNode * update[ZSKIPLIST_MAXLEVEL], * x;
        unsigned long rank[ZSKIPLIST_MAXLEVEL];
        int i, level;
    
        assert(!isnan(score));
        x = zsl->header;
        for (i = zsl->level-1; i>=0; i--) {
            /* store rank that is crossed to reach the insert position */
            rank[i] = i == zsl->level-1 ? 0 : rank[i+1];
            while (
                x->level[i].forward && (
                    x->level[i].forward->score < score || 
                        (x->level[i].forward->score == score && 
                            strcmp(x->level[i].forward->ele, ele) < 0)
                )
            ) {
                rank[i] += x->level[i].span;
                x = x->level[i].forward;
            }
            update[i] = x;
        }
    
        /* we assume the element is not already inside, since we allow duplicated
         * scores, reinserting the same element should never happen since the
         * caller of zslInsert() should test in the hash table if the element is
         * already inside or not. */
        level = zslRandomLevel();
        if (level > zsl->level) {
            for (i = zsl->level; i < level; i++) {
                rank[i] = 0;
                update[i] = zsl->header;
                update[i]->level[i].span = zsl->length;
            }
            zsl->level = level;
        }
        x = zslCreateNode(level, score, ele);
        if (!x) {
            return NULL;
        }
        for (i = 0; i < level; i++) {
            x->level[i].forward = update[i]->level[i].forward;
            update[i]->level[i].forward = x;
    
            /* update span covered by update[i] as x is inserted here */
            x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);
            update[i]->level[i].span = (rank[0] - rank[i]) + 1;
        }
    
        /* increment span for untouched levels */
        for (i = level; i < zsl->level; i++) {
            update[i]->level->span++;
        }
    
        x->backward = (update[0] == zsl->header) ? NULL : update[0];
        if (x->level[0].forward)
            x->level[0].forward->backward = x;
        else
            zsl->tail = x;
        zsl->length++;
        return x;
    }
    
    /* Internal function used by zslDelete, zslDeleteRangeByScore and
     * zslDeleteRangeByRank. */
    void zslDeleteNode(zskiplist * zsl, zskiplistNode * x, zskiplistNode ** update) {
        int i;
        
        for (i = 0; i < zsl->level; i++) {
            if (update[i]->level[i].forward == x) {
                update[i]->level[i].span += x->level[i].span - 1;
                update[i]->level[i].forward = x->level[i].forward;
            } else {
                update[i]->level[i].span -= 1;
            }
        }
        if (x->level[0].forward) {
            x->level[0].forward->backward = x->backward;
        } else {
            zsl->tail = x->backward;
        }
        while (zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL)
            zsl->length--;
        zsl->length--;
    }
    
    /* Delete an element with matching score/element from the skiplist.
     * The function returns 1 if the node was found and deleted, otherwise
     * 0 is returned.
     *
     * If 'node' is NULL the deleted node is freed by zslFreeNode(), otherwise
     * it is not freed (but just unlinked) and *node is set to the node pointer,
     * so that it is possible for the caller to reuse the node (including the
     * referenced SDS string at node->ele). */
    int zslDelete(zskiplist * zsl, double score, char * ele, zskiplistNode ** node) {
        zskiplistNode * update[ZSKIPLIST_MAXLEVEL], * x;
        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 && 
                            strcmp(x->level[i].forward->ele, ele) < 0))) {
                x = x->level[i].forward;
            }
            update[i] = x;
        }
    
        /* We may have multiple elements with the same score, what we need
         * is to find the element with both the right score and object. */
        x = x->level[0].forward;
        if (x && score == x->score && strcmp(x->ele, ele) == 0) {
            zslDeleteNode(zsl, x, update);
            if (!node)
                zslFreeNode(x);
            else
                *node = x;
            return 1;
        }
        return 0; /* not found */
    }
    
    /* Update the score of an element inside the sorted set skiplist.
     * Note that the element must exist and must match 'score'.
     * This function does not update the score in the hash table side, the
     * caller should take care of it.
     *
     * Note that this function attempts to just update the node, in case after
     * the score update, the node would be exactly at the same position.
     * Otherwise the skiplist is modified by removing and re-adding a new
     * element, which is more costly.
     *
     * The function returns the updated element skiplist node pointer. */
    zskiplistNode * zslUpdateScore(zskiplist * zsl, double curscore, char * ele, double newscore) {
        zskiplistNode * update[ZSKIPLIST_MAXLEVEL], * x;
        int i;
    
        /* We need to seek to element to update to start: this is useful anyway,
         * we'll have to update or remove it. */
        x = zsl->header;
        for (i = zsl->level-1; i >= 0; i--) {
            while (x->level[i].forward &&
                    (x->level[i].forward->score < curscore ||
                        (x->level[i].forward->score == curscore && 
                            strcmp(x->level[i].forward->ele, ele) < 0))) {
                x = x->level[i].forward;
            }
            update[i] = x;
        }
    
        /* Jump to our element: note that this function assumes that the
         * element with the matching score exists. */
        x = x->level[0].forward;
        assert(x && curscore == x->score && strcmp(x->ele, ele) == 0);
    
        /* If the node, after the score update, would be still exactly
         * at the same position, we can just update the score without
         * actually removing and re-inserting the element in the skiplist. */
        if ((!x->backward || x->backward->score < newscore) && 
            (!x->level[0].forward || x->level[0].forward->score > newscore)) {
            x->score = newscore;
            return x;
        }
    
        /* No way to reuse the old node: we need to remove and insert a new
         * one at a different place. */
        zslDeleteNode(zsl, x, update);
        zskiplistNode * newnode = zslInsert(zsl, newscore, x->ele);
        /* We reused the old node x->ele SDS string, free the node now
         * since zslInsert created a new one. */
        x->ele = NULL;
        zslFreeNode(x);
        return newnode;
    }

     

  • 相关阅读:
    Java实现 蓝桥杯 历届试题 小计算器
    事实证明,应用市场是个流量的生意(产品能力并不足以形成护城河)
    迅雷创始人程浩:创业公司5招做好内部创新(组建小型敢死队:一共3个人,一个产品经理,两个研发;腾讯做不做这个项目是一个伪命题;让用户来验证,而不是相反 good)
    核心思想:创业者要做正确的决定,而非容易的决定(享受创业路上的孤单,你必须要有将自己关在小屋子里独自做重大且艰难决定的勇气)
    Delphi能通过SSH登录Linux,连接MYSQL取数么?像Navicat一样
    Qt5.7中使用MySQL Driver(需要把libmysql.dll文件拷贝到Qt的bin目录中。或者自己编译的时候,链接静态库)
    c++对象内存布局的理解
    观察者模式
    本地事务和分布式事务工作
    弱引用
  • 原文地址:https://www.cnblogs.com/life2refuel/p/15217985.html
Copyright © 2011-2022 走看看