几个线性数据结构的简单实现-list/set/queue

学《elementary-algorithms》的PART 1需要用到,所以就自己简单实现了一下,用的是C语言,数据结构的操作也不全,只是实现了需要用到的部分。代码风格借鉴了之前学过的一个开源2D引擎-Orx Engine,前缀wtl是字母WangTaL的缩写,是廖望塔的意思。代码在codeblocks17下编译通过,gcc7编译不过。(2018.5.19更新-要在gcc下编译通过,加上参数“-fms-extensions”)。


list.h单链表的头文件

#ifndef _LIST_H_
#define _LIST_H_

// 单链表的节点结构,只支持存放无符号长整形数据
typedef struct _wtlNODE {
    struct _wtlNODE* next;
    unsigned long value;
} wtlNODE;
// 单链表结构,只有一个元素,header中的value用于记录链表的长度,next指向实际的节点
typedef struct _wtlLIST {
    struct _wtlNODE* header;
} wtlLIST;
// 创建一个单链表
wtlLIST* wtlList_Create(void);
// 销毁一个单链表,注意传入的是个二级指针,而不是一级指针
void wtlList_Destroy(wtlLIST** list);
// 获取单链表中第index个节点的值,从0开始
unsigned long wtlList_GetValue(wtlLIST* list, int index);
// 获取单链表的长度,也就是节点个数
int wtlList_Length(wtlLIST* list);
// 创建一个存放value的节点
wtlNODE* wtlNode_Create(unsigned long value);
// 销毁一个节点,也是传入二级指针
void wtlNode_Destroy(wtlNODE** node);

#endif // _LIST_H_

list.c单链表的实现文件

#include <stdlib.h>
#include <assert.h>
#include "list.h"

wtlLIST* wtlList_Create(void){
    wtlLIST* list = malloc(
        sizeof(*list) + sizeof(wtlNODE) // 这样申请内存是为了便于一次释放
    );

    assert(list != NULL);

    // 申请内存的时候多申请了一个wtlNODE的大小,所以让header指向这部分就行了
    // 这样就不用调用wtlNode_Create()为header单独申请内存
    list->header = (wtlNODE*)(list + 1);
    //初始化header的内容
    list->header->next = NULL;
    list->header->value = 0;

    return list;
}

void wtlList_Destroy(wtlLIST** list){
    if (NULL == *list) {
        return;
    }

    // 该节点指向链表中实际的节点
    wtlNODE* node = (*list)->header->next;
    // 如果该节点不为空
    while (node != NULL) {
        // 就让链表的next指向node的next,这样就不会丢失后面的节点信息
        (*list)->header->next = node->next;
        // 同时释放node的内存
        wtlNode_Destroy(&node);
        // 再将node赋值为header的next
        node = (*list)->header->next;
    } // 重复完整个循环后,链表的所有节点的内存就被释放了,不包括header这个特殊节点
    // 最后释放list指向的内存,包括header这个特殊节点,因为是一起申请的
    free(*list);
    // 注意,如果不是二级指针的话,就无法改变list指向的内容,为了安全起见,让它指向空
    // 就像形参传普通变量无法改变它的值一样,为了改变它的值,得传它的指针
    // 为了改变指针的内容,就得传指针的指针
    *list = NULL;
}

unsigned long wtlList_GetValue(wtlLIST* list, int index){
    // 链表为空,返回-1
    if (NULL == list) {
        return -1;
    }

    int len = (int)(list->header->value);
    wtlNODE* current = list->header->next;

    // 链表不为空,但是链表不存在任何节点
    if (NULL == current) {
        return -1; // 这里返回-1似乎也不合理,万一链表中要存入-1这个值呢...不过也不知道返回什么值合理
    }
    // 如果index小于0,直接返回第一个节点的值
    if (index < 0) {
        return current->value;
    }

    for (int i = 0; i < index && i < len - 1; i++) {
        current = current->next;
    }

    return current->value;
}



int wtlList_Length(wtlLIST* list){
    return (int)(list->header->value);
}

wtlNODE* wtlNode_Create(unsigned long value){
    wtlNODE* node = malloc(sizeof(*node));

    assert(node != NULL);

    node->next  = NULL;
    node->value = value;

    return node;
}

void wtlNode_Destroy(wtlNODE** node){
    if (*node != NULL) {
        free(*node);
        *node = NULL;
    }
}

在单链表的基础上加上特有的操作,形成一个有序正整数集。差不多是用到了继承的思想吧。
set.h有序正整数集的头文件

#ifndef _SET_H_
#define _SET_H_

#include "list.h"
//集合的结构
typedef struct _wtlSET {
    // 匿名结构体用法
    // 这样做是为了使用的时候可以直接set->header,而不是set->list->header
    struct _wtlLIST;
} wtlSET;

wtlSET* wtlSet_Create(void);

void wtlSet_Destroy(wtlSET** set);
// 向集合中插入一个元素,可保证集合中元素的唯一性和有序性
void wtlSet_Insert(wtlSET* set, unsigned long value);
// 获取集合的第一个元素,且会释放该节点
unsigned long wtlSet_GetFirst(wtlSET* set);

int wtlSet_Length(wtlSET* set);

#endif /* _SET_H_ */

set.c集合的实现部分

