C 链表 增删改查 实现

何为链表

是一组被称为节点的自引用结构体的线形排列。要访问链表中的其中一个节点,必须从链表中的第一个节点开始。如果第一个节点的地址丢失,则链表中的其他节点都无法访问。所以,必须有一个指针指向第一个节点(这个指针也称为头指针)。节点nexttr的值被置为空,那么这个节点就是尾节点

优点

因为链表使用malloc函数申请内存,所以只要内存足够,链表的长度就可以无限增长。
相比于数组,链表拥有更高的内存利用率

缺点

每次访问链表中的数据时,都必须从头指针开始寻找
指针也占用存储空间,动态分配内存将增加函数调用的开销

链表的实现

创建一个结构体"节点"(需要有一个指针指向第一个节点,这个头指针会在main()函数中定义)

struct listNode {
    int value;  //一个节点中存放的内容
    struct listNode *nextPtr;  //指向下一个节点的指针,链表最后一个节点这个值为空(NULL)
};

1、增

创建函数insert(),传入类型为struct listNode *的头指针&headPtr,使用struct listNode **类型的形参来接收;传入数据value;传入unsigned int *类型的参数listLength。如果数据添加成功,链表的长度 + 1

void insert_random (struct listNode **headPtr, int value, unsigned int* listLength) {

}

对于insert函数,有两种情况:
    1)链表为空,创建一个节点并将头指针指向这个节点,同时将*nextPtr值置空

    创建一个新节点之后链表的状态

    2)链表不为空,插入节点到末尾,更新尾节点。

insert函数具体实现

void insert_random(LISTNODE **headPtr, int value, unsigned int* listLength) {
    /* 新的内存区块 */
    LISTNODE *newNode = malloc(sizeof(LISTNODE));

    LISTNODE * currentPtr = *headPtr;

    /* 检测内存是否正确分配 */
    if(newNode == NULL) {
        puts("内存不足!");
        exit(2);
    }

    /* 将数据存放至节点 */
    (*newNode).data = value;
    newNode->nextPtr = NULL;

    /* 添加区块 */
    /* 添加区块到头节点 */
    if (*headPtr == NULL) {
        *headPtr = newNode;
    }else {
        /* 添加区块到尾节点 */
        while (currentPtr->nextPtr != NULL) {
            currentPtr = currentPtr->nextPtr;
        }
        
        currentPtr->nextPtr = newNode;
    }
    (*listLength)++;;
    printf("%s%d%s", "插入节点成功,当前链表长度:", *listLength, "\n\n");
}

2、删

创建delete函数,获取头指针的值&headPtr、要删除的节点序号value、链表长度listLength

void delete(struct listNode **headPtr, int value, unsigned int* listLength) {

}


提示用户输入删除点,判断删除的位置:
    1)用户输入值小于1,输入有误并退出
    2)用户输入值为1,删除头节点,更新头指针
    3)用户输入 大于1 且小于等于链表长度 的值,删除中间节点或尾节点,更新删除点 上层指针指向
    4)用户输入值大于链表长度,提示用户要删除的节点不存在,程序退出

deleteLink函数具体实现

void delete(struct listNode **headPtr, int value, unsigned int* listLength) {

    LISTNODE *currentPtr = *headPtr;

    if (value == 1) {
        /* 要删除的节点 */
        LISTNODE *delNode = NULL;
        /* 获取要删除的节点 */
        delNode = currentPtr;
        /* 更新头指针 */
        *headPtr = currentPtr->nextPtr;
        /* 删除节点 */
        free(delNode);
    }else {
        /* 要删除节点的上一个区块 */
        LISTNODE *previousPtr = NULL;

        int loop = 1;
        /* 移动到待删除点 */
        while (loop < value) {
            previousPtr = currentPtr;
            currentPtr = currentPtr->nextPtr;
            loop++;
        }
        // delNode = currentPtr;
        /* 上层指针的nextPtr指向要删除节点的下一个节点 */
        previousPtr->nextPtr = currentPtr->nextPtr;
        /* 删除节点 */
        free(currentPtr);
    }

    (*listLength)--;
    printf("%s%d%s", "删除节点成功,当前链表长度:", *listLength, "\n\n");
}

3、改

创建updateLink函数,函数接收struct listNode *类型的头指针&headPtr、int类型的更新点updateNum、int类型更新后的值update

void updateLink(struct listNode **headPtr, int updateNum, int value) {
    LISTNODE *currentPtr = *headPtr;

    int loop = 1;
    while(loop < updateNum) {
        currentPtr = currentPtr->nextPtr;
        loop++;
    }

    if(currentPtr != NULL) {
        currentPtr->data = value;
    }else {
        puts("[-] 超出链表范围");
        exit(3);
    }
}

4、查

无论是打印一个节点中的特定值还是打印所有节点中的值,都是非常简单的。本例实现的是打印所有数据

void printLink(struct listNode **headPtr, unsigned int listLength) {
    LISTNODE *currentPtr = *headPtr;

    printf("%s%d\n", "当前链表长度: ", listLength);
    puts("+-------------------------+");

    while (currentPtr != NULL) {
        printf("%s%d\n%s%p\n", "数据: ", currentPtr->data, "下一个节点: ", currentPtr->nextPtr);
        puts("+-------------------------+");
        currentPtr = currentPtr->nextPtr;
    }

    puts("");
}

完整代码

listNode.c

猜你喜欢

转载自www.cnblogs.com/zhaoritian12138/p/12587359.html