C/C++单链表从创建到实现(初学必看)

  • 这篇最适合刚学习单链表的同学,先从整体入手,搞清楚链表从0到1的实现过程,回头再看代码块会有更清晰的认识。
  • 看不懂的同学可多看看代码中的注释!!
  • VS中.cpp文件之间相互调用会出现LNK2005、LNK1169错误。也就是说,不借助头文件很有可能函数重定义报错。所以选择创建一个.h头文件(声明单链表结构体以及函数)和两个.cpp源文件(一个写函数的定义,另一个写主函数实现调用)

1.先上.h头文件(sq1.h)声明链表和函数(注意头文件固定格式

///头文件格式固定
//#ifndef SQLISTI_H
//#define SQLIST_H
//
//正文      //此处写相关结构体、函数等声明
//
//#endif

#ifndef SQLISTI_H
#define SQLIST_H

typedef int ElemType;
typedef struct LNode {        //单链表声明
	ElemType data;
	struct LNode* next;
}LNode;  
                              //各函数声明
void InitList(LNode*& L);
void CreatListHead(LNode*& L, ElemType a[], int n);
void CreatListTail(LNode*& L, ElemType a[], int n);
void DestroyList(LNode*& L);
bool ListEmpty(LNode* L);
int ListLength(LNode* L);
void DisppList(LNode* L);
bool GetElem(LNode* L, int i, ElemType& e);
int LocateElem(LNode* L, ElemType e);
bool ListInsert(LNode*& L, int i, ElemType e);
bool DeleteList(LNode*& L, int i, ElemType& e);


#endif

2.函数定义的.cpp源文件(sq1.cpp)

#include<stdio.h>
#include<stdlib.h>
#include"sq1.h"

//1.初始化链表
void InitList(LNode*& L)
{
	L = (LNode*)malloc(sizeof(LNode));
	L->next = NULL;
}

//2.头插法创建单链表
void CreatListHead(LNode*& L, ElemType a[], int n)
{
	printf("头插法创建演示:\n");
	LNode* s;                 //
	int i = 0;
	L = (LNode*)malloc(sizeof(LNode));    //为头结点开辟空间,“LNode*”类型的,开“LNode”长度的空间
	L->next = NULL;
	for (i = 0; i < n; i++)
	{
		s = (LNode*)malloc(sizeof(LNode));
		s->data = a[i];                   //新数据保存进s结点的数据域
		s->next = L->next;                //两步插入操作
		L->next = s;
		printf("插入第%d个,值为%d\n", i + 1, s->data);    //i为下标,从0开始算的,i+1是序数
	}

}

//3.尾插法创建单链表
void CreatListTail(LNode*& L, ElemType a[], int n)
{
	printf("尾插法创建演示:\n");
	LNode* s, * r;                         //不同于头插法,创建一个始终处于尾结点的r
	int i;
	L = (LNode*)malloc(sizeof(LNode));
	L->next = NULL;
	r = L;                                //结合下面的循环,让r成为每次插入s前的尾结点
	for (i = 0; i < n; i++)
	{
		s = (LNode*)malloc(sizeof(LNode));
		s->data = a[i];                   //新数据保存进s结点的数据域
		r->next = s;
		//printf("插入第%d个,值为%d\n", i, s->data);
		r = s;                            //更新r的位置
		printf("插入第%d个,值为%d\n", i + 1, s->data);
	}
	r->next = NULL;                       //插入完成后使尾结点的指针域为空
}

//4.销毁链表
void DestroyList(LNode*& L)
{
	//结点需要用free()函数逐个释放,所以可以看作乘客(每个结点)上车,
	//所以这里放出两辆“公交车”,结点们通过“换乘”抵达free()站
	LNode* pre = L;         //乘坐pre车直接去free()
	LNode* p = L->next;		//乘坐p车预备去free()
	while (p != NULL)
	{
		free(pre);
		//下面开始更新pre和p的位置(两辆车的乘客都更新)
		pre = p;
		p = pre->next;
	}
	free(pre);  //最后一位乘客需要pre车再跑一趟
}

//5.判断链表是否为空
bool ListEmpty(LNode* L)
{
	if (L->next == NULL)
		return true;
	else
		return false;
}

//6.测量链表长度
int ListLength(LNode* L)
{
	int i = 0;
	LNode* p = L->next;
	while (p != NULL)
	{
		i++;
		p = p->next;
	}
	return(i);
}

//7.输出链表
void DisppList(LNode* L)
{
	LNode* p = L->next;
	while (p != NULL)
	{
		printf("%d", p->data);
		p = p->next;
	}
	printf("\n");
}

//8.逻辑位序查找
bool GetElem(LNode* L, int i, ElemType& e)  //从头找第i个结点,若存在,将其结果赋值给e
{
	int j = 0;
	LNode* p = L;              //让p成为头结点
	while (j < i && p != NULL) //还没找完就继续找
	{
		j++;
		p = p->next;
	}
	if (p == NULL)
		return false;
	else
	{
		e = p->data;
		return true;
	}
}

//9.按元素值查找其逻辑位序
int LocateElem(LNode* L, ElemType e)
{
	int i = 1;     //直接从首结点开始查找(头结点->首结点->尾结点)
	LNode* p = L->next;
	while (p != NULL && p->data != e)
	{
		p = p->next;
		i++;
	}
	if (p == NULL)
		return false;
	else
		return(i);
}

//10.链表插入
bool ListInsert(LNode*& L, int i, ElemType e)
{
	//先查找插入位置是否存在(i-1是否存在)
	int j = 0;
	LNode* p = L, * s;
	while (j < i - 1 && p != NULL)
	{
		p = p->next;
		j++;
	}
	if (p == NULL)       //若位置不存在,返回false
		return false;
	else
		//若插入位置存在,则进行插入操作
	{
		//首先为s结点开辟空间
		s = (LNode*)malloc(sizeof(LNode));

		s->data = e;
		s->next = p->next;
		p->next = s;
		return true;
	}
}

//11.链表删除
bool DeleteList(LNode*& L, int i, ElemType& e)
{
	//先确定i-1位置是否存在结点以及是否存其在后继节点(i)
	int j = 0;
	LNode* p = L, * q;
	while (j < i - 1 && p != NULL)
	{
		j++;
		p = p->next;
	}
	if (p == NULL)
		return false;
	else
	{
		q = p->next;
		if (q == NULL) //判断是否存在i-1的后继结点(即i结点)
			return false;
		e = q->data;
		p->next = q->next;   //删除q结点
		return true;
	}
}

3.最后就是在源文件(main.cpp)中使用定义过的各个函数

#include<stdio.h>
#include<stdlib.h>
#include"sq1.h"

void main()
{
	LNode* L;
	ElemType a[6] = { 1,2,3,4,5,6 }, b[6] = { 6,5,4,3,2,1 };

	InitList(L);
	printf("初始化后链表长度ListElemtype(L)=%d\n", ListEmpty(L));
	CreatListHead(L, b, 6);
	CreatListTail(L, a, 6);
	DisppList(L);
	printf("链表长度为 %d\n", ListLength(L));

	int e;
	GetElem(L, 3, e);
	printf("L中第3个元素为%d\n", e);

	printf("L中第4个元素为%d\n", LocateElem(L, 4));

	ListInsert(L, 6, 888);//6位置插入888
	DisppList(L);

	DeleteList(L, 5, e); //删除第5个结点,第三个参数(e)表示将删除结点的值放入指定的存储单元
	DisppList(L);
	printf("删除的元素为 %d\n", e);

	DestroyList(L);

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值