单链表涉及的两个结构体:
描述节点信息的结构体:
struct node {
int data; //节点数据
struct node *next; //保存下一个节点的首地址
};
描述整个单链表的结构体:
struct list {
struct node *head; //保存头节点的首地址
struct node *tail; //保存尾结点的首地址
};
//list.h:单链表声明
#ifndef __LIST_H
#define __LIST_H
#include <stdio.h>
#include <stdlib.h>
//声明描述节点信息的结构体
typedef struct node {
int data;//节点数据
struct node *next;//保存下一个节点的首地址
}node_t;
//声明描述单链表信息的结构体
typedef struct list {
struct node *head;//保存头节点的首地址
struct node *tail; //保存尾节点的首地址
}list_t;
//声明操作函数
extern void list_init(list_t *list);//初始化
extern void list_deinit(list_t *list);//释放所有节点内存
extern void list_travel(list_t *list);//遍历
extern void list_add(list_t *list, int data);//顺序插入
extern void list_add_first(list_t *list, int data);//前插
extern void list_add_last(list_t *list, int data);//后插
extern void list_del(list_t *list, int data);//删除data所在的节点
#endif
vim list.c
//list.c:单链表定义
#include "list.h"
//定义分配新节点内存函数
static node_t *create_node(int data){
//1.分配节点内存
node_t *pnew = (node_t *)malloc(sizeof(node_t));
//2.初始化新节点内存
pnew->data = data;
pnew->next = NULL;
//3.返回新节点内存的首地址
return pnew;
}
//定义初始化单链表函数
void list_init(list_t *list) {
//1.给头结点分配内存
list->head = create_node(0);
//2.给尾结点分配内存
list->tail = create_node(0);
//3.头结点指向尾节点
list->head->next = list->tail;
list->tail->next = NULL;
}
//定义单链表的遍历函数
void list_travel(list_t *list) {
for(node_t *pnode = list->head; pnode != list->tail; pnode=pnode->next) {
//1.定义游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
if(pmid != list->tail) //pmid不指向尾节点那么就是指向有效节点
printf("%d ", pmid->data);
}
printf("\n");
}
//定义顺序插新节点函数
void list_add(list_t *list, int data) {
//1.创建新节点
node_t *pnew = create_node(data);
//2.遍历链表找到要插入的位置,让pmid指向后一个节点,pfirst指向前一个节点
//那么新节点pnew就插入到pfirst和pmid中间即可
for(node_t *pnode = list->head; pnode != list->tail; pnode=pnode->next) {
//1.定义游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
if(pmid->data >= pnew->data || pmid == list->tail/*新节点比前面节点都大*/) {
pfirst->next = pnew;
pnew->next = pmid;
break;
}
}
}
//定义前插函数
void list_add_first(list_t *list, int data) {
//1.创建新节点
node_t *pnew = create_node(data);
//临时备份原先的第一个有效节点
node_t *ptmp = list->head->next;
//2.将新节点插入到头结点的后面
list->head->next = pnew; //头结点指向新节点
pnew->next = ptmp; //新节点指向原先第一个节点
/*
pnew->next = list->head->next; //先让新新节点指向原先第一个节点
list->head->next = pnew; //然后让头结点指向新节点
*/
}
//定义后插函数
void list_add_last(list_t *list, int data) {
//1.创建新节点
node_t *pnew = create_node(data);
//2.遍历链表找到最后一个节点,让pfirst指向后最后一个节点,pmid指向尾节点
//那么新节点pnew就插入到pfirst和pmid中间即可,也就是新节点插入到原先最后一个节点
//和尾节点中间
for(node_t *pnode = list->head; pnode != list->tail; pnode=pnode->next) {
//3.定义游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
if(pmid == list->tail) {
pfirst->next = pnew;
pnew->next = pmid;
break;
}
}
}
//定义删除节点的函数
void list_del(list_t *list, int data) {
//1.遍历找到要删除的节点,让pmid指向要删除的节点,这样pfirst指向前一个节点
//plast指向后一个节点,最后指向让pfirst指向plast即可
for(node_t *pnode = list->head; pnode != list->tail; pnode=pnode->next) {
//2.定义游标
node_t *pfirst = pnode;
node_t *pmid = pfirst->next;
node_t *plast = pmid->next;
if(pmid->data == data && pmid != list->tail/*不能删除尾节点*/) {
pfirst->next = plast;
free(pmid); //释放pmid指向的节点内存
break;
}
}
}
//定义释放链表所有节点的函数
void list_deinit(list_t *list) {
node_t *pnode = list->head;
while(pnode) {
node_t *ptmp = pnode->next; //临时备份下一个节点的地址,不能让链表断开
free(pnode);//释放当前的节点
pnode = ptmp; //准备开始释放下一个节点
}
}
vim main.c
//main.c:测试
#include "list.h"
int main(void) {
list_t list; //创建链表
list_init(&list); //初始化链表
list_add(&list, 10);
list_add(&list, 20);
list_add(&list, 30);
list_travel(&list);
list_add_first(&list, 5);
list_add_first(&list, 3);
list_travel(&list);
list_add_last(&list, 40);
list_add_last(&list, 50);
list_travel(&list);
list_del(&list, 20);
list_travel(&list);
list_deinit(&list);
return 0;
}
vim Makefile
#Makefile
BIN=list
OBJ=main.o list.o
CC=gcc
$(BIN):$(OBJ)
$(CC) -o $(BIN) $(OBJ)
%.o:%.c
$(CC) -c -o $@ $<
clean:
rm $(BIN) $(OBJ)