C语言——链表(基础篇)

链表是一种非常重要的数据结构,在很多场景中都有广泛的应用,基本用法是动态存储链表的大小可以动态增长或缩小,不像数组那样需要预先分配固定的内存空间。这使得它在数据量不确定的情况下非常有用。相当于内存不连续的变长数组。

一个简单的 C 语言链表实现,包括链表节点的定义、创建节点、插入节点(头部插入和尾部插入)、删除节点和遍历链表的操作

本文为链表基础篇,链表基础入门,使用偏向c语言的代码,比c++代码冗长。

节点构建:

链表的基本单元是节点(node),每个节点包含一个值和指向下一个节点的指针。

struct ListNode {
    int data;
    struct ListNode* next;
};

这是一个数据类型为结构体指针的函数,使用new开辟新节点并进行初始化,返回新建节点的指针 

struct ListNode* createNode(int data) {
    struct ListNode* newNode=new ListNode();
    if(newNode==NULL){exit(-1);}//如果内存分配失败退出并返回-1
    newNode->data=data;
    newNode->next=NULL;
    return newNode;
}
链表的构建:

在头部(前面)添加节点: 

void insertAtHead(struct ListNode** head, int data) {
    struct ListNode* newNode=createNode(data);
    newNode->next=*head;//与原头部先连接
    *head=newNode;//更新头部
}

 在链表操作里,我们经常要对链表的头指针进行修改,比如在头部插入新节点、删除头节点时,头指针指向会改变。普通指针 struct ListNode* head 是按值传递,函数里修改它的值不会影响原头指针。而使用指向指针的指针 struct ListNode** head 按地址传递头指针,这样函数内部对它所指向的指针进行修改,外部的头指针也会跟着改变。

说人话就是*head指向的是链表头部,**head指向储存头部的地方,改变**head就是改变头部

在尾部(后面)添加节点: 

新建节点,特判头部不存在,寻找尾部,延长尾部(在尾部添加节点)。

void insertAtTail(struct ListNode** head, int data) {
    struct ListNode* newNode=createNode(data);
    if(*head==NULL) {
        *head=newNode;
        return;//特判头部不存在
    }
    struct ListNode* temp=*head;//建立一个临时指针用于遍历
    while(temp->next!=NULL) {
        temp=temp->next;
    }//找到尾部
    temp->next=newNode;//延长
}
删除包含特定数据(data)的节点:

检查是否为空链表,头部特判(检查头部是否为目标data),寻找data,如果存在data则删除节点 

while循环,如果temp的下一个是data节点(或NULL)停止循环,也就是说要删的是temp->next。

在连接节点的过程中,如果data是尾部节点,temp->next->是不存在的即为NULL

void deleteNode(struct ListNode** head,int data) {
    if(*head==NULL) {return;}//头部不存在,空链表特判
    if((*head)->data==data) {//头部特判
        struct ListNode* temp=*head;//用于删除原头节点
        *head=(*head)->next;
        delete temp;
        return;
    }
    struct ListNode* temp=*head;//temp初始是头,直接判定下一个,也就是next
    while(temp->next!=NULL&&temp->next->data!=data) {
        temp=temp->next;
    }
    if(temp->next!=NULL) {
        struct ListNode* temp2=temp->next;
        temp->next=temp2->next;//temp->next=temp1->next->next;
        delete temp2;
    }
    
}
遍历链表:
void traverselist(struct ListNode* head) {
    struct ListNode* temp=head;
    while(temp!=NULL) {
        cout<<temp->data<<" ";
        temp=temp->next;
    }
}
 摧毁链表:

因为我们节点是new出来的,动态分配在堆上,操作系统的回收内存过程是粗略的,可能存在内存泄漏,所有我建议用完链表后手动摧毁。

void destroyList(struct ListNode** head) {
    struct ListNode* temp = *head;
    struct ListNode* temp2;
    while (temp != NULL) {
        temp2 = temp->next;
        free(temp);
        temp = temp2;
    }
    *head = NULL;
}
总代码:
#include<iostream>
using namespace std;
struct ListNode {
    int data;
    struct ListNode* next;
};
struct ListNode* createNode(int data) {
    struct ListNode* newNode=new ListNode();
    if(newNode==NULL){exit(-1);}//如果内存分配失败退出并返回-1
    newNode->data=data;
    newNode->next=NULL;
    return newNode;
}
void insertAtHead(struct ListNode** head, int data) {
    struct ListNode* newNode=createNode(data);
    newNode->next=*head;//与原头部先连接
    *head=newNode;//更新头部
}
void insertAtTail(struct ListNode** head, int data) {
    struct ListNode* newNode=createNode(data);
    if(*head==NULL) {
        *head=newNode;
        return;//特判头部不存在
    }
    struct ListNode* temp=*head;//建立一个临时指针用于遍历
    while(temp->next!=NULL) {
        temp=temp->next;
    }//找到尾部
    temp->next=newNode;//延长
}
void deleteNode(struct ListNode** head,int data) {
    if(*head==NULL) {return;}//头部不存在,空链表特判
    if((*head)->data==data) {//头部特判
        struct ListNode* temp=*head;//用于删除原头节点
        *head=(*head)->next;
        delete temp;
        return;
    }
    struct ListNode* temp=*head;//temp初始是头,直接判定下一个,也就是next
    while(temp->next!=NULL&&temp->next->data!=data) {
        temp=temp->next;
    }
    if(temp->next!=NULL) {
        struct ListNode* temp2=temp->next;
        temp->next=temp2->next;//temp->next=temp1->next->next;
        delete temp2;
    }
}
void traverselist(struct ListNode* head) {
    struct ListNode* temp=head;
    while(temp!=NULL) {
        cout<<temp->data<<" ";
        temp=temp->next;
    }
}
void destroyList(struct ListNode** head) {
    struct ListNode* temp = *head;
    struct ListNode* temp2;
    while (temp != NULL) {
        temp2 = temp->next;
        free(temp);
        temp = temp2;
    }
    *head = NULL;
}
int main() {
    struct ListNode* head = NULL;
    insertAtHead(&head, 2);insertAtHead(&head, 5);insertAtHead(&head, 6);// 插入节点
    insertAtTail(&head, 0);insertAtTail(&head, 1);insertAtTail(&head, 3);
    insertAtTail(&head, 1);insertAtTail(&head, 4);
    printf("Original list: ");
    traverselist(head);
    deleteNode(&head, 1);deleteNode(&head, 3);deleteNode(&head, 1);deleteNode(&head, 4);// 删除节点
    printf("After deleting 1314: ");
    traverselist(head);
    destroyList(&head);
    return 0;
}

posted @ 2025-01-23 08:00  mc12356  阅读(76)  评论(0)    收藏  举报  来源