通用单链表(一)

通用单链表

我们在学习链表的时候,大部分老师和大部分书籍都只会教我们常规的链表,此处常规是指链表的数据部分只适用单一的数据类型,对于怎么扩展到适用所有的数据类型,大多都是让学生自己去摸索,甚至根本就没有提及,可能中国的老师和作者都觉得每个学习数据结构的人都跟linux的创始人一样聪明吧!
我在学习和写代码的过程中,觉得链表两个比较重要点大多数老师和书籍都语焉不详。
1. 链表节点的结构
2. 如何动态的添加节点

下面我会尽自己所能用最通俗的语言和最简单的代码把这两个问题解释清楚,如有写的不好的地方,欢迎随时指出,谢谢!

就我所知道的,一个通用链表,可以用两种结构来实现

# 第一种:节点的数据部分用一个void类型的指针来表示
    typedef struct _node {
        struct _node *next;     /* 指针域 */
        void *data;             /* 数据域 */
    } node_t;

    /* 链表的头结点 */
    typedef struct _head {
        int length;             /* 链表长度 */
        node_t head;
    }

# 第二种:节点只包含一个指针域
    typedef struct _node {
        struct _node *next;     /* 指针域 */
    } node_t;

节点结构定义好了,最关键是如何动态添加节点,对于第一种结构:
先定义一个你需要的数据元素的数据类型,为了例子能具有通用性,这里我定义一个结构体,结构体有两个成员变量,一个int型,一个字符数组型。

    typedef struct _student {
        int id;
        char name[64];
    } student_t;
int main(void)
{
    student_t stu;
    stu.id = 1;
    strcpy(stu.name, "James");

    /* 一. 创建一个单链表  此处只是创建了一个链表的头结点并初始化 */
    head_t *list = list_create();

    /*
     二. 插入节点至该链表
     假设我现在要把main函数里面的局部变量stu动态插入到链表节点中
     分析:
     要把一个局部变量动态插入到一个链表中,需要在堆内存中为该节点动态申请一块内存
     注意是动态插入,网上好多文章都是先在main函数的栈空间中定义一个变量,然后把这块栈空间插入链表中,想象一下,假如你需要添加100个节点,你需要在main函数
     中定义100个这样的变量,是不是要骂娘了?所以这里需要在堆中创建内存,只需要在main函数中定义一个临时变量即可
     再思考一个问题,在插入的时候都需要哪些参数?
     1:插入到哪个链表  2:插入到什么位置  3:插入的数据
     现在你能在网上找到的链表插入的函数,百分之八九十都是这三个参数,但是如果要使用malloc在堆中分配内存,需要分配多大的内存呢?我们在写
     链表的插入函数时并不知道,所以需要从调用者那里传递过来,也就是我们的第四个参数,插入数据的大小, 如下:
     函数原型:list_insert(head_t *list, int pos, void *data, int datasize);
     */
    list_insert(list, 0, (void *)&stu, sizeof(student_t));

    return 0;
}

接下来也就是最重要,插入函数该如何实现:

/* 创建链表 */
head_t *list_create()
{
    head_t *list = (head_t *)malloc(sizeof(head_t));
    if ( list == NULL ) {
        printf("Error: list create failed!\n");
        return NULL;
    }
    list->length = 0;
    list->head.next = NULL;     /* 初始化时只有一个头结点,所以这里为NULL */
}

/* 动态插入节点 */
void list_insert(head_t *list, int pos, void *data, int datasize)
{
    /* 判断参数合法性 */
    if ( list == NULL || data == NULL ) {
        printf("invalid list or data point to NULL!\n");
        return;
    }
    if ( pos < 0 || pos > list->length ) {
        printf("position error!\n");
        return;
    }
    int i;
    node_t *p_cur = &list->head;    /* 定义一个辅助指针 */

    /* 1. 给新节点分配一块内存空间 内存布局如下图 */
    node_t *tmp = (node_t *)malloc(sizeof(node_t)+datasize);
    tmp->data = (void *)(tmp + 1);

    /* 2. 将参数data传递进来的数据复制给新创建节点的void *data成员, 大小为datasize */
    memcpy(tmp->data, data, datasize);

    /* 3. 插入操作 */
    for ( i=0; i<pos; i++ ) {
        p_cur = p_cur->next;
    }
    tmp->next = p_cur->next;
    p_cur->next = tmp;
    list->length++;
}

