// list.h
#pragma once
#ifndef __LIST_H
#define __LIST_H
typedef struct _LinkNode {
void* data;
struct _LinkNode* next;
} LinkList, LinkNode;
typedef struct _List {
LinkList* head;
LinkList* tail;
int (*Compar)(const void* key1, const void* key2);
void (*Destroy)(void* data);
void (*Display)(void* data);
} List;
#define LIST_DATA(node) ((node)->data)
#define LIST_NEXT(node) ((node)->next)
#define LIST_HEAD(L) ((L)->head)
#define LIST_TAIL(L) ((L)->tail)
void ListInit(List* L,
void (*Destroy)(void* data),
int (*Compar)(const void* key1, const void* key2),
void (*Display)(void* data));
void ListDestroy(List* L);
int ListEmpty(List* L);
int ListInsNext(List* L, LinkNode* node, const void* data);
int ListRemNext(List* L, LinkNode* node, void** data);
int ListSize(List* L);
int ListInsByIdx(List* L, int idx, const void* data);
int ListRemByIdx(List* L, int idx, void** data);
LinkNode* ListSearch(List* L, const void* data);
void ListDisplay(List* L);
void ListInit_WH(List* L,
void (*Destroy)(void* data),
int (*Compar)(const void* key1, const void* key2),
void (*Display)(void* data));
void ListDestroy_WH(List* L);
int ListEmpty_WH(List* L);
int ListInsNext_WH(List* L, LinkNode* node, const void* data);
int ListRemNext_WH(List* L, LinkNode* node, void** data);
int ListSize_WH(List* L);
int ListInsByIdx_WH(List* L, int idx, const void* data);
int ListRemByIdx_WH(List* L, int idx, void** data);
LinkNode* ListSearch_WH(List* L, const void* data);
void ListDisplay_WH(List* L);
#endif
// list.c
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "list.h"
/* 不带头结点的单链表操作 */
void ListInit(List* L,
void (*Destroy)(void* data),
int (*Compar)(const void* key1, const void* key2),
void (*Display)(void* data))
{
LIST_HEAD(L) = NULL;
LIST_TAIL(L) = NULL;
L->Destroy = Destroy;
L->Compar = Compar;
L->Display = Display;
return;
}
void ListDestroy(List* L)
{
LinkNode* p;
for (p = LIST_HEAD(L); p != NULL; p = LIST_NEXT(p)) {
L->Destroy(LIST_DATA(p));
free(p);
}
(void)memset(L, 0, sizeof(List));
return;
}
int ListEmpty(List* L)
{
if (!LIST_HEAD(L)) {
return 1;
}
else {
return 0;
}
}
int ListInsNext(List* L, LinkNode* node, const void* data)
{
LinkNode* newNode;
newNode = (LinkNode*)malloc(sizeof(LinkNode));
if (!newNode) { return -1; }
newNode->data = (void*)data;
newNode->next = NULL;
/* 当node为NULL时,表示在链表第一个结点之前插入 */
if (node == NULL) {
if (ListEmpty(L)) {
LIST_TAIL(L) = newNode;
}
newNode->next = LIST_HEAD(L);
LIST_HEAD(L) = newNode;
}
else {
if (node->next == NULL) {
LIST_TAIL(L) = newNode;
}
newNode->next = node->next;
node->next = newNode;
}
return 0;
}
/* node为NULL,表示删除第一个结点 */
int ListRemNext(List* L, LinkNode* node, void** data)
{
LinkNode* p;
if (ListEmpty(L)) {
*data = NULL;
return 0;
}
if (node == NULL) {
p = LIST_HEAD(L);
*data = p->data;
LIST_HEAD(L) = p->next;
if (p->next == NULL) { /* 链表只有一个结点 */
LIST_TAIL(L) = NULL;
}
}
else {
if (node == LIST_TAIL(L)) {
*data = NULL;
return 0;
}
else {
p = node->next;
*data = p->data;
node->next = p->next;
if (p->next == NULL) {
LIST_TAIL(L) = node;
}
}
}
free(p);
return 0;
}
LinkNode* ListSearch(List* L, const void* data)
{
LinkNode* p;
for (p = LIST_HEAD(L); p != NULL; p = LIST_NEXT(p)) {
if (L->Compar(p->data, (void*)data) == 0) {
return p;
}
}
return NULL;
}
int ListSize(List* L)
{
int size = 0;
LinkNode* p;
for (p = LIST_HEAD(L); p != NULL; p = LIST_NEXT(p)) {
size++;
}
return size;
}
int ListInsByIdx(List* L, int idx, const void* data)
{
int size;
int i;
LinkNode* p;
size = ListSize(L);
if (idx < 1 || idx > size + 1) {
return -1;
}
if (idx == 1) {
return ListInsNext(L, NULL, data);
}
if (idx == size + 1) {
return ListInsNext(L, LIST_TAIL(L), data);
}
i = 1;
p = LIST_HEAD(L);
while (p && (i < idx - 1)) {
p = LIST_NEXT(p);
i++;
}
return ListInsNext(L, p, data);
}
int ListRemByIdx(List* L, int idx, void** data)
{
int size;
int i;
LinkNode* p;
size = ListSize(L);
if (idx < 1 || idx > size) {
return -1;
}
if (idx == 1) {
return ListRemNext(L, NULL, data);
}
i = 1;
p = LIST_HEAD(L);
while (p && (i < idx - 1)) {
p = LIST_NEXT(p);
i++;
}
return ListRemNext(L, p, data);
}
void ListDisplay(List* L)
{
LinkNode* p;
if (ListEmpty(L)) {
printf("\nList is Empty.");
return ;
}
p = LIST_HEAD(L)->data;
printf("\nHEAD:%d", *(int*)p);
p = LIST_TAIL(L)->data;
printf("\nTAIL:%d\n", *(int*)p);
for (p = LIST_HEAD(L); p != NULL; p = LIST_NEXT(p)) {
L->Display(p->data);
}
return;
}
/* 带头结点的单链表操作 */
void ListInit_WH(List* L,
void (*Destroy)(void* data),
int (*Compar)(const void* key1, const void* key2),
void (*Display)(void* data))
{
LinkNode* p;
p = (LinkNode*)malloc(sizeof(LinkNode));
if (!p) { return ; }
p->next = NULL;
LIST_HEAD(L) = p;
LIST_TAIL(L) = p;
L->Destroy = Destroy;
L->Compar = Compar;
L->Display = Display;
return;
}
void ListDestroy_WH(List* L)
{
LinkNode* p;
for (p = LIST_HEAD(L)->next; p != NULL; p = LIST_NEXT(p)) {
L->Destroy(LIST_DATA(p));
free(p);
}
if (LIST_HEAD(L)) { free(LIST_HEAD(L)); }
(void)memset(L, 0, sizeof(List));
return;
}
int ListEmpty_WH(List* L)
{
LinkNode* p = LIST_HEAD(L)->next;
if (!p) {
return 1;
}
else {
return 0;
}
}
int ListInsNext_WH(List* L, LinkNode* node, const void* data)
{
LinkNode* newNode;
if (!node) { return -1; }
newNode = (LinkNode*)malloc(sizeof(LinkNode));
if (!newNode) { return -1; }
newNode->data = (void*)data;
newNode->next = NULL;
if (node->next == NULL) {
LIST_TAIL(L) = newNode;
}
newNode->next = node->next;
node->next = newNode;
return 0;
}
int ListRemNext_WH(List* L, LinkNode* node, void** data)
{
LinkNode* p;
if (ListEmpty_WH(L)) {
*data = NULL;
return 0;
}
if (node == LIST_TAIL(L)) {
*data = NULL;
return 0;
}
else {
p = node->next;
*data = p->data;
node->next = p->next;
if (p->next == NULL) {
LIST_TAIL(L) = node;
}
}
free(p);
return 0;
}
LinkNode* ListSearch_WH(List* L, const void* data)
{
LinkNode* p;
for (p = LIST_HEAD(L)->next; p != NULL; p = LIST_NEXT(p)) {
if (L->Compar(p->data, (void*)data) == 0) {
return p;
}
}
return NULL;
}
int ListSize_WH(List* L)
{
int size = 0;
LinkNode* p;
for (p = LIST_HEAD(L)->next; p != NULL; p = LIST_NEXT(p)) {
size++;
}
return size;
}
int ListInsByIdx_WH(List* L, int idx, const void* data)
{
int size;
int i;
LinkNode* p;
size = ListSize_WH(L);
if (idx < 1 || idx > size + 1) {
return -1;
}
if (idx == size + 1) {
return ListInsNext_WH(L, LIST_TAIL(L), data);
}
i = 0;
p = LIST_HEAD(L);
while (p && (i < idx - 1)) {
p = LIST_NEXT(p);
i++;
}
return ListInsNext_WH(L, p, data);
}
int ListRemByIdx_WH(List* L, int idx, void** data)
{
int size;
int i;
LinkNode* p;
size = ListSize(L);
if (idx < 1 || idx > size) {
return -1;
}
i = 0;
p = LIST_HEAD(L);
while (p && (i < idx - 1)) {
p = LIST_NEXT(p);
i++;
}
return ListRemNext(L, p, data);
}
void ListDisplay_WH(List* L)
{
LinkNode* p;
if (ListEmpty_WH(L)) {
printf("\nList is Empty.");
return;
}
p = LIST_HEAD(L)->next->data;
printf("\nHEAD:%d", *(int*)p);
p = LIST_TAIL(L)->data;
printf("\nTAIL:%d\n", *(int*)p);
for (p = LIST_HEAD(L)->next; p != NULL; p = LIST_NEXT(p)) {
L->Display(p->data);
}
return;
}
// main.c
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "list.h"
void Destroy(void* data)
{
if (data) {
free((int*)data);
}
}
int Compar(const void* key1, const void* key2)
{
return *(int*)key1 - *(int*)key2;
}
void Display(void* data)
{
printf("%d\t", *(int*)data);
}
void MenuList(void)
{
printf("\n 单链表的操作");
printf("\n==================================");
printf("\n| 1--第一个结点之前插入");
printf("\n| 2--第一个结点之后插入");
printf("\n| 3--最后一个结点之后插入");
printf("\n| 4--指定位置插入");
printf("\n| 5--删除第一个结点");
printf("\n| 6--指定位置删除");
printf("\n| 7--查找");
printf("\n| 8--显示");
printf("\n| 0--返回");
printf("\n==================================");
printf("\n请输入菜单号(0~8):");
}
int ListOperation(void)
{
List L;
char ch1, ch2;
int elem = 12345678, idx = 0;
int* oldElem, * newElem;
int ret;
ListInit(&L, Destroy, Compar, Display);
ch1 = 'y';
while (ch1 == 'y' || ch1 == 'Y') {
MenuList();
scanf_s("%c", &ch2, 1);
(void)getchar();
switch (ch2) {
case '1':
printf("\n请输入插入的元素值:");
scanf_s("%d", &elem);
(void)getchar();
newElem = (int*)malloc(sizeof(int));
if (!newElem) { exit(1); }
*newElem = elem;
ret = ListInsNext(&L, NULL, (void*)newElem);
if (!ret) {
printf("\n插入成功!");
}
else {
printf("\n插入失败");
Destroy((void*)newElem);
}
break;
case '2':
printf("\n请输入插入的元素值:");
scanf_s("%d", &elem);
(void)getchar();
newElem = (int*)malloc(sizeof(int));
if (!newElem) { exit(1); }
*newElem = elem;
ret = ListInsNext(&L, LIST_HEAD(&L), (void*)newElem);
if (!ret) {
printf("\n插入成功!");
}
else {
printf("\n插入失败");
}
break;
case '3':
printf("\n请输入插入的元素值:");
scanf_s("%d", &elem);
(void)getchar();
newElem = (int*)malloc(sizeof(int));
if (!newElem) { exit(1); }
*newElem = elem;
ret = ListInsNext(&L, LIST_TAIL(&L), (void*)newElem);
if (!ret) {
printf("\n插入成功!");
}
else {
printf("\n插入失败");
}
break;
case '4':
printf("\n请输入插入的位置(1~%d)和元素值:", ListSize(&L) + 1);
scanf_s("%d,%d", &idx, &elem);
(void)getchar();
newElem = (int*)malloc(sizeof(int));
if (!newElem) { exit(1); }
*newElem = elem;
ret = ListInsByIdx(&L, idx, (void*)newElem);
if (!ret) {
printf("\n插入成功!");
}
else {
printf("\n插入失败");
}
break;
case '5':
ret = ListRemNext(&L, NULL, (void**)(&oldElem));
if (!ret) {
printf("\n删除成功!");
}
else {
printf("\n删除失败!");
}
break;
case '6':
if (!ListSize(&L)) {
printf("\n链表序列为空!");
break;
}
printf("\n请输入结点位置(1~%d):", ListSize(&L));
scanf_s("%d", &idx);
(void)getchar();
ret = ListRemByIdx(&L, idx, (void**)(&oldElem));
if (!ret) {
printf("\n删除成功!");
}
else {
printf("\n删除失败!");
}
break;
case '7':
printf("\n请输入要查找的元素值:");
scanf_s("%d", &elem);
(void)getchar();
if (ListSearch(&L, (void*)(&elem))) {
printf("\n查找成功!");
}
else {
printf("\n查找失败");
}
break;
case '8':
printf("\n链表序列:");
ListDisplay(&L);
break;
case '0':
ch1 = 'n';
break;
default:
printf("\n输入有误,请输入0~8进行选择!");
break;
}
}
return 0;
}
int ListOperation_WH(void)
{
List L;
char ch1, ch2;
int elem, idx;
int* oldElem, * newElem;
int ret;
ListInit_WH(&L, Destroy, Compar, Display);
ch1 = 'y';
while (ch1 == 'y' || ch1 == 'Y') {
MenuList();
scanf_s("%c", &ch2, 1);
(void)getchar();
switch (ch2) {
case '1':
printf("\n请输入插入的元素值:");
scanf_s("%d", &elem);
(void)getchar();
newElem = (int*)malloc(sizeof(int));
if (!newElem) { exit(1); }
*newElem = elem;
ret = ListInsNext_WH(&L, LIST_HEAD(&L), (void*)newElem);
if (!ret) {
printf("\n插入成功!");
}
else {
printf("\n插入失败");
Destroy((void*)newElem);
}
break;
case '2':
printf("\n请输入插入的元素值:");
scanf_s("%d", &elem);
(void)getchar();
newElem = (int*)malloc(sizeof(int));
if (!newElem) { exit(1); }
*newElem = elem;
ret = ListInsNext_WH(&L, LIST_HEAD(&L)->next, (void*)newElem);
if (!ret) {
printf("\n插入成功!");
}
else {
printf("\n插入失败");
}
break;
case '3':
printf("\n请输入插入的元素值:");
scanf_s("%d", &elem);
(void)getchar();
newElem = (int*)malloc(sizeof(int));
if (!newElem) { exit(1); }
*newElem = elem;
ret = ListInsNext_WH(&L, LIST_TAIL(&L), (void*)newElem);
if (!ret) {
printf("\n插入成功!");
}
else {
printf("\n插入失败");
}
break;
case '4':
printf("\n请输入插入的位置(1~%d)和元素值:", ListSize_WH(&L) + 1);
scanf_s("%d,%d", &idx, &elem);
(void)getchar();
newElem = (int*)malloc(sizeof(int));
if (!newElem) { exit(1); }
*newElem = elem;
ret = ListInsByIdx_WH(&L, idx, (void*)newElem);
if (!ret) {
printf("\n插入成功!");
}
else {
printf("\n插入失败");
}
break;
case '5':
ret = ListRemNext_WH(&L, LIST_HEAD(&L), (void**)(&oldElem));
if (!ret) {
printf("\n删除成功!");
}
else {
printf("\n删除失败");
}
break;
case '6':
if (!ListSize_WH(&L)) {
printf("\n链表序列为空!");
break;
}
printf("\n请输入结点位置(1~%d):", ListSize_WH(&L));
scanf_s("%d", &idx);
(void)getchar();
ret = ListRemByIdx_WH(&L, idx, (void**)(&oldElem));
if (!ret) {
printf("\n删除成功!");
}
else {
printf("\n删除失败");
}
break;
case '7':
printf("\n请输入要查找的元素值:");
scanf_s("%d", &elem);
(void)getchar();
if (ListSearch_WH(&L, (void*)(&elem))) {
printf("\n查找成功!");
}
else {
printf("\n查找失败");
}
break;
case '8':
printf("\n链表序列:");
ListDisplay_WH(&L);
break;
case '0':
ch1 = 'n';
break;
default:
printf("\n输入有误,请输入0~8进行选择!");
break;
}
}
return 0;
}
int main(int* argc, char** argv)
{
(void)ListOperation(); /* 不带头结点的单链表操作 */
//(void)ListOperation_WH(); /* 带头结点的单链表操作 */
return 0;
}
执行结果:
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):1
请输入插入的元素值:100
插入成功!
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):2
请输入插入的元素值:200
插入成功!
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):3
请输入插入的元素值:300
插入成功!
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):8
链表序列:
HEAD:100
TAIL:300
100 200 300
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):4
请输入插入的位置(1~4)和元素值:1,500
插入成功!
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):8
链表序列:
HEAD:500
TAIL:300
500 100 200 300
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):4
请输入插入的位置(1~5)和元素值:5,600
插入成功!
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):8
链表序列:
HEAD:500
TAIL:600
500 100 200 300 600
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):4
请输入插入的位置(1~6)和元素值:2,700
插入成功!
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):8
链表序列:
HEAD:500
TAIL:600
500 700 100 200 300 600
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):5
删除成功!
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):8
链表序列:
HEAD:700
TAIL:600
700 100 200 300 600
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):6
请输入结点位置(1~5):3
删除成功!
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):8
链表序列:
HEAD:700
TAIL:600
700 100 300 600
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):7
请输入要查找的元素值:34
查找失败
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):7
请输入要查找的元素值:700
查找成功!
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):8
链表序列:
HEAD:700
TAIL:600
700 100 300 600
单链表的操作
==================================
| 1--第一个结点之前插入
| 2--第一个结点之后插入
| 3--最后一个结点之后插入
| 4--指定位置插入
| 5--删除第一个结点
| 6--指定位置删除
| 7--查找
| 8--显示
| 0--返回
==================================
请输入菜单号(0~8):