【数据结构】单链表

前言
单链表是一种数据结构,其中每个元素(通常称为节点)都包含一个数据字段和一个指向下一个节点的指针。


文章目录

  • 一、初识单链表
  • 二、单链表的操作
    • 2.1 初始化链表
    • 2.2 插入操作
    • 2.3 删除操作
    • 2.4 打印链表
    • 2.5 查找节点
    • 2.6 修改节点数据
    • 2.7 销毁链表并释放内存
  • 三、完整代码
    • list.h
    • list.c
    • main.c

一、初识单链表

单链表就像一段火车,每节车厢都由前面一节车厢牵引,单链表的其中一个节点可以看作一节车厢,车厢内的货物就是节点存储的数据,而车厢的挂钩就是指针,这个“指针”可以由前面一节车厢找到后面一节车厢,但是不能通过后面一节车厢找到前面一节车厢。

【数据结构】单链表_第1张图片

单链表通常由结构体(struct)和指针来实现:

//在头文件中定义
typedef struct Node {
    int data;           // 节点存储的数据
    struct Node* next;  // 指向下一个节点的指针
};

二、单链表的操作

2.1 初始化链表

头文件中定义链表

typedef int dataType;//将类型转换成dataType,方便修改,使用不同数据

typedef struct Node {
    int data;           // 节点存储的数据
    struct Node* next;  // 指向下一个节点的指针
}Node;

主函数内创建头节点
【数据结构】单链表_第2张图片

Node* head = NULL;//创建一个头节点,这里不使用哨兵位

使用哨兵节点的好处:

  1. 简化代码:在插入和删除操作时,不需要单独处理链表头。这使得代码更简洁、易读和易于维护。

  2. 统一处理:由于所有节点(包括链表头)现在都有一个前置节点(即哨兵节点),一些操作可以更统一地执行。

  3. 减少错误:代码简化通常会降低出错的可能性。

  4. 方便返回新的头节点:在某些链表操作(如排序、反转等)后,链表的实际头节点可能会改变。使用哨兵节点可以很容易地找到新的头节点,即哨兵节点的 next 指针所指向的节点。


用于创建新节点的函数,在插入操作中可以方便调用

Node* createNode(int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) {
        perror("Error in createNode\n");//createNode函数发生错误
        return NULL;
    }
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}

2.2 插入操作

通常插入操作有头插,尾插和指定位置插入

头插

void insertHead(Node** head,dataType data) {
    Node* newNode = createNode(data);
    if (newNode == NULL) {
        perror("Error in insertNode");//insertNode函数发生错误
        return NULL;
    }
    newNode->next = *head;
    *head = newNode;
}

尾插

void insertTail(Node** head, dataType data) {
    Node* newNode = createNode(data);
    if (newNode == NULL) {
        perror("Error in insertTail");//insertTail函数发生错误
        return NULL;
    }

    //当仅有head一个头节点时
    if (*head == NULL) {  
        *head = newNode;
        return;
    }
    Node* temp = *head;   //始终保持head指针不变

    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = newNode;
}

指定位置插入

//指定位置插入,第一个节点下标为1,头节点为0
void insertPosition(Node** head, dataType data, int position) {
    Node* newNode = createNode(data);
    if (newNode == NULL) {
        perror("Error in insertTail");
        return;
    }

    if (position < 1) {  //位置错误
        printf("Invalid position!\n");
        return;
    }

    //头插
    if (position == 1) {  
        newNode->next = *head;
        *head = newNode;
        return;
    }

    Node* temp = *head;

    for (int i = 1; i < position - 1; i++) {  
        if (temp == NULL) {
            printf("Position out of bounds!\n"); //位置越界
            return;
        }
        temp = temp->next;  //最后一次赋值,temp来到指定位置的前一位
    }

    
    if (temp == NULL) {
        printf("Position out of bounds!\n");  
        return;
    }

    newNode->next = temp->next; //temp->next是数据插入指定位置后的后一节点的地址
    temp->next = newNode;  //将节点地址newNodef赋给前一位节点的next
}

2.3 删除操作

删除链表第一个有效节点

void deleteHead(Node** head) {
    if (*head == NULL) {
        return;
    }

    Node* temp = *head; //*head第一个节点地址
    *head = (*head)->next; 
    free(temp);//释放空间
}

删除链表尾部节点

