C语言实现链表的基本操作(增删改查)

283 阅读3分钟

单链表的基本操作,增删改查

概述:

  • 链表属于线性结构,由多个节点构成,与数组不同,他是离散存储,通过指针相连
  • 链表的节点必须包含两个信息“有效数据和指针”,也就是“数据域和指针域 ”
  • 每一个节点都有前驱节点和后续节点,首节点没有前驱节点,尾节点没有后续节点
  • 链表的“首节点”前面还有一个“头节点”,头节点不存放有效数据,数据域是个无效值,头节点作用是方便对链表进行操作
  • 双链表:每一个节点有两个指针域,一个指针域指向上一个节点,一个指针域指向下一个节点
  • 循环链表:能通过任何一个节点找到其他所有节点(也就是最后一个节点指向第一个节点形成了一个循环)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>// 链表的节点必须包含两个信息“有效数据和指针”,也就是“数据域和指针域 ”
typedef struct Node {
    //数据域
    int data;
    //指针域
    struct Node *pNext;//链表中的指针,指向的是下一个节点的整体,所以指针类型必须和下一个节点类型相同
} *PNODE, NODE;
​
PNODE init_list(); // 初始化链表
void add_list(PNODE pHead,int val); // 给链表追加节点
void traverse_list(PNODE pHead); // 遍历输出链表
bool is_empty(PNODE pHead); // 判断链表是否为空
int length_list(PNODE pHead); // 获取链表长度
void sort_list(PNODE pHead); // 对链表排序
// 这里我使用的是下标index(从0开始),注意不是pos(从1开始) 
bool insert_list(PNODE pHead,int index,int val);// 插入节点 
bool delete_list(PNODE pHead,int index,int *val);// 删除节点 int main() {
    PNODE pHead = init_list();
​
    int i;
    for(i = 0; i < 5; i++) {
        add_list(pHead, pow(2,i));
    }
​
    traverse_list(pHead);
​
    if(is_empty(pHead)) {
        printf("链表为空\n");
    } else {
        printf("链表不为空\n");
    }
​
    printf("链表长度为:%d\n",length_list(pHead));
​
    sort_list(pHead);
    traverse_list(pHead);
​
    insert_list(pHead,0,5);
    printf("插入成功:"); 
    traverse_list(pHead);
​
    int val;
    delete_list(pHead, 5, &val);
    printf("删除的值为:%d\n", val);
    traverse_list(pHead);
    //pHead指向的内存是我在init_list函数中动态分配,执行完之后需要手动销毁 
    free(pHead);
​
    return 0;
}
​
PNODE init_list() {
    PNODE pHead = (PNODE) malloc(sizeof(NODE));
    if(pHead == NULL) {
        printf("内存分配失败\n");
        exit(-1);
    }
    pHead->pNext = NULL;
    return pHead;
}
​
void add_list(PNODE pHead,int val) {
    // 1.新建一个节点
    PNODE pNew = (PNODE) malloc(sizeof(NODE));
    if(pNew == NULL) {
        printf("内存分配失败\n");
        exit(-1);
    }
    // 2.给新节点赋值
    pNew->data = val;
    pNew->pNext = NULL;
    // 3.将pTail指向尾节点
    PNODE pTail = pHead;
    while(pTail->pNext != NULL) { // 当pTail指向的节点不为NULL,说明pTail指向的不是尾节点
        pTail = pTail->pNext; // 进入循环说明pTail指向的还不是尾节点,所以将pTail指向下一个节点
    }
    // 4.最后将新建节点pNew挂载到尾节点之后
    pTail->pNext = pNew;
}
​
void traverse_list(PNODE pHead) {
    // 1.创建一个指针(pMOVE),指向首节点(pHead->pNext)
    PNODE pMove = pHead->pNext;
    int i;
    // 2.如果pMove指向的节点不为空,则进入循环输出
    while(pMove != NULL) {
        printf("%d ", pMove->data);
        //3.将 pMove 指向下一个节点
        pMove = pMove->pNext;
    }
    printf("\n");
    return;
}
​
bool is_empty(PNODE pHead) {
    if(pHead->pNext == NULL) {
        return true;
    } else {
        return false;
    }
}
​
int length_list(PNODE pHead) {
    PNODE pMove = pHead;
    int len = 0;
    while(pMove->pNext != NULL) {
        pMove = pMove->pNext;
        len++;
    }
    return len;
}
​
void sort_list(PNODE pHead) {
    //这里使用的是选择排序,与数组的选择排序有点不同,因为链表不能记录最小值的下标,所以每轮比较会进行多次交换
    int i,j,t;
    int len = length_list(pHead);
    PNODE p, q;
    for(i = 0, p = pHead->pNext; i < len-1; i++) {
        for(j = i+1,q = p->pNext; j < len; j++, q = q->pNext) {
            if(p->data > q->data) {
                t = p->data;
                p->data = q->data;
                q->data = t;
            }
        }
    }
    return;
}
​
bool insert_list(PNODE pHead,int index,int val) {
    int i = 0;
    PNODE p = pHead;
    // 在下标index前插入节点,通过循环拿到下标index前面的节点,这样便于插入
    while(p != NULL && i < index) {
        p = p->pNext;
        i++;
    }
    // 判断p是否指向 NULL,插入的下标位置index是否小于0
    if(p == NULL || index < 0) {//老师这里用的是 i>pos所表达的意思就是 index<0,个人认为index<0更加便于理解
        return false;
    }
​
    PNODE pNew = (PNODE) malloc(sizeof(NODE));
    if(pNew == NULL) {
        printf("内存分配失败\n");
        exit(-1);
    }
    pNew->pNext = p->pNext;
    pNew->data = val;
    p->pNext = pNew;
​
    return true;
}
​
bool delete_list(PNODE pHead,int index,int *val) {
    int i = 0;
    PNODE p = pHead;
    // 这里删除,也是通过循环拿到“需要删除节点的前一个节点 ” 
    while(p->pNext != NULL && i < index) {
        p = p->pNext;
        i++;
    }
    if(NULL == p->pNext || index < 0){
        return false;
    }
    *val = p->pNext->data; 
    // 删除 p 后面第一个节点,并让 p 指向后面的第二个节点 
    PNODE q = p->pNext;
    // 判断后面的第二个节点是否存在,不存在直接赋值为NULL 
    p->pNext = p->pNext->pNext == NULL  ? NULL : p->pNext->pNext;
    free(q);
    q = NULL;
    
    return true;
}

\