实参形参与指针4——单链表的基本操作实现(C语言)

前言

基本上搞定了顺序表和单链表就能对指针了然于心了,下面是我自己调通的单链表基本操作和执行代码,仅用作记录。

单链表基本操作代码

  • 代码包括以下单链表基本操作:
    • 初始化头指针
    • 单链表判空
    • 尾插法建立单链表
    • 头插法建立单链表
    • 插入新结点
    • 删除已有结点
    • 顺序打印单链表所有结点数据域

下面附完整代码,使用Visual Studio2019,我这边确保调通了(如果没通,可能与安全函数有关,请尝试去vs设置里打开安全检查)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#define ERROR 0
#define OK 1

typedef int Elemtype;
typedef int Status;

/*
使用typedef定义一个结构体变量,并将其命名为LNode;
为了方便,可以同时命名一个结构体指针变量*Linklist,等价与 struct Node* Linklist;
在后续函数传参中,Linklist结构体指针变量专用于指代头指针,而LNode结构体变量专用于指代链表元素;
*/

/*补充1:结构体自引用与链表
1. 链表在物理结构上是离散的,所以为了在逻辑上连续,需要在定义时利用结构体自引用特性构造一个指针域指向后继结点;
2. 引用自身时需要利用指针的特性建立,因为指针的占用大小在系统中是有定义的,如代码段中的定义就是最正确的方式之一;
3. 切记不可直接使用非指针变量自引用,这是因为在定义是,结构体本身还未初始化,未初始化变量引用未初始化变量会导致内存溢出,所以是非法的;

//	一种典型的错误定义方式如下
//	typedef struct Node{
//		Elemtype data;
//		struct Node next;
//	}LNode,*Linklist;
*/
typedef struct Node {
	Elemtype data;
	struct Node* next;
}LNode, * Linklist;

/*
定义一个函数,用于初始化头指针;
由于传入指针变量本身也需要被修改,所以要进行函数的地址传递,即传递一个Linklist结构体指针变量的地址(否则会出现“L=nullptr”的报错);
/
/这里做一个详细说明;
/已知有一个结构体指针L,其中L->data表述L指向结构体的数据域,L->next表述L指向结构体的指针域;
/
*/
Status initLHead(Linklist& L) {
	L = (Linklist)malloc(sizeof(LNode));
	L->next = NULL;
	return OK;
}
/*
判空操作不需要对链表进行修改,所以进行函数的值传递;
*/
Status isEmpty(Linklist L) {
	if (L->next == NULL) {
		printf_s("链表为空");
		return ERROR;
	}
	return OK;
}
/*
由用户输入数值对单链表进行初始化,使用尾插法
*/
Status tailInitList(Linklist& L, int num) {
	Linklist p;//功能指针,从头指针开始顺序移动,指向每个结点
	Linklist s;//用于动态创建新的结点空间
	int tempData;//用于每次给新结点赋值
	p = L;
	if (p->next != NULL) {
		printf_s("当前链表不需要初始化,将继续执行下一步或选择执行其它操作。\n");
		return ERROR;
	}
	else if (num <= 0) {
		printf_s("插入元素个数为非法值,尾插建表失败\n");
		return ERROR;
	}
	while (num != 0) {
		s = (Linklist)malloc(sizeof(LNode));
		scanf_s("%d", &tempData);
		s->data = tempData;
		if (p != NULL) {//!!!!注意,指针一定要结合非NULL判断进行,否则会产生C6011报错或者告警
			p->next = s;
			p = s;
			p->next = NULL;
			num--;
		}
	}
	return OK;
}
/*
由用户输入数值对单链表进行初始化,使用头插法
*/
Status headInitList(Linklist& L, int num) {
	Linklist p = L;//功能指针
	Linklist s = NULL;//新建结点指针
	int tempData = 0;
	if (p == NULL) {
		printf_s("头指针非法或为nullptr,请重新初始化头指针。\n");
		return ERROR;
	}
	else if (p->next != NULL) {
		printf_s("该链表不需要初始化。\n");
		return ERROR;
	}
	else if (num <= 0) {
		printf_s("输入的插入元素个数为非法值,尾插建表失败。\n");
	}
	while (num != 0) {
		s = (Linklist)malloc(sizeof(LNode));
		scanf_s("%d", &tempData);
		s->data = tempData;
		if (p != NULL) {
			s->next = p->next;
			p->next = s;
			num--;
		}
	}
	return OK;
}
/*
打印单链表所有元素
*/
Status printCheck(Linklist L) {
	Linklist p = L;
	if (p->next == NULL) {
		printf_s("没有元素可以打印。\n");
		return ERROR;
	}
	printf_s("当前链表为:");
	while (p->next != NULL) {//注意此处,由于初始时p=L,所以要检查p->next是否为NULL,否则将溢出
		p = p->next;//由于最开始p=L,所以要先将p指向下一个有意义结点,也就是p->next
		printf_s("%d ", p->data);//while循环里这两句不要反过来
	}
	printf_s("\n");
	return OK;
}
/*
插入函数,在pos位置插入一个数据域为e的结点的功能函数
*/
Status insertList(Linklist& L, Elemtype e, Elemtype pos) {
	int i = 1;
	Linklist p = L;
	Linklist s = NULL;
	if (p == NULL || pos <= 0) {
		printf_s("函数传值有非法数值,即将结束程序。\n");
		return ERROR;
	}
	while (p != NULL && i < pos) {
		p = p->next;
		i++;
	}
	if (p == NULL) {
		return ERROR;
	}
	s = (Linklist)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return OK;
}
/*
删结点函数,删除第pos个结点,用全局变量elem获取被删除结点的数值
*/
Status deleteList(Linklist& L, int pos, int& elem) {
	Linklist s;//功能指针1
	Linklist p = L;//功能指针2
	int i = 1;//计数器
	if (p == NULL) {
		printf_s("单链表错误,即将结束程序。\n");
		return ERROR;
	}
	while (p != NULL && i < pos) {
		p = p->next;
		i++;
	}
	if (p->next == NULL) {
		printf_s("结点位置过大,即将结束程序。\n");
		return ERROR;
	}
	s = p->next;
	elem = s->data;
	p->next = s->next;
	free(s);
	printf_s("删除成功,即将结束程序。\n");
	return OK;
}
int main() {
	Linklist L = NULL;
	Linklist M = NULL;
	Elemtype pos = 0;
	Elemtype dpos = 0;
	Elemtype e = 0;
	Elemtype num = 0;
	Elemtype elem = 0;
	initLHead(L);
	isEmpty(L);
	printf_s("准备初始化链表,请输入你想插入的元素个数(尾插法):");
	scanf_s("%d", &num);
	tailInitList(L, num);
	printCheck(L);
	printf_s("请输入想插入元素的数值:");
	scanf_s("%d", &e);
	printf_s("请输入想插入元素的位置:");
	scanf_s("%d", &pos);
	insertList(L, e, pos);
	printCheck(L);
	printf_s("请输入想删除元素的位置:");
	scanf_s("%d", &dpos);
	deleteList(L, dpos, elem);
	printCheck(L);
	/*
	initLHead(M);
	isEmpty(M);
	printf_s("准备初始化链表,请输入你想插入的元素个数(头插法):");
	scanf_s("%d", &num);
	headInitList(M, num);
	printCheck(M);
	*/
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值