徐善通的随笔

千里之行, 始于足下



使用C语言实现一个单链表


代码实现

由于手动分配内存太麻烦, 我就写了两个函数模仿其他语言new的用法

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

/**
 * 链表元素
 */ 
typedef struct node {
    int data; // 元素值
    struct node *next; // 指向下个元素
} Node;

/**
 * 链表
 */ 
typedef struct linklist {
    int length; // 链表长度
    Node *head; // 带头节点
    Node *tail; // 尾结点,通过这个节点可以使插入的时间复杂度变为O(1)
} LinkList;

// 打印链表
void printAll(const LinkList *l);
// 插入元素,尾部插入
void insert(LinkList *l, int data);
// 按索引删除元素
int delete(LinkList *l, int index);
// 按值删除匹配的第一个元素
int deleteByValue(LinkList *l, int data);
// 按值删除所有匹配的元素
int deleteAllByValue(LinkList *l, int value);
// 按索引更新元素
int update(LinkList *l, int index, int value);
// 按索引获取元素
int get(const LinkList *l, int index);
// 清空链表, 不会删除带头节点
int clear(LinkList *l);
// 销毁链表, 释放所有内存
void destory(LinkList *l); // 销毁链表
// 分配并创建链表
LinkList *newLinkList(void);
// 分配并创建链表元素
Node *newNode(int data, Node *next);

int main()
{
    LinkList *l = newLinkList();
    // 添加元素
    insert(l, 1);
    insert(l, 2);
    insert(l, 3);
    insert(l, 4);
    insert(l, 4);
    insert(l, 4);
    insert(l, 4);
    insert(l, 5);
    // 元素删除
    delete(l, 1);
    deleteByValue(l, 2);
    deleteAllByValue(l, 4);
    // 更新
    update(l, 2, 6);
    // 打印
    printAll(l);
    // 获取元素
    printf("第2个元素的值为:%d\n", get(l, 2));
    // 清空链表
    clear(l);
    // 清空之后再打印
    printAll(l);
    // 销毁
    destory(l);


    return 0;
}

/**
 * 手动创建一个new 函数来创建链表
 */ 
LinkList *newLinkList(void)
{
    LinkList *l = (LinkList *)malloc(sizeof(LinkList));
    l->length = 0;
    l->head = newNode(0, NULL);
    l->tail = l->head;
    return l;
}

/**
 * 手动创建一个new 函数来创建元素
 */ 
Node *newNode(int data, Node *next)
{
    Node *n = (Node *)malloc(sizeof(Node));
    n->data = data;
    n->next = next;
    return n;
}

/**
 * 插入一个新元素, 尾部插入
 */
void insert(LinkList *l, int data)
{
    // 创建一个新的元素
    Node *latestNode = newNode(data, l->tail->next);
    // 将新元素放在最后一个元素之后
    l->tail->next = latestNode;
    // 将尾结点指向最后一个元素
    l->tail = latestNode;
    // 更新链表长度
    l->length++;
}

/**
 * 打印链表
 */ 
void printAll(const LinkList *l)
{
    printf("\n开始打印链表。。。\n");
    int i = 1;
    if (l->length > 0) {
        // 忽略带头结点r
        Node *temp = l->head->next;

        while (temp != NULL)
        {
            printf("  ->第%d个元素的值为: %d\n", i, temp->data);
            i++;
            temp = temp->next;
        }
    }
    printf("结束打印链表。。。\n\n");
}

/**
 * 根据索引删除元素, 查到到上一个索引, 将上一个索引指向下下个元素即可
 */ 
int delete(LinkList *l, int index)
{
    if (index > l->length || index < 1) {
        return 0;
    }
    int i = 1;
    Node *temp = l->head; // 带头节点, 指向要删除的元素前一个节点
    while (i != index)
    {
        temp = temp->next;
        i++;
    }
    Node *item = temp->next; // 先保存要删除的节点
    temp->next = temp->next->next; // 更新上个节点指向下下个节点
    free(item);
    return 1;
}

/**
 * 按值删除第一个匹配的元素
 */ 
int deleteByValue(LinkList *l, int value)
{
    if (l->length < 1) {
        return 0;
    }
    Node *temp, *item;
    temp = l->head;
    // 查找要删除的第一个元素, 如果查找到最后一个元素(最后一个元素next指向NULL), 说明没有该值
    while (temp->next != NULL && temp->next->data != value)
    {
        temp = temp->next;
    }

    // 查找到了元素
    if (temp->next != NULL) {
        item = temp->next; // 要删除的元素
        temp->next = temp->next->next; // 指向下下个元素
        free(item); // 释放要删除的元素
        l->length--; // 更新链表长度
        return 1;
    }

    return 0;
}

/**
 * 按值删除所有匹配的元素
 */
int deleteAllByValue(LinkList *l, int value)
{
    if (l->length < 1) {
        return 0;
    }

    Node *temp, *item;
    temp = l->head;

    while (temp != NULL && temp->next != NULL)
    {
        if (temp->next->data == value) {
            item = temp->next;
            temp->next = temp->next->next; // 将指针指向下下个元素, 不能移动指针到下下个元素, 如果下下个元素也是要删除的元素,将会出现内存溢出和遗漏
            free(item);
            l->length--;
        } else {
            temp = temp->next;
        }
    }

    return 1;
}

/**
 * 按索引更新元素
 */
int update(LinkList *l, int index, int value)
{
    if (index > l->length || l->length < 1) {
        return 0;
    }
    Node *temp = l->head;
    // 找到要修改的节点
    // 第0次, 指向第一个节点
    // 。。。, 所以index-1个节点就是我们要修改的节点
    for (int i = 0; i < index; i++)
    {
        temp = temp->next;
    }
    temp->data = value;

    return 1;
}

/**
 * 按索引获取元素
 */ 
int get(const LinkList *l, int index)
{
    if (index > l->length || l->length < 1) {
        return -1;
    }

    Node *temp = l->head;

    for (int i = 0; i < index; i++)
    {
        temp = temp->next;
    }

    return temp->data;
}

/**
 * 清空链表元素,仅保留头结点和尾结点
 */ 
int clear(LinkList *l)
{
    if (l->length <= 0) {
        return 0;
    }
    // 从第一个元素开始删除,保留头节点
    Node *temp = l->head->next;

    while (temp != NULL)
    {
        l->tail = temp->next; // 存储下个节点的指针
        free(temp); // 释放当前节点
        temp = l->tail; // 重新指向下个节点

    }

    l->tail = l->head; // 修复尾结点指向头结点
    l->length = 0; // 重置长度
    return 1;
}

/**
 * 销毁链表
 */ 
void destory(LinkList *l)
{
    clear(l); // 先清空元素
    free(l->head); //释放头结点
    free(l);
    l = NULL; // 将链表指向NULL
}

输出结果

 shantong@mac# gcc ./linklist.c && ./a.out

开始打印链表。。。
  ->1个元素的值为: 3
  ->2个元素的值为: 6
结束打印链表。。。

第2个元素的值为:6

开始打印链表。。。
结束打印链表。。。

作者: 徐善通
地址: https://www.xstnet.com/article-144.html
声明: 除非本文有注明出处,否则转载请注明本文地址


我有话说



最新回复


正在加载中....

Copyrights © 2016-2019 醉丶春风 , All rights reserved. 皖ICP备15015582号-1