微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用C的Skiplist实现

如何解决使用C的Skiplist实现

下面是我的跳过列表实现,代码工作正常,但是我需要的是,当键与现有键中的键匹配时,用新值更新值,但是在我的情况下,该项目插入了两次,数据为不更换。我想在更新时实施此操作,这样我就不必搜索整个列表。

在这方面的任何帮助都将受到赞赏

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>

#ifndef EQ
#define EQ(a,b) (a == b)
#endif

#ifndef LTE
#define LTE(a,b) (a < b)
#endif

struct skipLink
{
    int key;
    int value;
    struct skipLink *next;
    struct skipLink *down;
};

struct skipList
{
    struct skipLink *topSentinel;
    int size;
};

/* the public interface */
void test(void);
void initSkipList(struct skipList *slst);
int containsSkipList(struct skipList *slst,int key);
void removeSkipList(struct skipList *slst,int key);
void addSkipList(struct skipList *slst,int key,int value);
int sizeSkipList(struct skipList *slst);
void printSkipList(struct skipList *slst);

/* internal routines */
int flipSkipLink();
struct skipLink *slideRightSkipList(struct skipLink *current,int key);
struct skipLink *skipLinkAdd(struct skipLink *current,int value);
struct skipLink *newSkipLink(int key,int value,struct skipLink *nextLnk,struct skipLink *downLnk);

/* ************************************************************************
Main Function
 ************************************************************************ */
/* Test function:
 param: no parameters
 pre:   no parameres
 post: prints out the contents of the skip list */

int main()
{
    int i = 0;
    /*srand(time(NULL));*/

    /*  Initialize the skip list */
    struct skipList *sl1 = (struct skipList *)malloc(sizeof(struct skipList));
    initSkipList(sl1);

    /*  Add to the skip list  M = 20 random integers in [0,100] */
    for (i = 0; i < 20; i++)
    {
        addSkipList(sl1,rand() % 101,i + 5);
    }
    addSkipList(sl1,1,9);

    /*  Print out the contents of the skip list in the breadth-first order,starting from top.
     While printing the elements,move to a new line every time you reach the end of one level,and move down to the lower level of the skip list.
     For example,the print out of a skip list with 5 elements should look like

     7
     7 14 29
     3 7 9 14 20

     */
    printf("---------- skipList 1 -----");
    printf("----- size %d -----\n",sizeSkipList(sl1));
    printSkipList(sl1);
    printf("---------- removed %d from skipList -----",sl1->topSentinel->next->key);
    removeSkipList(sl1,sl1->topSentinel->next->key);
    printf("----- size %d -----\n",sizeSkipList(sl1));
    printSkipList(sl1);

    return 0;
}

/* ************************************************************************
Internal Functions
 ************************************************************************ */

/* Coin toss function:
 param:     no parameters
 pre:   no parameres
 post: output is a random intiger number in {0,1} */
int flipSkipLink(void)
{
    return (rand() % 2);
}

/* Move to the right as long as the next element is smaller than the input key:
 param:     current -- pointer to a place in the list from where we need to slide to the right
 param: key --  input key
 pre:   current is not NULL
 post: returns one link before the link that contains the input key key */
struct skipLink *slideRightSkipList(struct skipLink *current,int key)
{
    while ((current->next != 0) && LTE(current->next->key,key))
        current = current->next;
    return current;
}

/* Create a new skip link for a key
    param: key   -- the key to create a link for
    param: nextLnk   -- the new link's next should point to nextLnk
    param: downLnk -- the new link's down should point to downLnk
    pre:    none
    post:   a link to store the key */
struct skipLink *newSkipLink(int key,struct skipLink *downLnk)
{
    struct skipLink *tmp = (struct skipLink *)malloc(sizeof(struct skipLink));
    assert(tmp != 0);
    tmp->key = key;
    tmp->value = value;
    tmp->next = nextLnk;
    tmp->down = downLnk;
    return tmp;
}

/* Add a new skip link recursively
 param: current -- pointer to a place in the list where the new element should be inserted
 param: key  -- the key to create a link for
 pre:   current is not NULL
 post: a link to store the key */
