用数组方式实现的链表

链表是一种常用的线性表,可以快捷的进行插入和删除。但在常规的做法中,这些操作需要有动态内存分配的支持。偏偏有些编程环境,想要动态内存分配的话,需要额外添加一些库文件,比较繁琐。如何在没有动态内存分配函数的情况下,实现链表的随机删除和插入特性呢?

其实解决方法很简单,既然系统中不存在malloc和free函数,我们就自己实现一个简易的就行了。由于假设链表中,每个节点都是同类型的元素,所以这里自制的node_malloc和node_free其实远没有真正的复杂,但是又能够满足链表的需要。

我们事先定义一个节点数组,下标可从0~LEN-1。规定,数组元素存在"被占用"和"空闲"状态。

node_malloc函数,就是从0~LEN-1逐个搜索,看哪个元素是"空闲"状态,就标记它为“被占用”,然后返回它的地址。

node_free函数更加简单,把节点的状态赋值为"空闲"即可。

首先,定义一个链表的节点。

typedef struct node

{

    int No;

    int is_free;

    struct node *pre;

    struct node *next;

}node;

注意,is_free元素代表这个节点是否是空闲状态。

 

再定义自己的node_malloc和node_free函数。

typedef node elemtype;

#define NODE_MEM_FREE   0

 

elemtype* node_malloc(elemtype* mem_start,elemtype* mem_end)

{

    elemtype* mem;

    for(mem = mem_start;mem<=mem_end;mem++)

    {

        if(mem->is_free == NODE_MEM_FREE)

        {

            mem->is_free = !NODE_MEM_FREE;

            return mem;

        }

    } 

   return 0;

}

 

void node_free(elemtype* mem_free)

{

    mem_free->is_free = NODE_MEM_FREE;

}

这样就能使用内存分配和释放了。

要注意的是,使用node_malloc的使用方法。

1.先定义一个数组,node Array[LEN];

2.node_malloc可从Array中进行内存分配。需要传入开始内存地址和结尾内存地址,如node_malloc(&Array[0],&Array[LEN-1]);

3.node_malloc返回的地址,所代表的空间肯定是一个节点的大小。所以无需指定内存分配长度。

4.不可以释放没有分配过内存的地址,即不要对没有node_malloc的地址进行node_free。

有了这两个函数,就能像平时编写链表一样去调用内存分配和释放了。这样实现的链表,并不能像常规的链表一样,占用的内存可以根据节点数目而变化。但尽管如此,这样的链表依然可以实现随机新增和删除,这才是主要的。

下面实现一些基本的链表操作:新建,遍历,新增,删除,插入。

elemtype* list_Create(elemtype* mem_start,elemtype* mem_end)

{

    elemtype* list_head = node_malloc(mem_start,mem_end);

    list_head->pre = list_head->next = 0;

    return list_head;

}

 

typedef void(p_node_fun)(elemtype* node);

int  list_each(elemtype* list_head,p_node_fun node_fun)

{

    elemtype* list = list_head;

    int list_len = 0;

    while(list!=0)

    {

        list_len++;

        node_fun(list);

        list = list->next;

    }

    return list_len;

}

 

elemtype* list_add(elemtype* list_head,elemtype* node)

{

    elemtype* list = list_head;

    if(list == 0)

    {

        list_head = node;

        node->pre = 0;

    }

    else

    {

        while(list->next!=0)

        {

            list = list->next;

        }

        list->next = node;

        node->pre = list;

    }

 

    node->next = 0;

 

    return list_head;

}

 

elemtype* list_delete(elemtype* list_head,elemtype* node)

{

    elemtype* pre_node = node->pre;

    elemtype* next_node = node->next;

    if(pre_node != 0)

    {

        pre_node->next = next_node;

    }

    else

    {

        list_head = next_node;

    }

    if(next_node != 0)

    {

        next_node->pre = pre_node;

    }

 

    return list_head;

}

 

void list_insert(elemtype* list_head,elemtype* node_insert_start,elemtype* node)

{

    elemtype* next_node = node_insert_start->next;

    node->pre = node_insert_start;

    node_insert_start->next = node;

    node->next = next_node;

    if(next_node!=0)

    {

        next_node->pre = node;

    }

}

注意定义了一个函数指针的数据类型p_node_fun。它可以定义遍历链表的时候,对每个节点做的操作。

有了以上的函数后,我们可以新建一个main.c文件,对链表的接口函数进行测试。以下是一些使用链表的示范代码。

#define LEN  10

elemtype List_Mem[LEN];

void fun_node_view(elemtype* node)

{

    printf("node->No:%d\n",node->No);

}

void main()

{

    int i = 0,list_len = 0;

    elemtype* my_list,* node,* temp;

    my_list = list_Create(&List_Mem[0],&List_Mem[LEN-1]);

 

    for(i=1;i<LEN/2;i++)

    {

        node = node_malloc(&List_Mem[0],&List_Mem[LEN-1]);

        node->No = i;

        list_add(my_list,node);

    }

    printf("create list and add 4 nodes\n");

    list_len = list_each(my_list,fun_node_view);

    printf("list_len = %d \n",list_len);

 

    printf("delete 1 node: <2> \n");

    temp = my_list->next->next;

    list_delete(my_list,temp);

    node_free(temp);

    list_len = list_each(my_list,fun_node_view);

    printf("list_len = %d \n",list_len);

    

    printf("add 1 node : <5> \n");

    node = node_malloc(&List_Mem[0],&List_Mem[LEN-1]);

    node->No = 5;

    list_add(my_list,node);

    list_len = list_each(my_list,fun_node_view);

    printf("list_len = %d \n",list_len);

 

    printf("insert 1 node : behind <3> ,insert <6> \n");

    node = node_malloc(&List_Mem[0],&List_Mem[LEN-1]);

    node->No = 6;

    temp = my_list->next->next;

    list_insert(my_list,temp,node);

    list_len = list_each(my_list,fun_node_view);

    printf("list_len = %d \n",list_len);

 

}

输出结果截图如下。

另外,使用链表要注意以下几点:

1.时刻保证链表的长度不大于内存数组长度LEN;

2.经过list_delete之后的节点,要进行node_free,避免浪费内存。

3.对某个节点进行list_delete时,比如list_delete(head,head->next)。之后,千万不要执行node_free(head->next)。因为这个时候,head->next已经改变了,这样释放的根本不是被node_delete删除的节点。正确的做法是先用一个中间变量保存待删除节点的地址,list_delete和node_free都只传入这个中间变量。详情见示范代码的标红处。

4.list_delete返回值是头节点地址,这是为了防止删除的节点刚好是头节点的情况。此时头节点将后移。list_add同理。

 

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值