内存分配图

至于第二种结构的实现其实差不多,有兴趣的可以自己试试。自己实现和看别人的思路再去实现其实区别很大,因为中间会有一个思考的过程,这个过程的作用可能短时间体现不出来,但只要你长期坚持去思考,最后肯定会收获不一样的东西的。
此链表完整的函数实现和测试程序请移步github:

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 我可以提供一些有关如何使用C语言编写通用单链表的指导。首先,您需要定义一个通用单链表节点,它应该包含一个指向下一个节点的指针,以及一个用于存储用户数据的变量。然后,您可以定义一个函数用于创建新节点,并将其添加到单链表中。最后,您可以定义一些函数用于在单链表中插入,删除和搜索元素。 ### 回答2: 通用单链表是指能够存储任意类型数据的单链表,下面是使用C语言来实现通用单链表的简单示例。 首先,定义一个通用的结点结构体,用于表示链表中的每个节点。结点结构体包含一个数据变量(用来存储任意类型的数据)和一个指向下一个结点的指针。 ```c typedef struct Node { void *data; struct Node *next; } Node; ``` 接下来,定义一个单链表结构体,用于表示整个链表链表结构体包含一个指向头结点的指针。 ```c typedef struct LinkedList { Node *head; } LinkedList; ``` 然后,实现向链表中插入新结点的函数。该函数需要接收链表结构体和要插入的数据作为参数。 ```c void insert(LinkedList *list, void *data) { Node *newNode = (Node*) malloc(sizeof(Node)); newNode->data = data; newNode->next = NULL; if (list->head == NULL) { list->head = newNode; } else { Node *current = list->head; while (current->next != NULL) { current = current->next; } current->next = newNode; } } ``` 最后,实现遍历链表的函数,用来打印链表中的所有元素。 ```c void printLinkedList(LinkedList *list) { Node *current = list->head; while (current != NULL) { printf("%d ", *(int*)current->data); // 假设链表存储的是int类型数据 current = current->next; } printf("\n"); } ``` 通过以上操作,就可以实现一个简单的通用单链表。可以通过调用insert函数向链表中插入新元素,然后使用printLinkedList函数遍历并打印出链表中的所有元素。 注意:以上代码是一个简单示例,只能存储一种数据类型(int),实际使用中需要根据实际需要进行修改。另外,需要在程序结束时释放链表占用的内存,以避免内存泄漏。 ### 回答3: 为了使用C语言编写一个通用单链表,可以按照以下步骤进行: 1. 首先,我们需要定义一个节点结构体。这个结构体应该包含一个数据成员和一个指向下一个节点的指针。 ``` typedef struct Node{ void* data; struct Node* next; }Node; ``` 2. 然后,我们需要定义一个链表结构体。这个结构体应该包含一个指向头节点的指针以及其他相关的链表信息。 ``` typedef struct LinkedList{ Node* head; int size; //其他链表信息 }LinkedList; ``` 3. 接下来,我们可以定义一些函数来操作链表。例如,可以定义一个函数来创建一个新的链表,并初始化其相关的信息。 ``` LinkedList* createLinkedList(){ LinkedList* list = (LinkedList*)malloc(sizeof(LinkedList)); list->head = NULL; list->size = 0; return list; } ``` 4. 接着,我们可以定义一个函数来在链表的头部插入一个新的节点。 ``` void insert(LinkedList* list, void* data){ Node* newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->next = list->head; list->head = newNode; list->size++; } ``` 5. 还可以定义其他的函数来遍历链表、在指定位置插入节点、删除节点等等,以满足实际需求。 总之,使用C语言编写通用单链表可以通过定义节点和链表结构体,以及相应的操作函数来实现。这样的实现可以适用于存储任意类型的数据,并且可以方便地进行插入、删除和遍历操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值