无头单向非循换链表的实现
文章目录
1.无头单向非循环链表的功能API框架
对于无头单向非循环链表代码实现的功能接口(API)框架:
//以下为无头单向非循环链表功能API框架 //基本增删查改接口API // 动态申请一个节点 SListNode* BuySListNode(SLTDateType x); // 单链表打印 void SListPrint(SListNode* plist); // 单链表尾插 void SListPushBack(SListNode** pplist, SLTDateType x); // 单链表的头插 void SListPushFront(SListNode** pplist, SLTDateType x); // 单链表的尾删 void SListPopBack(SListNode** pplist); // 单链表头删 void SListPopFront(SListNode** pplist); // 单链表查找 SListNode* SListFind(SListNode* plist, SLTDateType x); // 单链表在pos位置之后插入x void SListInsertAfter(SListNode* pos, SLTDateType x); // 单链表删除pos位置之后的值 void SListEraseAfter(SListNode* pos);
2.无头单向非循环链表的功能API代码实现
0.无头单向非循环链表的结构定义:
// 1、无头+单向+非循环链表增删查改实现 typedef int SLTDateType; //链表结点结构 typedef struct SListNode { SLTDateType data;//数据域 struct SListNode* next;//指针域 }SListNode;
1.动态申请一个结点:
//动态申请一个节点 SListNode* BuySListNode(SLTDataType x) { SListNode* newnode = (SListNode*)malloc(sizeof(SListNode)); if (newnode == NULL) { printf("malloc fail\n"); exit(-1); } else { newnode->data = x; newnode->next = NULL; } return newnode; }
2.销毁链表所有结点:
//思路:链表销毁只能一个结点一个结点的销毁 //让结点指向下一个结点,然后销毁本结点 void SListDestroy(SListNode** pphead) { assert(pphead); //结点操作,不要直接操作原结点,而是创建结点来操作,操作完进行结点覆盖 SListNode* cur = *pphead;//创建一个指针指向当前节点 while (cur) { SListNode* next = cur->next;//创建一个节点指针让其后移 free(cur);//释放当前结点 cur = next;//更新新结点头 } *pphead = NULL;//结点销毁完毕,置空头结点 }
3.打印链表:
//思路:打印结点内容,然后让结点指针后移,又打印结点内容 void SListPrint(SListNode* phead) { SListNode* cur = phead; while (cur != NULL) { printf("%d->", cur->data); cur = cur->next; } printf("NULL\n"); }
4.单链表头插:
核心问题:为什么这里传参要使用二级指针?
原因:一级指针存放的是首结点的内容,如果我们使用一级指针,一级指针存放的是首结点的地址,修改首结点,使用*phead相当于直接使用的实参变量,而不是实参内存地址,随着函数栈帧的销毁,形参的改变不影响实参,实际没有实现功能!我们应该传入的是地址,因此需要二级指针来存储首结点地址
传参使用二级指针的地方:如果要对结点进行修改,那么就使用二级指针作为参数
头插思路:创建一个新结点,让新结点指向首结点,然后把存储首结点的指针指向新结点
void SListPushFront(SListNode** pphead, SLTDataType x) { assert(pphead); SListNode* newnode = BuySListNode(x); newnode->next = *pphead;//新结点连接首结点 *pphead = newnode;//更新存储首结点的指针 }
5.单链表头删:
头删思路:让存储首结点的指针指向第二个结点
void SListPopFront(SListNode** pphead) { assert(pphead); //情况分为: // 1、空 // 2、非空 if (*pphead == NULL) { return; } else { SListNode* next = (*pphead)->next;//,pphead表示首元素地址,*pphead表示的首结点 free(*pphead);//释放首结点 *pphead = next;//连接新结点 } }
6.单链表尾插:
尾插思路:找到链表尾部,创建新结点,插入在最后就行
void SListPushBack(SListNode** pphead, SLTDataType x) { assert(pphead); SListNode* newnode = BuySListNode(x); //考虑结点是否为NULL结点 if (*pphead == NULL) { *pphead = newnode; } else { // 找尾---条件是:尾节点的next为NULL,即tail->next=NULL SListNode* tail = *pphead; while (tail->next != NULL) { tail = tail->next; } tail->next = newnode; // 错误写法,这里没有链接起来 /*SListNode* tail = *pphead; while (tail != NULL) { tail = tail->next; } tail = newnode;*/ } }
7.单链表尾删:
尾删思路:找到倒数第二个结点,free掉尾结点,然后把倒数第二个结点的next指向NULL
void SListPopBack(SListNode** pphead) { assert(pphead); // 也可以暴力检查为空的情况 //assert(*pphead != NULL); // 1、空 // 2、一个节点 // 3、多个节点 if (*pphead == NULL) // 温柔检查 { return; } else if ((*pphead)->next == NULL) { free(*pphead); *pphead = NULL; } else { //格式一:推荐 SListNode* prev = NULL; SListNode* tail = *pphead; while (tail->next != NULL) { prev = tail; tail = tail->next; } free(tail); tail = NULL; prev->next = NULL; /*格式二 SListNode* tail = *pphead; while (tail->next->next != NULL) { tail = tail->next; } free(tail->next); tail->next = NULL; */ } }
8.在单链表中查找指定值的节点:
查找思路:遍历结点,进行值匹配
SListNode* SListFind(SListNode* phead, SLTDataType x) { SListNode* cur = phead; while (cur != NULL) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; }
9.单链表在pos位置之前插入:
pos位置之前插入思路:
//思路参考插入 void SListInsert(SListNode** pphead, SListNode* pos, SLTDataType x) { assert(pphead); assert(pos); // 1、pos是第一个节点 // 2、pos不是第一个节点 if (pos == *pphead) { SListPushFront(pphead, x); } else { SListNode* prev = *pphead; while (prev->next != pos) { prev = prev->next; } SListNode* newnode = BuySListNode(x); prev->next = newnode; newnode->next = pos; } }
10.单链表在pos位置之后插入:
//思路参考pos位置之前插入 void SListInsertAfter(SListNode* pos, SLTDataType x) { assert(pos); //SListNode* next = pos->next; //SListNode* newnode = BuySListNode(x); //pos->next = newnode; //newnode->next = next; SListNode* newnode = BuySListNode(x); newnode->next = pos->next; pos->next = newnode; }
11.单链表删除指定pos位置的节点:
pos位置删除思路:
void SListErase(SListNode** pphead, SListNode* pos) { assert(pphead); assert(pos); if (*pphead == pos) { SListPopFront(pphead); } else { SListNode* prev = *pphead; while (prev->next != pos) { prev = prev->next; } prev->next = pos->next; free(pos); pos = NULL; } }
12.求单链表长度:
//思路:遍历指针,设置计数标记 //求单链表长度 int SListSize(SListNode* phead) { int size = 0; SListNode* cur = phead; while (cur != NULL) //遍历链表 { size++; cur = cur->next; } return size; }
13.判断单链表是否为空:
//思路:判断头结点是否为NULL //单链表判空 bool SListEmpty(SListNode* phead) { //plist为空,返回1(true),非空,返回0(false) return phead == NULL; }
14.移除全部指定数据结点:
SListNode* SListremoveElements(SListNode** head, int val) { struct SListNode* prev = NULL;//用于中间转换 struct SListNode* cur = *head; while (cur) { //空结点 if (*head == NULL) return head; //一个结点满足条件 if (cur->next==NULL && cur->data == val) { struct List* newhead = NULL; newhead = cur->next; free(*head); *head = NULL; return newhead; } if (cur->data != val)//不满足条件 { prev = cur; cur = cur->next; *head = prev; } else { struct ListNode* next = cur->next; free(cur); cur = next; *head = cur; } } return head; }
3.无头单向非循环链表的功能API效果动态展示
4.无头单向非循环链表的功能API源代码
SList.h 头文件
#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLTDataType;
//链表结点结构
typedef struct SListNode
{
SLTDataType data; // val
struct SListNode* next; // 存储下一个节点的地址
}SListNode;
void SListPrint(SListNode* phead);
void SListPushBack(SListNode** pphead, SLTDataType x);
void SListPushFront(SListNode** pphead, SLTDataType x);
void SListPopBack(SListNode** pphead);
void SListPopFront(SListNode** pphead);
SListNode* SListFind(SListNode* phead, SLTDataType x);
// 在pos位置之前插入
void SListInsert(SListNode** pphead, SListNode* pos, SLTDataType x);
// 删除pos 位置
void SListErase(SListNode** pphead, SListNode* pos);
// 在pos之后插入
void SListInsertAfter(SListNode* pos, SLTDataType x);
// 删除pos位置后面的值
void SListEraseAfter(SListNode* pos);
void SListDestroy(SListNode** pphead);
//删除指定数据
SListNode* SListremoveElements(SListNode** head, int val);
SList.c 源文件
#include "SList.h"
void SListPrint(SListNode* phead)
{
SListNode* cur = phead;
while(cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
if (cur == NULL)
continue;
}
printf("NULL\n");
}
SListNode* BuySListNode(SLTDataType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
else
{
newnode->data = x;
newnode->next = NULL;
}
return newnode;
}
void SListPushBack(SListNode** pphead, SLTDataType x)
{
assert(pphead);
SListNode* newnode = BuySListNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
// 找尾
SListNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
// 错误写法,这里没有链接起来
/*SListNode* tail = *pphead;
while (tail != NULL)
{
tail = tail->next;
}
tail = newnode;*/
}
}
void SListPushFront(SListNode** pphead, SLTDataType x)
{
assert(pphead);
SListNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SListPopBack(SListNode** pphead)
{
assert(pphead);
// 也可以暴力检查为空的情况
//assert(*pphead != NULL);
// 1、空
// 2、一个节点
// 3、多个节点
if (*pphead == NULL) // 温柔检查
{
return;
}
else if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
/*SListNode* prev = NULL;
SListNode* tail = *pphead;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;*/
SListNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SListPopFront(SListNode** pphead)
{
assert(pphead);
// 1、空
// 2、非空
if (*pphead == NULL)
{
return;
}
else
{
SListNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
}
SListNode* SListFind(SListNode* phead, SLTDataType x)
{
SListNode* cur = phead;
while (cur != NULL)
{
if (cur->data == x)
{
printf("找到了目标结点\n");
return cur;
}
cur = cur->next;
}
return NULL;
}
// 在pos位置之前插入
void SListInsert(SListNode** pphead, SListNode* pos, SLTDataType x)
{
assert(pphead);
assert(pos);
// 1、pos是第一个节点
// 2、pos不是第一个节点
if (pos == *pphead)
{
SListPushFront(pphead, x);
}
else
{
SListNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SListNode* newnode = BuySListNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
assert(pos);
//SListNode* next = pos->next;
//SListNode* newnode = BuySListNode(x);
//pos->next = newnode;
//newnode->next = next;
SListNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
// 删除pos 位置
void SListErase(SListNode** pphead, SListNode* pos)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
SListPopFront(pphead);
}
else
{
SListNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
void SListEraseAfter(SListNode* pos)
{
assert(pos);
SListNode* next = pos->next;
if (next)
{
pos->next = next->next;
free(next);
next = NULL;
}
}
void SListDestroy(SListNode** pphead)
{
assert(pphead);
SListNode* cur = *pphead;
while (cur)
{
SListNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
SListNode* SListremoveElements(SListNode** head, int val) {
SListNode* prev = NULL;//用于中间转换
SListNode* cur = *head;
while (cur)
{
//空结点
if (*head == NULL)
return head;
//一个结点满足条件
if (cur->next==NULL && cur->data == val)
{
SListNode* newhead = NULL;
newhead = cur->next;
free(*head);
*head = NULL;
return newhead;
}
if (cur->data != val)//不满足条件
{
prev = cur;
cur = cur->next;
*head = prev;
}
else
{
SListNode* next = cur->next;
free(cur);
cur = next;
*head = cur;
}
}
return head;
}
main.c 源文件主函数
#define _CRT_SECURE_NO_WARNINGS
#include "SList.h"
void menu()
{
printf("**************************************************\n");
printf("------------无头单向非循环链表功能选择------------\n");
printf("**************************************************\n");
printf("1.头插数据0 2.头删数据043\n");
printf("3.尾插数据234 4.尾删数据234\n");
printf("5.指定位置0之后插入数据2 6.删除指定位置1数据2\n");
printf("7.指定查找数据1结点 8.删除指定数据内容2\n");
printf("0.退出程序\n");
printf("**************************************************\n");
}
int main()
{
//结点创建和初始化
SListNode* node1 = malloc(sizeof(SListNode));
SListNode* node2 = malloc(sizeof(SListNode));
SListNode* node3 = malloc(sizeof(SListNode));
SListNode* node4 = malloc(sizeof(SListNode));
node1->data = 4;
node2->data = 3;
node3->data = 2;
node4->data = 1;
//结点连接
node1->next = node2;
node2->next = node3;
node3->next = node4;
node4->next = NULL;
int choice = 0;
do
{
menu();
printf("请输入要选择的功能:");
scanf("%d", &choice);
switch (choice)
{
case 0:continue;
case 1:SListPushFront(&node1, 0); SListPrint(node1); break;
case 2:SListPopFront(&node1); SListPopFront(&node1); SListPopFront(&node1); SListPrint(node1); break;
case 3:SListPushBack(&node1, 2); SListPushBack(&node1, 3); SListPushBack(&node1, 4); SListPrint(node1); break;
case 4:SListPopBack(&node1); SListPopBack(&node1); SListPopBack(&node1); SListPrint(node1); break;
case 5:SListInsertAfter(node1, 2); SListPrint(node1); break;
case 6:SListEraseAfter(node1); SListPrint(node1); break;
case 7:SListFind(node1, 1); break;
case 8:SListremoveElements(&node1, 2); SListPrint(node1); break;
default:printf("功能选择错误,请输入数字0~8\n"); break;
}
} while (choice != 0);
printf("程序已退出,感谢使用无头单向非循环链表!\n");
system("pause");
return 0;
}