接下来将介绍c语言实现单链表的一些基础操作:
1. 初始化单链表
2. 创建链表的节点
3. 打印链表
4. 销毁链表
5. 尾部插入链表
6. 尾部删除链表
7. 头部插入元素
8. 尾部插入元素
9. 指定位置前插入元素
10. 指定位置后插入元素
11. 删除元素
12. 删除指定元素
13. 删除所有相同元素
14. 判断链表是否为空
15. 求链表长度
具体的实现代码如下:
头文件:linklist.h
#pragma once
//#include<stddef.h>
#define TestType printf("\n*************************%s*****************************\n",__FUNCTION__);
typedef char LinkNodeType;
typedef struct LinkNode{
LinkNodeType data;
struct LinkNode* next;
}LinkNode;
void LinkListInit(LinkNode** node);
LinkNode* CreateNode(LinkNodeType value);
void LinkListPrintChar(LinkNode* phead,const char* msg);
void LinkListDestroy(LinkNode** phead);
void LinkListPushBack(LinkNode** phead, LinkNodeType value);
void LinkListPopBack(LinkNode** phead);
void LinkListPushFront(LinkNode** phead,LinkNodeType value);
void LinkListPopFront(LinkNode** phead);
LinkNode* LinkListFind(LinkNode* phead,LinkNodeType to_find);
void LinkListInsert(LinkNode** phead,LinkNode* pos,LinkNodeType value);
void LinkListInsertAfter(LinkNode** phead,LinkNode* pos,LinkNodeType value);
void LinkListErase1(LinkNode** phead,LinkNode* pos);
void LinkListErase2(LinkNode** phead,LinkNode** pos);
void LinkListRemove(LinkNode** phead,LinkNodeType value);
void LinkListRemoveAll(LinkNode** phead,LinkNodeType value);
int LinkListEmpty(LinkNode* phead);
size_t LinkListSize(LinkNode* phead);
实现函数: linklist.c
#include"linklist.h"
#include<stdlib.h>
#include<stdio.h>
//打印链表
void LinkListPrintChar(LinkNode* phead,const char* msg){
printf("[%s]:\n",msg);
LinkNode* cur = phead;
for(;cur != NULL;cur = cur->next){
printf("[%c][%p]->",cur->data,cur);
}
printf("[NULL]\n\n");
}
//初始化链表,将头指针指向空
void LinkListInit(LinkNode** node){
if(node == NULL){
//非法输入
return;
}
*node = NULL;
}
//构造新的节点
LinkNode* CreateNode(LinkNodeType value){
LinkNode* new_node = (LinkNode*)malloc(sizeof(LinkNode));
if(new_node == NULL){
printf("申请内存失败!\n");
exit(1);
}else{
new_node->data = value;
new_node->next = NULL;
return new_node;
}
}
//释放节点
void DestroyNode(LinkNode* node){
free(node);
}
//销毁链表
void LinkListDestroy(LinkNode** phead){
if(phead == NULL){
return;
}
if(*phead == NULL){
return;
}
//遍历整个链表,从头开始删除
while((*phead)->next != NULL){
LinkNode* pnode = *phead;
*phead = (*phead)->next;
free(pnode);
pnode = NULL;
}
return;
}
//尾部插入元素
void LinkListPushBack(LinkNode** phead, LinkNodeType value){
if( phead == NULL){
//非法输入
return;
}
if(*phead == NULL){
//空链表,指向结构体的指针都不存在,说明结构体不存在
*phead = CreateNode(value);
return;
}
LinkNode* cur = *phead;
while(cur->next != NULL){
cur = cur->next;
}
//找到最后一个节点的指针
cur->next = CreateNode(value);
return;
}
//尾部删除
void LinkListPopBack(LinkNode** phead){
if(phead == NULL){
return;
}
if(*phead == NULL){
printf("链表为空,无法删除.\n");
return;
}
if((*phead)->next == NULL){
DestroyNode(*phead);
*phead = NULL;
return;
}
LinkNode* cur = *phead;
while(cur->next->next != NULL){
cur = cur->next;
}
LinkNode* last = cur->next;
DestroyNode(last);
cur->next = NULL;
return;
}
//头部插入节点
void LinkListPushFront(LinkNode** phead,LinkNodeType value){
if(phead == NULL){
return;
}
if(*phead == NULL){
*phead = CreateNode(value);
return;
}
LinkNode* cur = *phead;
*phead = CreateNode(value);
(*phead)->next = cur;
return;
}
//头部删除节点
void LinkListPopFront(LinkNode** phead){
if(phead == NULL){
return;
}
if(*phead == NULL){
printf("链表为空,无法删除.\n");
return;
}
LinkNode* cur = (*phead)->next;
(*phead)->next = NULL;
DestroyNode(*phead);
*phead = cur;
return;
}
//查找节点
LinkNode* LinkListFind(LinkNode* phead,LinkNodeType to_find){
if(phead == NULL){
printf("链表为空,查找不到!\n");
return;
}
LinkNode* cur = phead;
for(;cur != NULL; cur = cur->next){
if(cur->data == to_find){
return cur;
}
}
return;
}
//指定位置前插入节点
void LinkListInsert(LinkNode** phead,LinkNode* pos,LinkNodeType value){
if(phead == NULL){
return;
}
if(*phead == NULL){
//若为空链表,pos位置无效,将value当作新节点插入
*phead = CreateNode(value);
return;
}
if(pos == NULL){
//pos为NULL,简化为尾插
LinkListPushBack(phead,value);
return;
}
if(pos == *phead){
//pos为头节点,简化为头插
LinkListPushFront(phead,value);
return;
}
LinkNode* cur = *phead;
while(cur->next != NULL){
if(cur->next == pos){
LinkNode* tmp = CreateNode(value);
tmp->next = pos;
cur->next = tmp;
return;
}
cur = cur->next;
}
printf("没有找到!\n");
return;
}
//指定位置后插入节点
void LinkListInsertAfter(LinkNode** phead,LinkNode* pos,LinkNodeType value){
if(phead == NULL){
return;
}
if(*phead == NULL){
//链表为空,则直接将value作为新节点插入
*phead = CreateNode(value);
return;
}
if(pos == NULL){
printf("[ pos = NULL ]\n");
printf("非法输入!\n\n");
return;
}
LinkNode* cur = *phead;
while(cur->next != NULL){
if(cur == pos){
LinkNode* tmp = CreateNode(value);
tmp->next = cur->next;
cur->next = tmp;
return;
}
cur = cur->next;
}
return;
}
void LinkListErase1(LinkNode** phead,LinkNode* pos){
//找到前一个元素进行删除的方法
if(phead == NULL || pos == NULL){
//非法输入
return;
}
if(*phead == NULL){
printf("[ phead = NULL ]\n");
printf("空链表,无法删除!\n\n");
return;
}
if(*phead == pos){
//删除位置是头节点
LinkNode* to_delete = *phead;
*phead = (*phead)->next;
DestroyNode(to_delete);
to_delete->next = NULL;
return;
}
//找到前一个节点进行删除
LinkNode* cur = *phead;
while(cur != NULL){
if(cur->next == pos){
cur->next = pos->next;
DestroyNode(pos);
pos->next = NULL;
return;
}
cur = cur->next;
}
return;
}
//移形换位法,用要删除节点的后一个节点覆盖前一个节点,删除后面的节点
//优点:不用遍历整合链表
void LinkListErase2(LinkNode** phead,LinkNode** pos){
if(phead == NULL){
return;
}
if(*phead == NULL){
printf("[ phead = NULL ]\n");
printf("链表为空,无法删除!\n\n");
return;
}
if(*pos != NULL && (*pos)->next != NULL){
LinkNode* to_delete = (*pos)->next;
(*pos)->data = to_delete->data;
(*pos)->next = to_delete->next;
to_delete->next = NULL;
DestroyNode(to_delete);
return;
}
if((*pos)->next == NULL){
LinkListPopBack(phead);
return;
}
printf(" 找不到pos,无法删除!\n");
return;
}
//删除指定的节点
void LinkListRemove(LinkNode** phead,LinkNodeType value){
if(phead == NULL){
return;
}
if(*phead == NULL){
return;
}
LinkNode* pos = LinkListFind(*phead,value);
LinkListErase1(phead,pos);
}
//删除链表中所有指定的元素
void LinkListRemoveAll(LinkNode** phead,LinkNodeType value){
if(phead == NULL){
return;
}
if(*phead == NULL){
return;
}
while(1){
LinkNode* to_delete = LinkListFind(*phead,value);
if(to_delete == NULL){
break;
}
LinkListErase1(phead,to_delete);
}
return;
}
//判断链表是否为空,为空返回1 不为空返回0
int LinkListEmpty(LinkNode* phead){
return phead == NULL ? 1 : 0;
}
//求链表的长度
size_t LinkListSize(LinkNode* phead){
size_t count = 1;
if(phead == NULL){
return 0;
}
LinkNode* cur = phead;
for(;cur->next != NULL;cur = cur->next){
count++;
}
return count;
}
测试函数:main.c
#include"linklist.h"
#include<stdio.h>
void TestInit(){
TestType;
LinkNode* phead;
LinkListInit(&phead);
printf("phead = %p\n",phead);
}
void TestPushBack(){
TestType;
LinkNode* phead;
LinkListInit(&phead);
LinkListPushBack(&phead,'a');
LinkListPushBack(&phead,'b');
LinkListPushBack(&phead,'c');
LinkListPushBack(&phead,'d');
LinkListPrintChar(phead," 尾部插入");
LinkListPopBack(&phead);
LinkListPrintChar(phead," 尾部删除 ");
}
void TestLinkListPushFront(){
TestType;
LinkNode* phead;
LinkListInit(&phead);
LinkListPushBack(&phead,'a');
LinkListPushBack(&phead,'b');
LinkListPushBack(&phead,'c');
LinkListPushBack(&phead,'d');
LinkListPrintChar(phead," 尾部插入 ");
LinkListPushFront(&phead,'x');
LinkListPrintChar(phead," 头部插入 ");
LinkListPopFront(&phead);
LinkListPrintChar(phead," 头部删除 ");
}
void TestLinkListFind(){
TestType;
LinkNode* phead;
LinkNode* cur;
LinkListInit(&phead);
LinkListPushBack(&phead,'a');
LinkListPushBack(&phead,'b');
LinkListPushBack(&phead,'c');
LinkListPushBack(&phead,'d');
LinkListPushBack(&phead,'e');
LinkListPrintChar(phead," 插入五个元素 ");
cur = LinkListFind(phead,'d');
printf("要查找的元素位于[%p]\n",cur);
}
void TestLinkListInsert(){
TestType;
LinkNode* phead;
LinkListInit(&phead);
LinkListInsert(&phead,phead,'x');
LinkListPrintChar(phead," 对空链表插入 ");
LinkListPushBack(&phead,'a');
LinkListPushBack(&phead,'b');
LinkListPushBack(&phead,'c');
LinkListPushBack(&phead,'d');
LinkListPushBack(&phead,'e');
LinkListPrintChar(phead," 尾部插入五个元素 ");
LinkListInsert(&phead,NULL,'y');
LinkListPrintChar(phead," pos = NULL ");
LinkListInsert(&phead,phead,'z');
LinkListPrintChar(phead," pos = phead ");
LinkNode* pos = LinkListFind(phead,'c');
LinkListInsert(&phead,pos,'m');
LinkListPrintChar(phead," c位置插入m ");
}
void TestLinkListInsertAfter(){
TestType;
LinkNode* phead;
LinkListInit(&phead);
LinkListInsertAfter(&phead,phead,'x');
LinkListPrintChar(phead," 对空链表插入 ");
LinkListPushBack(&phead,'a');
LinkListPushBack(&phead,'b');
LinkListPushBack(&phead,'c');
LinkListPushBack(&phead,'d');
LinkListPushBack(&phead,'e');
LinkListPrintChar(phead," 尾部插入五个元素 ");
LinkListInsertAfter(&phead,NULL,'y');
LinkListInsertAfter(&phead,phead,'z');
LinkListPrintChar(phead," pos = phead ");
LinkNode* pos = LinkListFind(phead,'c');
LinkListInsertAfter(&phead,pos,'m');
LinkListPrintChar(phead," c位置插入m ");
}
TestLinkListErase1(){
TestType;
LinkNode* phead;
LinkListInit(&phead);
LinkListPushBack(&phead,'a');
LinkListPushBack(&phead,'b');
LinkListPushBack(&phead,'c');
LinkListPushBack(&phead,'d');
LinkNode* pos = LinkListFind(phead,'d');
LinkListErase1(&phead,phead);
LinkListPrintChar(phead," 删除头节点 ");
LinkListErase1(&phead,pos);
LinkListPrintChar(phead," 删除d元素 ");
}
TestLinkListErase2(){
TestType;
LinkNode* phead;
LinkListInit(&phead);
LinkListPushBack(&phead,'a');
LinkListPushBack(&phead,'b');
LinkListPushBack(&phead,'c');
LinkListPushBack(&phead,'d');
LinkListPrintChar(phead," 尾插四个元素 ");
LinkNode* posa = LinkListFind(phead,'a');
LinkNode* posd = LinkListFind(phead,'d');
LinkListErase2(&phead,&posa);
LinkListPrintChar(phead," 删除a元素 ");
LinkListErase2(&phead,&posd);
LinkListPrintChar(phead," 删除d元素 ");
}
TestLinkListRemove(){
TestType;
LinkNode* phead;
int empty;
int count;
LinkListInit(&phead);
LinkListPushBack(&phead,'a');
LinkListPushBack(&phead,'b');
LinkListPushBack(&phead,'c');
LinkListPushBack(&phead,'d');
LinkListPrintChar(phead," 尾插四个元素 ");
empty = LinkListEmpty(phead);
printf("empty = [%d]\n",empty);
count = LinkListSize(phead);
printf("数组长度为:%d\n\n",count);
LinkListRemove(&phead,'c');
LinkListPrintChar(phead," 删除元素c ");
}
TestLinkListRemoveAll(){
TestType;
LinkNode* phead;
LinkListInit(&phead);
LinkListPushBack(&phead,'b');
LinkListPushBack(&phead,'a');
LinkListPushBack(&phead,'c');
LinkListPushBack(&phead,'b');
LinkListPushBack(&phead,'a');
LinkListPushBack(&phead,'b');
LinkListPushBack(&phead,'c');
LinkListPushBack(&phead,'b');
LinkListPrintChar(phead," 尾部插入元素 ");
LinkListRemoveAll(&phead,'b');
LinkListPrintChar(phead," 删除所有的b ");
}
int main(){
TestInit();
TestPushBack();
TestLinkListPushFront();
TestLinkListFind();
TestLinkListInsert();
TestLinkListInsertAfter();
TestLinkListErase1();
TestLinkListErase2();
TestLinkListRemove();
TestLinkListRemoveAll();
return 0;
}
Makefile
linklist:linklist.c main.c
gcc $^ -o -g $@
.PHONY:clean
clean:
rm linklist
这是单链表的一些基本操作,在写单链表函数的时候最好在纸上画一下链表,只要理清增删改查得思路,代码自然而然的就能写出来,还有一些其它的操作,我将在之后的博客中介绍。
由于个人能力有限,以上的说明可能有Bug或者有什么不妥之处,欢迎发邮件到我的邮箱( Cyrus_wen@163.com )批评指正!