void deleteTail(Node** head) {
    if (*head == NULL) {
        return;
    }
    Node* temp = *head;
    Node* prev = NULL; //prev尾节点的前一节点
    while (temp->next != NULL) {
        prev = temp;
        temp = temp->next;
    }
    if (prev != NULL) {  //prev为空说明仅有head一个头节点
        prev->next = NULL;
    }
    else {
        *head = NULL;
    }
    free(temp);//释放空间
}

删除链表指定位置的节点

void deleteIndex(Node** head, int index) {
    if (index < 1) {
        printf("Invalid index. Must be greater than 0.\n");
        return;
    }

    if (index == 1) {
        deleteHead(head);
        return;
    }

    Node* temp = *head;
    Node* prev = NULL; //prev目标节点前一个节点

    for (int i = 1; i < index; ++i) {
        if (temp == NULL) {
            printf("Index out of range.\n");//超出范围
            return;
        }
        prev = temp;
        temp = temp->next;
    }

    if (temp == NULL) {
        printf("Index out of range.\n");//超出范围
        return;
    }

    prev->next = temp->next;
    free(temp);
}

2.4 打印链表

void printList(Node** head) {
    Node* temp = *head;
    while (temp != NULL) {
        printf("%d->", temp->data);
        temp = temp->next;
    }
    printf("\n");
}

2.5 查找节点

Node* searchNode(Node** head, int data) {
    Node* temp = *head;

    int count = 1;
    while (temp != NULL) {
        if (temp->data == data) {
            printf("在第%d个节点\n", count);
            return temp;  // 返回找到的节点
        }
        temp = temp->next;
        count++;
    }
    printf("没找到\n");
    return NULL;  // 返回NULL表示没找到
}

2.6 修改节点数据

void updateNode(Node** head, int newData,int index) {
    Node* temp = *head;

    int count = 1;
    while (temp != NULL) {     
        if (count == index) {
            temp->data = newData;
            return;
        }
        temp = temp->next;
        count++;
    }
    printf("访问越界\n");
    return;
}

2.7 销毁链表并释放内存

void destroyList(Node** head) {
    Node* cur = *head;
    Node* nextNode;

    while (cur != NULL) {
        nextNode = cur->next;
        free(cur);
        cur = nextNode;
    }

    *head = NULL;
}

三、完整代码

list.h

#include
#include
typedef int dataType;

typedef struct Node {
    int data;           // 节点存储的数据
    struct Node* next;  // 指向下一个节点的指针
}Node;


// 创建新节点
Node* createNode(int data);
//头插
void insertHead(Node** head, dataType data);
//尾插
void insertTail(Node** head, dataType data);
//指定插入
void insertPosition(Node** head, dataType data, int position);
//删除链表头部节点
void deleteHead(Node** head);
// 删除链表尾部节点
void deleteTail(Node** head);
// 删除链表指定位置的节点
void deleteIndex(Node** head, int index);

// 打印链表
void printList(Node** head);

// 修改节点数据
void updateNode(Node** head, int newData, int index);

// 查找节点
Node* searchNode(Node** head, int data);

// 销毁链表并释放内存
void destroyList(Node** head);

list.c

#include"list.h"

// 创建新节点
Node* createNode(int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) {
        perror("Error in createNode\n");//createNode函数发生错误
        return NULL;
    }
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}

// 头部插入节点
void insertHead(Node** head,dataType data) {
    Node* newNode = createNode(data);
    if (newNode == NULL) {
        perror("Error in insertHead");//insertHead函数发生错误
        return ;
    }
    newNode->next = *head;
    *head = newNode;
}

//尾插
void insertTail(Node** head, dataType data) {
    Node* newNode = createNode(data);
    if (newNode == NULL) {
        perror("Error in insertTail");//insertTail函数发生错误
        return ;
    }

    //当仅有head一个头节点时
    if (*head == NULL) {  
        *head = newNode;
        return;
    }
    Node* temp = *head;   //始终保持head指针不变

    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = newNode;
}

