单链表C/C++实现
#include <stdio.h>
#include "malloc.h"
typedef int ElemType;
typedef struct LNode
{ // 定义单链表节点类型
ElemType data; // 数据域
struct LNode *next; // 指针域
} LNode, *LinkList;
// 初始化
// 无头结点
// bool InitList(LinkList &L){
// L = NULL; //空表,暂时还没有人和结点
// return true;
// }
// 带头结点
bool InitList(LinkList &L)
{
L = (LNode *)malloc(sizeof(LNode)); // 分配一个头结点
if (L == NULL) // 内存不足,分配失败
return false;
L->next = NULL; // 头结点的指针域指向空值
L->data = 0; // 头结点的数据域用于存放结点个数 不存放也可以,我选择存放
return true;
}
// 判断是否为空表
bool Empty(LinkList L)
{
return L == NULL;
}
// 创建表
// 头插法
void List_HeadInsert(LinkList &L)
{
int x, n;
L = (LNode *)malloc(sizeof(LNode)); // 分配头结点
LNode *s; // 临时结点用于存放数据
L->next = NULL; // 初始为空链表
printf("请输入你要创建单莲表结点的个数\n");
scanf("%d", &n);
printf("请输入你要插入的数据\n");
for (int i = 0; i < n; i++)
{
scanf("%d", &x); // 输入节点的值
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
}
L->data = n; // 用头结点存放结点个数 不存放也可以,我选择存放
}
//尾插法
void List_TailInsert(LinkList &L)
{
int x, n;
L = (LNode *)malloc(sizeof(LNode)); // 分配头结点
LNode *s,*r=L; // s用于存放数据,r表示尾部结点
printf("请输入你要创建单莲表结点的个数\n");
scanf("%d", &n);
printf("请输入你要插入的数据\n");
for (int i = 0; i < n; i++)
{
scanf("%d", &x); // 输入节点的值
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
r->next = s;
r = s;
}
r->next = NULL;
L->data = n; // 用头结点存放结点个数 不存放也可以,我选择存放
}
// 展示表
bool PrintList(LinkList L)
{
if (L->next == NULL||L->data == 0)
{
printf("表是空表\n");
return false;
}
LNode *s;
s = L->next; // 将第一个数据结点赋值给s
int n = L->data; // 定义n为结点个数
for (int i = 0; i < n; i++)
{
printf("第%d个数据元素为%d\n", i + 1, s->data);
s = s->next; // 遍历下一个结点
}
return true;
}
//按位查找
LNode *GetElem(LinkList L,int i){
if(L==NULL||i<1)
return NULL;
LNode *p = L->next; //从第一个节点开始找
int j = 1;
while(p!=NULL&&j<i){
p = p->next;
j++;
}
if(p==NULL)
return NULL;
return p;
}
//按值查找
LNode *LocateElem(LinkList L,ElemType e){
if(L==NULL)
return NULL;
LNode *p = L->next; //从第一个节点开始
while(p!=NULL&&p->data!=e)
p = p->next;
if(p==NULL)
return NULL;
return p;
}
// 插入结点
// 在第i个位置插入元素e(带头结点)
bool ListInsert(LinkList &L,int i,ElemType e){
if(i<1)
return false;
LNode *p; //指针p指向当前扫描到的结点
int j = 0; //当前p指向的是第几个结点,0表示头结点
p = L; //L指向头结点
while(p!=NULL&&j<i-1){ //循环找到第i-1个结点
p = p->next;
j++;
}
if(p==NULL) //i值不合法
return false;
LNode *s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
L->data += 1;
return true;
}
//最好时间复杂度O(1),最坏、平均时间复杂度O(n)
//插入操作的子操作:前插操作和后插操作
//指定节点的后插操作
//后插操作:在p结点之后插入元素e
bool InsertNextNode(LNode *p,ElemType e){
if(p==NULL)
return false;
LNode *s = (LNode*)malloc(sizeof(LNode));
if(s==NULL) //内存分配失败
return false;
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
//可是这样的话L->data的数据长度就失效了。个人认为这个函数可有可无
//时间复杂度O(1)
//前插操作
//前插操作:在p结点之前插入元素e
bool InsertPriorNode(LNode *p,ElemType e){
if(p==NULL)
return false;
LNode *s = (LNode*)malloc(sizeof(LNode));
if(s==NULL) //内存分配失败
return false;
//插入s到p后面
s->next = p->next;
p->next = s;
//把p->data的值赋给s
s->data = p->data;
//p->data重新赋值为e
p->data = e;
return true;
}
//时间复杂度O(1)
//封装的插入操作
//结合按位查找和按值查找之后插入节点可以这么写
bool SimpleListInsert(LinkList &L,int i,ElemType e){
if(i<1)
return false;
//找到第i-1的结点
LNode *p = GetElem(L,i-1);
if(p==NULL)
return false;
//后插操作,在i-1后面,就是i的位置插入结点
InsertNextNode(p,e); //实际上可以直接return InsertNextNode(p,e);但是我有L->data += 1;所以不可以这么写
L->data += 1;
return true;
}
//删除结点
//删除第i个位置,并返回值到e(带头结点)
bool ListDelete(LinkList &L,int i,ElemType &e){
if(i<1)
return false;
LNode *p=L,*q;
int j = 0;
while(p!=NULL&&j<i-1){
p = p->next;
j++;
}
q = p->next;
e = q->data;
p->next = q->next;
free(q); //释放结点的内存
L->data -= 1;
return true;
}
//最好时间复杂度O(1),最坏、平均时间复杂度O(n)
//删除节点的子操作
//指定节点删除
bool DeleteNode(LNode *p,ElemType &e){
if(p==NULL)
return false;
LNode *q = p->next;
e = p->data;
//把p的下一个结点q的data值赋给p->data
//注:因为被删除的是p结点,所以p结点的值被覆盖了也无所谓;或者说p的data值已经赋给了e
p->data = q->data;
//p指向下下一个结点,相当于删除q结点,但是把q结点的值赋给了p
p->next = q->next;
free(q);
return true;
}
//如果删除节点是最后一个结点的话,会出现空指针错误。
//不能实现L->data同步
//封装的删除操作
//综合按位查找函数和删除指定结点函数可以这么写
bool SimpleListDelete(LinkList &L,int i,ElemType &e){
if(i<1)
return false;
//找到第i个结点
LNode *p = GetElem(L,i);
if(p==NULL)
return false;
//删除第i个结点
DeleteNode(p,e);
L->data -= 1;
return true;
}
//求表长操作
int Length(LinkList L){
return L->data;
}
//这是因为我用L->data存储表长了,当然可以遍历全表然后len++ return len;
//销毁操作
void Destroy(LinkList &L){
L->data = 0;
LNode *p = L;
while (p!=NULL)
{
L = L->next;
free(p);
p = L;
}
}
int main(int argc, char const *argv[])
{
ElemType e;
LinkList L; // 声明一个指向单链表的指针
// 初始化一个空表
InitList(L);
PrintList(L);
printf("--------------------------------\n");
// 头插法
// List_HeadInsert(L);
// PrintList(L);
// 尾插法
List_TailInsert(L);
PrintList(L);
printf("--------------------------------\n");
// 插入操作
// ListInsert(L,2,666);
// PrintList(L);
// 删除操作
// ListDelete(L,2,e);
// PrintList(L);
// printf("e=%d",e);
// 前插操作
// InsertPriorNode(L->next->next,666);
// // 相当于展示表
// LNode *s;
// s = L->next; // 将第一个数据结点赋值给s
// int n = L->data+1; // 定义n为结点个数 因为前插函数插入了一个数据,但是没有同步L.data
// for (int i = 0; i < n; i++)
// {
// printf("第%d个数据元素为%d\n", i + 1, s->data);
// s = s->next; // 遍历下一个结点
// }
// 按位查找
// LNode *p = GetElem(L,3);
// printf("p->data=%d",p->data);
// 按值查找
// LNode *p = LocateElem(L,5);
// printf("p->next->next->data=%d",p->next->next->data);
// 删除指定节点
// DeleteNode(L->next->next,e);
// //相当于展示表
// LNode *s;
// s = L->next; // 将第一个数据结点赋值给s
// int n = L->data-1; // 定义n为结点个数 因为删除指定节点函数删除了一个数据,但是没有同步L.data
// for (int i = 0; i < n; i++)
// {
// printf("第%d个数据元素为%d\n", i + 1, s->data);
// s = s->next; // 遍历下一个结点
// }
// printf("e=%d",e);
// 封装后的插入操作
// SimpleListInsert(L,3,666);
// PrintList(L);
// 封装后的删除操作
// SimpleListDelete(L,3,e);
// PrintList(L);
// printf("e=%d",e);
//销毁操作
Destroy(L);
return 0;
}