struct skipLink *skipLinkAdd(struct skipLink *current,int value)
{
    struct skipLink *newLink,*downLink;
    newLink = 0;
    if (current->down == 0)
    {
        newLink = newSkipLink(key,value,current->next,0);
        current->next = newLink;
    }
    else
    {
        downLink = skipLinkAdd(slideRightSkipList(current->down,key),key,value);
        if (downLink != 0 && flipSkipLink())
        {
            newLink = newSkipLink(key,downLink);
            current->next = newLink;
        }
    }
    return newLink;
}

/* ************************************************************************
Public Functions
 ************************************************************************ */

/* Initialize skip list:
 param:  slst -- pointer to the skip list
 pre:   slst is not null
 post: the sentinels are allocated,the pointers are set,and the list size equals zero */
void initSkipList(struct skipList *slst)
{
    assert(slst != NULL);
    slst->topSentinel = (struct skipLink *)malloc(sizeof(struct skipLink));
    slst->topSentinel->next = 0;
    slst->topSentinel->down = 0;
    slst->size = 0;
}

/* Checks if an element is in the skip list:
 param: slst -- pointer to the skip list
 param: key -- element to be checked
 pre:   slst is not null
 post: returns true or false  */
int containsSkipList(struct skipList *slst,int key)
{
    struct skipLink *current = slst->topSentinel;
    while (current)
    {
        current = slideRightSkipList(current,key);
        if ((current->next != 0) && EQ(current->next->key,key))
            return 1;
        current = current->down;
    }
    return 0;
}

/* Remove an element from the skip list:
 param: slst -- pointer to the skip list
 param: key -- element to be removed
 pre:   slst is not null
 post: the new element is removed from all internal sorted lists */
void removeSkipList(struct skipList *slst,int key)
{
    struct skipLink *current,*temp;
    current = slst->topSentinel;

    while (current)
    {
        current = slideRightSkipList(current,key))
        {
            temp = current->next;
            current->next = current->next->next;
            free(temp);
            if (current->down == NULL)
                slst->size--;
        }
        current = current->down;
    }
}

/* Add a new element to the skip list:
    param: slst -- pointer to the skip list
    param: key -- element to be added
    pre:    slst is not null
    post:   the new element is added to the lowest list and randomly to higher-level lists */
