数据结构 :: 顺序栈与链式栈的设计与实现

数据结构 :: 双链表的设计与实现

说明:本文属于读书笔记。笔者将以讲述的方式表达全片文章。故文中提到的某些字词是非正式术语,只是笔者本人的理解性词语。

前言:本文将对顺序栈与链式栈进行设计与实现!其中,基于 动态数组 实现顺序栈;基于 无头链表 实现链式栈。值得注意的是,此前《无头链表的设计与实现》文章中笔者简单的分享了相关内容,在本篇文章中,笔者将在对此前的实现方式进行改进。同时加深对链表使用的解释,特别提示:此处将为后续实战小项目《某某管理系统》的实现进行铺垫!!!
文章阅读指引:实现栈推荐使用顺序结构,即仔细理解基于动态数组的设计与实现;深入理解链表与指针及后续项目实现基础,请阅读基于 无头链表实现链式栈。


目录


1. 栈的简介
1.1 栈的定义

1. 栈是限定仅在表尾进行插入和删除操作的线性表。
2. 栈顶(top):表尾!!!
3. 栈底(bottom):表头!!!
4. 别称:后进先出(先进后出)的线性表。【LIFO结构:last in first out】
5. 插入:进栈、压栈、入栈。
6. 删除:出栈、弹栈。

在这里插入图片描述

1.2 应用场景

1. 撤销操作:如 word 等软件的撤销原理。
2. 网页页面返回


2. 基于动态数组的设计与实现
2.1 动态数组原理简介

动态数组的基本原理:每次新增,或删除数据,均会使用内存的动态分配!

新增数据:1. 新建一个更大的数组;2. 将原数组的数据拷贝到新数组;3. 释放原数组空间;4. 新数据加入到新数组。(删除数据:反之!)

在这里插入图片描述


2.2 顺序栈的设计与实现

根据栈的特性,本文简单设计与实现。

功能:1. 初始化栈结构;2. 判断栈是否为空;3. 入栈;4. 出栈。(5. 遍历栈【该方法一般是不提供的,此处只是作为命令行输出看效果的可视化作用】)

说明:注意事项等及设计原理均在代码的注释中!!!

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
// 全局变量
int* Stack;		// 栈指针:栈地址的存储
int size;		// 栈大小

// 初始化栈
void initStack();
// 判断是否为空
bool isEmpty();
// 入栈
void push(int);
// 出栈
void pop();

int main(){
	// 顺序结构实现方式很简单,自行 CV 代码测试!
	return 0;
}

// 初始化栈
void initStack(){
	/*
  		初始化就是对全局定义的 Stack,size 赋值;
	*/
	Stack = NULL; 	// 初始化指向空
	size = 0;		// 初始化栈大小为 0
}
// 判断是否为空
bool isEmpty(){
	return (0 == size);
}
// 入栈
void push(int data){
	// 1. 申请内存空间(此处仅设置每次增增加一个单位)
	int* pnew = (int*)malloc(sizeof(int)*(size+1));
	// 2. 判断栈是否为空
	/* 如果栈为空!须对原数组进行数据拷贝到新数组	*/
	if(!isEmpty()){
		memcpy(pnew,Stack,sizeof(int)*size);
		free(Stack);
	}
	// 3. 指针改变指向对象
	Stack = pnew;
	// 4. 新增数据
	Stack[size++] = data;
}
// 出栈
/* 出栈:抛出被删除值!!! */
void pop(){
	// 1. 判断栈是否为空
	if(isEmpty()){
		printf("栈为空!操作失败!\n");
		return;
	}
	// 栈不为空
	// 2. 打印被查出值
	printf("被删除的值为:%d", Stack[size-1]);
	// 3. 申请内存空间
	int* pnew = (int*)malloc(sizeof(int)*(--size));
	// 4. 数据拷贝
	memcpy(pnew,Stack,sizeof(int)*size);
	// 5. 指针改变指向对象
	Stack = pnew;
}

3. 基于无头链表的设计与实现

说明:

  1. 该部分内容将会是实战小项目《某某管理系统》的实现基础铺垫,如有需求请仔细阅读。
  2. 指针与链表在链表中的操作将加深叙述。(弥补《无头链表的设计与实现》)的叙述空白!(提示:实现基础思路仍在文章中,本文仅对指针与链表在链表中的操作将加深叙述。)

3.1 初始化链表

