数据结构(C语言版)之栈及递归

简介: 数据结构(C语言版)之栈及递归

前言

●数据结构作为计算机专业基础课,综合性强,抽象性高,在一定程度上增加了学习难度,本次我们共同从数据结构的基础探讨,由浅入深进行数据结构的学习。
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

●本文只浅显的探讨了栈的基本知识,作者相信随着学习课程的深入,我们将会对数据结构有更深的理解与收获!

正文

————————————————

一、栈

1.栈的概念及术语

image.gif编辑image.gif编辑

1.定义

只能在表的一端(栈顶)进行插入和删除运算的线性表

2.逻辑结构

与线性表相同,仍为一对一关系

3.存储结构

用顺序栈或链栈存储均可,但以顺序栈更常见

4.运算规则

只能在栈顶运算,且访问结点时依照后进先出(LIFO)或先进后出(FILO)的原则

5.实现方式

关键是编写入栈和出栈函数,具体实现依顺序栈或链栈的不同而不同 基本操作有入栈、出栈、读栈顶元素值、建栈、判断栈满、栈空等

6.栈是一种特殊的线性表,它的逻辑结构和存储结构与线性表相同,其特殊性体现在“运算受限” 。

限制在表的一端进行插入和删除操作。能够进行操作的一端是浮动端,称之为栈顶,通常用一个“栈顶指针”指示,它的位置会随着操作而发生变化;与此相对,表的另一端是固定端,称之为栈底。 如同线性表可以为空表一样,当栈中没有元素时称为空栈。往栈中插入元素的操作称为入栈,删除栈中元素的操作称栈 。

image.gif编辑

2.栈的特点

image.gif编辑image.gif编辑

二、栈的表示及基本操作

1.顺序栈

顺序栈的类型定义

#define MAXSIZE 100
typedef struct
{ 
  DataType data[MAXSIZE];
  int top;
}SqStack;
image.gif

image.gif编辑

说明

在顺序栈中,用于指示栈顶的当前位置的top是整型,它的实质是栈顶元素在数组中的下标。 栈顶指针top直接反映出栈的当前状态:空栈时,栈顶指针top为-1;

栈满时,栈顶指针top为MAXSIZE-1;

入栈时,栈顶指针top加1;

出栈时,栈顶指针top减1。

栈底位置固定不变,可以设置在数组的任意端,一般习惯上将数组低下标端作为栈底。

顺序栈的算法实现

(1)初始化栈(置栈空) 
初始化栈主要是分配存储空间,并将栈顶指针置为-1。 
int  InitStack(SqStack &S)
{   //构造一个空栈
  S.top= -1;
  return OK;
}
(2)判栈空
int  StackEmpty(SqStack S) 
//判栈为空栈时返回值为真,反之为假
{ return(S.top==-1? TRUE:FALSE);} 
(3)判栈满
int StackFull(SqStack S)
//判栈为满栈时返回值为真,反之为假
{   return(S.top==MAXSIZE-1?TRUE:FALSE);}
(4)进栈
进栈时应首先判栈满,若栈不满则将栈顶指针top上移,存入元素。
int Push(SqStack &S, DataType e)
{ //将元素e插入到栈中,作为的新栈顶
  if(StackFull(S))  return ERROR; //栈满
  S.top++;    // top加1,栈顶位置上移
  S.data[S.top]=e;   //数据e存入当前栈顶
  return  OK;
}
(5)出栈
出栈时应首先判断栈是否为空,若栈不为空,则取出栈顶元素,将栈顶指针top下移,再返回栈顶元素。
int Pop(SqStack &S,DataType &e)
{//若栈不为空,则删除栈顶元素
  if(StackEmpty(S)) return ERROR; //栈空
  e=S.data[S.top];   //取出数据放入e所指单元中
  S.top--;     // top减1,栈顶位置下移
  return OK;
}
(6)取栈顶元素1
int GetTop(SqStack S,DataType &e)
{//若栈不为空,则取栈顶元素
  if(StackEmpty(S)) return ERROR;  //栈空
  e=S.data[S.top];    //取出数据,top不变
  return OK;
}
(6)取栈顶元素2
DataType GetTop(SqStack S)
{//若栈不为空,则取栈顶元素
    DataType e;
  if(StackEmpty(S)) return ERROR;  //栈空
  e=S.data[S.top];    //取出数据,top不变
  return e;
}
image.gif