//指定位置插入,第一个节点下标为1,头节点为0
void insertPosition(Node** head, dataType data, int position) {
    Node* newNode = createNode(data);
    if (newNode == NULL) {
        perror("Error in insertTail");
        return;
    }

    if (position < 1) {  //位置错误
        printf("Invalid position!\n");
        return;
    }

    //头插
    if (position == 1) {  
        newNode->next = *head;
        *head = newNode;
        return;
    }

    Node* temp = *head;

    for (int i = 1; i < position - 1; i++) {  
        if (temp == NULL) {
            printf("Position out of bounds!\n"); //位置越界
            return;
        }
        temp = temp->next;  //最后一次赋值,temp来到指定位置的前一位
    }

    
    if (temp == NULL) {
        printf("Position out of bounds!\n");  
        return;
    }

    newNode->next = temp->next; //temp->next是数据插入指定位置后的后一节点的地址
    temp->next = newNode;  //将节点地址newNodef赋给前一位节点的next
}

// 删除链表第一个有效节点
void deleteHead(Node** head) {
    if (*head == NULL) {
        return;
    }

    Node* temp = *head; //*head第一个节点地址
    *head = (*head)->next; 
    free(temp);
}

// 删除链表尾部节点
void deleteTail(Node** head) {
    if (*head == NULL) {
        return;
    }
    Node* temp = *head;
    Node* prev = NULL; //prev尾节点的前一节点
    while (temp->next != NULL) {
        prev = temp;
        temp = temp->next;
    }
    if (prev != NULL) {  //prev为空说明仅有head一个头节点
        prev->next = NULL;
    }
    else {
        *head = NULL;
    }
    free(temp);
}

// 删除链表指定位置的节点
void deleteIndex(Node** head, int index) {
    if (index == 1) {  
        deleteHead(head);
        return;
    }

    Node* temp = *head;
    Node* prev = NULL; //prev目标节点前一个节点
    for (int i = 1; i < index; i++) {
        if (temp == NULL) {
            printf("Index out of range\n");  //超出范围
            return;
        }
        prev = temp;
        temp = temp->next;
    }

    if (prev != NULL && temp !=NULL) {
        prev->next = temp->next;
        free(temp);
    }
    else {
        printf("Index out of range or prev is NULL.\n");
    }

}

// 打印链表
void printList(Node** head) {
    Node* temp = *head;
    while (temp != NULL) {
        printf("%d->", temp->data);
        temp = temp->next;
    }
    printf("\n");
}

// 查找节点
Node* searchNode(Node** head, int data) {
    Node* temp = *head;

    int count = 1;
    while (temp != NULL) {
        if (temp->data == data) {
            printf("在第%d个节点\n", count);
            return temp;  // 返回找到的节点
        }
        temp = temp->next;
        count++;
    }
    printf("没找到\n");
    return NULL;  // 返回NULL表示没找到
}

// 修改节点数据
void updateNode(Node** head, int newData,int index) {
    Node* temp = *head;

    int count = 1;
    while (temp != NULL) {     
        if (count == index) {
            temp->data = newData;
            return;
        }
        temp = temp->next;
        count++;
    }
    printf("访问越界\n");
    return;
}

// 销毁链表并释放内存
void destroyList(Node** head) {
    Node* cur = *head;
    Node* nextNode;

    while (cur != NULL) {
        nextNode = cur->next;
        free(cur);
        cur = nextNode;
    }

    *head = NULL;
}


main.c

#include"list.h"

int main() {
	Node* head = NULL;//创建一个头节点,说明这是一个带有头节点的单链表
	insertHead(&head,2);//在头部插入2,2->
	insertHead(&head,1);//在头部插入1, 1->2->
	insertTail(&head,3);//尾部插入3,1->2->3->
	insertTail(&head,5);//尾部插入5,1->2->3->5->
	insertPosition(&head,4,4);//第四个节点插入4,1->2->3->4->5->
	printList(&head);//1->2->3->4->5->
	
	searchNode(&head,3);//查找3在哪个节点
	printList(&head);

	updateNode(&head, 4, 3);//将第三个节点改为4,1->2->4->4->5->
	printList(&head);

	deleteHead(&head);//删除头节点,2->3->4->5->
	deleteTail(&head);//删除尾节点,2->3->4->
	deleteIndex(&head, 2);//删除第二个节点,2->4->
	
	destroyList(&head);


	return 0;
}

在这里插入图片描述
如果你喜欢这篇文章,点赞+评论+关注⭐️哦!
欢迎大家提出疑问,以及不同的见解。

你可能感兴趣的:(数据结构,c语言,链表)