同线性表,在本次实现过程中,设计初始化链表函数。(实现代码如下,注意事项见注释!

// 初始化链表的指针!!!
// 无需参数传递

struct Node* initStack(){
	struct Node* plist = NULL;	// 初始化指向空
	return plist;
}

3.2 结点设置与数据“单元”

此处会结合后续项目需求进行实例设计。(实现代码如下,注意事项见注释!

// 数据“单元”:使用结构体作为单元架构
// 其中 typedef 用于取别名 
typedef struct Student{
	char name[20];		// 姓名
	int age;			// 年龄
	int score;			// 成绩
}Stu;

// 链表结点
struct Node{
	Stu data;
	struct Node* next;
};

3.3 创建结点

此处涉及指针操作!(实现代码如下,注意事项见注释!

// 1. 函数返回值类型:由于含返回的是新建结点,需返回地址,故使用数据类型 struct Node*
// 2. 参数列表类型:由于我们的数据“单元”是结构体,并且使用结构体数组,故对数据的访问与修改须使用指针进行传递地址。
struct Node* createNode(Stu* data){
	// 1. 申请内存空间
	struct Node* pnew = (struct Node*)malloc(sizeof(struct Node));
	// 2. 数据载入
	// 注意区分指针数据域是结构体对象,故通过 pnew 访问成员是 . 访问形式
	// 而对于 data 而言,具有指针指向 故使用 -> 访问形式
	// 关系如下图
	strcpy(pnew->data.name,data->name);
	pnew->data.sex = data->sex;
	pnew->data.age = data->age;
	pnew->next = NULL;
	return pnew;
}

在这里插入图片描述



3.4 是否栈判断与遍历
  1. 空栈的判断指标即,初始化后的链表指针是否为空!
  2. 根据栈的特性一般提供该方式,实现仅是为了可视化测试功能正确性!
    实现代码如下,注意事项见注释!
// 判断栈是否为空
// 返回值类型直接使用 bool 型
// 参数列表:需要判断对象,传入对象地址进行判断
bool isEmpty(struct Node* pHead){
	return (NULL == pHead);
}

// 遍历栈元素
// 参数列表:需要遍历对象,传入对象地址进行操作
void _travel(struct Node* pHead){
	// 1. 空栈判断
	if(isEmpty(pHead)){
		printf("栈为空!\n");
		return;
	}
	// 2. 设置游标
	struct Node* ptemp = pHead;
	while(ptemp){
	// 注意函数参数是结点数据域地址,即data的地址 != ptemp 的地址
		printStu(&(ptemp->data));
		ptemp = ptemp->next;
	}
	printf("\n遍历完成!\n");
}

// 打印数据函数
// 单节点数据打印,故参数需求,单个结点地址
void printStu(struct Node* p){
	printf("\n学生姓名:%s,学生年龄:%d,学生成绩:%d\n",
	p->name,p->sex,p->age);
}

3.5 入栈

入栈是实质就是使用尾插法,因为栈是限定在尾部插入的线性表。(实现代码如下,注意事项见注释!

// 参数列表:操作对象(链表地址!不等于结点地址!)、数据对象(结构体)
// 指针解引用就是访问指针所向的数据值
void push(struct Node** pHead,Stu* data){
	// 1. 创建结点
	struct Node* pnew = createNode(data);
	// 2. 判断栈是否为空
	if(isEmpty(*pHead)){
		// 注意我们传入的是链表的指针,是一个二级指针,访问其值(一级指针:地址数据)须使用解引用
		// 如果为空,直接挂载
		*pHead = pnew;
	}
	else{	// 3. (不为空)寻找尾结点
		// 设置游标指针
		struct Node* ptemp = *pHead;
		while(ptemp->next){
			ptemp = ptemp->next;
		}
		// 挂载结点
		ptemp->next = pnew;
	}
	
}

3.6 出栈

出栈功能抛出删除结果,释放空间!(实现代码如下,注意事项见注释!

void pop(struct Node* pHead){
	// 1. 判断链表是否为空
	if(isEmpty(*pHead)){
		printf("链表为空!");
		return;
	}
	// 2. 设置游标,寻找尾结点
	struct Node* pTail = *pHead;
	while(pTail->next){
		pTail = pTail->next;
	}
	printf("\n删除对象数据为:");
	printStu(&(pTail->data));
	// 3. 如果只有一个结点:直接置空并释放
	if(pTail == *pHead){
		*pHead = NULL;
		return;
	}
	free(pTail);
	// 4. 如果不止一个,设置游标并找到尾结点的前一个结点
	struct Node* ptemp = *pHead;
	if (ptemp->next != pTail)
	{
		ptemp = ptemp->next;
	}
	ptemp->next = NULL;
	free(pTail);
}

3.7 测试代码及结果

编译器:vs2013

int main(){
	Stu info[] = {
			{ "刘备", 32, 32 },
			{ "张飞", 12, 12 },
			{ "关羽", 22, 22 },
	};
	struct Node* plist = initStack();
	_travel(plist);
	for (int i = 0; i < 3; i++)
		push(&plist, &(info[i]));
	_travel(plist);
	pop(&plist);
	_travel(plist);
	while (1);
	return 0;
}

在这里插入图片描述

4 结语

实操系列与功能补充指引!(待更新)

  1. 计数功能的实现。相关文章地址 => 双链表的设计与实现
  2. 学生成绩管理系统(待更新)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NPC的白话文谈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值