2.链栈

链栈的类型定义

链栈:通常用一个无头结点的单链表表示,其结点结构与单链表的结点结构相同。

typedef struct node
{ 
       DataType data;
       struct node* next;
} StackNode,*LinkStack;
LinkStack top;   //top为栈顶指针
image.gif

说明

对于单链表来说,在表头插入和删除结点要比在表尾相对简单,因此将单链表表头作为栈顶,则单链表的头指针即为栈顶指针。

链栈的算法实现

链栈的本质是简化的单链表,top作为栈顶指针始终指向链表首结点。进栈操作就是在链表表头插入一个新的结点,出栈操作就是删除当前的表头结点并释放空间。

(1)初始化栈(置空栈)
LinkStack  InitStack()
//空栈的top指针为NULL
{  return NULL;   }
(2)判栈空
int  StackEmpty(LinkStack top)      
//判栈为空栈时返回值为真,反之为假
{   return(top==NULL? TRUE:FALSE);  }
(3)进栈
void Push(LinkStack top, DataType e)
{   //将元素e进链栈,即在表头插入新的结点
  StackNode  *s;
  s=(StackNode*)malloc(sizeof(StackNode));
  s->data=e;
  s->next=top;
  top=s;
}
(4)出栈
int Pop(LinkStack top,DataType &e)
{   //若栈不为空将栈顶元素出栈,即为删除表头结点
  StackNode *p;
  if(StackEmpty(top)) return  ERROR;  //栈空
  e=top->data;
  p=top;
  top=top->next;
  free(p);
  retrun OK;
}
image.gif

三、栈与递归

递归的定义  

若一个对象部分地包含它自己,  或用它自己给自己定义,  则称这个对象是递归的;

若一个过程直接地或间接地调用自己, 则称这个过程是递归的过程。

long Fact ( long n ) {
    if ( n == 0) return 1;
    else return n * Fact (n-1); }
image.gif

以下三种情况常常用到递归方法

递归定义的数学函数

具有递归特性的数据结构

可递归求解的问题

image.gif编辑

image.gif编辑

image.gif编辑

image.gif编辑

image.gif编辑

image.gif编辑

image.gif编辑

image.gif编辑

image.gif编辑

image.gif编辑

总结:

本文共同探讨了栈的相关内容,在日常生活中有极其丰富的应用,作者认为要认真对待数据结构的学习,搭建基本知识框架,随着日积月累的学习逐渐填补总结,从脚踏实地做起,祝愿大家能够熟悉掌握这门课程,并将其能够熟悉的应用!

耐心看到这里的小伙伴一定很棒!加油!路在脚下,梦在前方!

●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

相关文章
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
540 1
|
6月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
127 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
11月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
526 77
|
10月前
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
241 11
|
10月前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
11月前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
433 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
10月前
|
定位技术 C语言
c语言及数据结构实现简单贪吃蛇小游戏
c语言及数据结构实现简单贪吃蛇小游戏
|
11月前
|
搜索推荐 C语言
数据结构(C语言)之对归并排序的介绍与理解
归并排序是一种基于分治策略的排序算法,通过递归将数组不断分割为子数组,直到每个子数组仅剩一个元素,再逐步合并这些有序的子数组以得到最终的有序数组。递归版本中,每次分割区间为[left, mid]和[mid+1, right],确保每两个区间内数据有序后进行合并。非递归版本则通过逐步增加gap值(初始为1),先对单个元素排序,再逐步扩大到更大的区间进行合并,直至整个数组有序。归并排序的时间复杂度为O(n*logn),空间复杂度为O(n),且具有稳定性,适用于普通排序及大文件排序场景。
|
11月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
582 16