void addSkipList(struct skipList *slst,int value)
{
    struct skipLink *downLink,*newLink;
    downLink = skipLinkAdd(slideRightSkipList(slst->topSentinel,value);
    if (downLink != 0 && flipSkipLink())
    {
        struct skipLink *newTopSentinel = (struct skipLink *)malloc(sizeof(struct skipLink));
        newLink = newSkipLink(key,downLink);
        newTopSentinel->next = newLink;
        newTopSentinel->down = slst->topSentinel;
        slst->topSentinel = newTopSentinel;
    }
    slst->size++;
}

/* Find the number of elements in the skip list:
 param: slst -- pointer to the skip list
 pre:   slst is not null
 post: the number of elements */
int sizeSkipList(struct skipList *slst)
{
    return slst->size;
}

/* Print the links in the skip list:
    param: slst -- pointer to the skip list
    pre:    slst is not null and slst is not empty
    post: the links in the skip list are printed breadth-first,top-down */
void printSkipList(struct skipList *slst)
{
    struct skipLink *currentlist = slst->topSentinel;
    struct skipLink *currentlink;
    while (currentlist != NULL)
    {
        currentlink = currentlist->next;
        while (currentlink != NULL)
        {
            printf("{%d,%d}",currentlink->key,currentlink->value);
            currentlink = currentlink->next;
        }
        currentlist = currentlist->down;
        printf("\n");
        printf("\n");
    }
}

解决方法

更新现有元素

如果希望代码在插入新值时能够更新现有元素,则必须将在containsSkipList()中进行的搜索与创建新的skipLink结合起来。所以:

void addSkipList(struct skipList *slst,int key,int value)
{
    
    struct skipLink *current = slst->topSentinel;

    while (current)
    {
        /* Search as usual */
        current = slideRightSkipList(current,key);
        if (current->next != NULL && current->next->key == key)
        {
            /* If found,update the value */
            current->next->value = value;

            /* Also update all the skipLinks downwards from this point */
            ...
            return;
        }

        if (current->down == NULL)
            break;

        current = current->down;
    }

    /* current now points to the closest smaller item to key,* add the new item right after it */
    ...

    if (flipSkipLink())
    {
        /* Add to higher level-list as well */
    }
}

避免编写宏

您定义了两个不是很有用的宏,但更糟糕的是它们是不正确的。问题在于参数将在字面上扩展,这可能是一个问题。考虑这个例子,在这里我想比较一个值是否设置了给定的位:

int value = 0x01;
int mask = 0x10;
int required = 0x10;

printf("%d\n",EQ(value & mask,required));

您期望value & mask为零,不等于required,因此您希望它打印0。但是,它会打印1,因为在宏扩展之后,它会显示:

printf("%d\n",(value & mask == required));

并且由于==的{​​{3}}比&高,因此它等效于(value & (mask == required))。您要么必须修复宏:

#define EQ(a,b) ((a) == (b))

或者,如果您知道参数的类型,则编写一个函数:

bool EQ(int a,int b) { return a == b; }

但是在这种情况下,宏根本没有用,只需在代码中直接使用==<。例如,代替:

while ((current->next != 0) && LTE(current->next->key,key))

只需写:

while (current->next != NULL && current->next->key < key)

使用NULL代替0作为指针

虽然您可以将指针与0进行比较,但最好显式编写NULL。如果您想检查一个NULL指针,但无意中比较了一个非指针变量与0,这将有助于捕获错误。它将在没有任何警告的情况下进行编译,但是如果您编写了NULL,则编译器将给出警告您这是潜在的错误。

避免转发声明

通过更改源文件中定义函数的顺序,可以避免编写前向声明。这样可以避免重复,并减少潜在的错误。

制作内部例程static

可以将仅在同一.c文件内使用且在其他文件中不可见的功能设为static。这样可以避免名称空间污染,也可以帮助编译器生成更好的代码。

在适当的地方使用const指针

如果函数使用指向skipList的指针,但是您不打算修改该跳过列表,则使指针为const。这样一来,当您不小心写入跳过列表时,编译器便会生成错误,并且还可能生成更优化的代码。

为您的公共接口使用一致的前缀

为避免名称空间冲突,如果所有公共接口函数都具有公共前缀,则将有所帮助。使用操作它们的struct的名称是一个不错的选择。当前,在大多数情况下(但并非总是如此),您将其用作后缀。所以:

  • skipList_init()
  • skipList_contains()
  • ...

我建议您也将其用于内部例程,所以:

- `skipLink_add()`
- `skipLink_new()`
- ...

避免重复输入类型名称

在这一行中,您会重复输入该类型的名称三次:

struct skipList *sl1 = (struct skipList *)malloc(sizeof(struct skipList));

您可以将其重写为:

struct skipList *sl1 = malloc(sizeof *sl1);

符合标准的编译器应接受此说明,而不会发出任何警告。如果使用C ++编译器进行编译,则可能需要强制转换。

创建构造函数和析构函数

您将其留给跳过列表的用户来为struct skipList分配内存,然后让它调用initSkipList()。尽管在某些情况下这可能是有道理的,但是在完成skipList之后,还有一个问题。用户是否仅在free()变量上调用skipList?但是分配的所有skipLink呢?创建一个负责内存分配,初始化和释放的构造函数和析构函数:

struct skipList *skipList_new(void) {
    struct skipList *list = malloc(sizeof *list);
    skipList_init(list);
    return list;
}

void skipList_delete(struct skipList *list) {
    for (/* each skipLink in the list */)
         skipLink_delete(link);

    free(list);
}

在适当的地方使用bool

当某些东西返回真/假值时,请使用bool中的<stdbool.h>类型。例如:

bool skipList_contains(const struct skipList *slst,int key)
{
    const struct skipLink *current = slst->topSentinel;
    while (current)
    {
        current = skipList_slideRight(current,key);
        if (current->next != NULL && current->next->key == key)
            return true;
        current = current->down;
    }
    return false;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?