#include <stdlib.h>
#include <assert.h>
#include "set.h"
// 集合的创建,直接调用链表的创建函数,因为set的结构和list的结构是一样的
wtlSET* wtlSet_Create(void){
    return (wtlSET*)wtlList_Create();
}
// 集合的销毁,道理同创建函数一样
void wtlSet_Destroy(wtlSET** set){
    wtlLIST* p = (wtlLIST*)(*set);
    wtlList_Destroy(&p);
}

int wtlSet_Length(wtlSET* set){
    return wtlList_Length((wtlLIST*)set);
}

void wtlSet_Insert(wtlSET* set, unsigned long value){
    assert(set != NULL);
    // current指向header,而不是header的next
    wtlNODE* current = set->header;
    while (current) {
        // 如果集合一个元素都不包含
        if (NULL == current->next) {
            // 直接将该元素作为第一个元素插入
            wtlNODE* node = wtlNode_Create(value);
            current->next = node;
            (set->header->value)++;
            // 结束循环
            break;
        }
        // 如果集合包含元素了,就按下面的步骤插入元素
        wtlNODE* next = current->next;
        // 将要插入的元素已存在集合中,就不插入了
        if (next->value == value) {
            break;
        }
        // 不存在则找到合适的位置插入
        if (next->value > value) {
            wtlNODE* node = wtlNode_Create(value);
            node->next = next;
            current->next = node;
            (set->header->value)++;

            break;
        }

        current = next;
    }
}

unsigned long wtlSet_GetFirst(wtlSET* set){
    wtlNODE* first = set->header->next;
    unsigned long value = 0;

    if (NULL == first) {
        return value;
    }

    value = first->value;
    set->header->next = first->next;
    (set->header->value)--;
    wtlNode_Destroy(&first);

    return value;
}


同样的,在单链表的基础上增加特有的操作,形成一个队列。
queue.h队列的头文件

#ifndef _QUEUE_H_
#define _QUEUE_H_

#include "list.h"
// 队列的结构
typedef struct _wtlQUEUE {
    // 继承自list,该用法在set部分有讲
    struct _wtlLIST;
    // 增加一个tail指针用于指向尾节点,使元素的入列操作的时间复杂度达到为O(1)
    wtlNODE* tail;
} wtlQUEUE;

wtlQUEUE* wtlQueue_Create(void);

void wtlQueue_Destroy(wtlQUEUE** queue);
// 元素的入列操作
void wtlQueue_Push(wtlQUEUE* queue, unsigned long value);
// 元素的出列操作
unsigned long wtlQueue_Pop(wtlQUEUE* queue);

int wtlQueue_Length(wtlQUEUE* queue);
// 获取队列第一个元素,该操作不会删除节点信息
unsigned long wtlQueue_Front(wtlQUEUE* queue);

#endif /* _QUEUE_H_ */

queue.c队列的实现文件

#include <stdlib.h>
#include <assert.h>
#include "queue.h"
// 队列结构多了一个tail指针,不能直接调用list的创建函数了
wtlQUEUE* wtlQueue_Create(void){
    wtlQUEUE* queue = malloc(
        sizeof(*queue) + sizeof(wtlNODE) * 2 // 多申请两个wtlNODE的内存给header和tail
    );

    assert(queue != NULL);

    queue->header = (wtlNODE*)(queue + 1); // 使header指向正确的位置
    queue->tail = (wtlNODE*)(queue->header + 1); // 使tail指向正确的位置
    queue->header->value = 0;
    queue->header->next = NULL;
    queue->tail->next = NULL;

    return queue;
}
// 队列的销毁也不能直接调用链表的销毁操作
void wtlQueue_Destroy(wtlQUEUE** queue){
    if (NULL == *queue) {
        return;
    }
    // 下面这个循环只是释放了队列的node
    wtlNODE* node = (*queue)->header->next;
    while (node) {
        (*queue)->header->next = node->next;
        wtlNode_Destroy(&node);
        node = (*queue)->header->next;
    }
    // 这里才释放队列的结构,包括header和tail
    free(*queue);
    *queue = NULL;
}
// 元素的入列操作,该操作不会保证元素的唯一性和有序性
void wtlQueue_Push(wtlQUEUE* queue, unsigned long value){
    assert(queue != NULL);

    wtlNODE* node = wtlNode_Create(value);

    if (NULL == queue->header->next) {
        queue->header->next = node;
        queue->tail->next = node;
        (queue->header->value)++;

        return;
    }

    wtlNODE* last = queue->tail->next;
    last->next = node;
    queue->tail->next = node;
    (queue->header->value)++;
}

unsigned long wtlQueue_Pop(wtlQUEUE* queue){
    assert(queue != NULL);

    unsigned long value = -1;
    wtlNODE* first = queue->header->next;

    if (NULL == first) {
        return value;
    }

    queue->header->next = first->next;
    value = first->value;
    wtlNode_Destroy(&first);
    (queue->header->value)--;

    return value;
}

int wtlQueue_Length(wtlQUEUE* queue){
    return (int)wtlList_Length((wtlLIST*)queue);
}

unsigned long wtlQueue_Front(wtlQUEUE* queue){
    return wtlList_GetValue((wtlLIST*)queue, 0);
}

以上就是个人对几个线性数据结构的简单实现了,注释不是特别详细,学过数据结构的应该看下就能懂。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值