栈与循环队列、斐波那契

本文详细介绍了栈和队列的基础知识,包括栈的顺序存储结构,如入栈、出栈、空栈和栈满的条件,以及队列的循环队列实现,包括元素入队、出队及队列是否为空的判断。此外,还涉及了链表的头插法和尾插法创建链表,以及斐波那契数列的递归计算方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


我们为什么需要在形参的地方使用引用?

在子函数中去给对应的形参赋值后,子函数结束,主函数中对应的实参就发生了变化。
如果没有使用引用,那么在子函数中给形参赋值后,子函数结束,主函数中对应的实参不会变化的。

一、栈

栈:只允许一端进行插入或删除操作的线性表。
先进后出,first in last out

 1.1 栈顶(top)

在这里插入图片描述

 1.2 栈的基本操作

在这里插入图片描述

 1.3 顺序存储栈

  1.3.1入栈

typedef struct{
	Elemtype data[50];
	int top;
	}SqStack;
SqStack S;

让4元素入栈
在这里插入图片描述
解释一下:一开始,有3个值,下标为0 1 2 ,S.top的值是2,即为栈顶。让4入栈,就把栈顶给4,即S.data[3]=4。也可以写成S.data[++S.top]=4;

  1.3.2出栈

元素出栈
在这里插入图片描述
解释以下:S.top=3 ,x=S.data[S.top]; S.top–;
这个时候,3为数组的下标,并不是存储的值,x拿到的具体值是4。然后S.top–才会变成下标2。
我们只需要标识栈顶是哪个就行,不必把删除那个元素的位置置空或者改为0.

  1.3.3空栈

在这里插入图片描述
解释一下:因为若此栈只有一个元素的话,即S.top=0;再执行出栈操作后,S.top=-1;此时栈顶没有指向元素,而是指向了一个空值,则表明此栈为空。所以,栈为空的标志是,S.top=-1.

  1.3.4 栈满

在这里插入图片描述
栈满,不可以再加入元素,否则数组会访问越界。

 1.4 实战代码

// 导入包
#include <stdio.h>
#include <stdlib.h>

// 声明栈的空间
#define MaxSize 50
// 定义了一个类型别名 ElemType,将 int 类型重新命名为 ElemType,方便后续使用
// typedef用于定义类型别名,可以将一个已有的数据类型起一个新的名字。
typedef int ElemType;
// 定义栈
typedef struct
{
    ElemType data[MaxSize]; // 数组
    int top;                // 声明栈顶元素

} SqStack;
// 定义初始化函数
void InitStack(SqStack &S) // 括号内需要加引用
{
    S.top = -1; // 此栈为空栈
}
// 定义判断栈是否为空的函数
bool StackEmpty(SqStack S) // 括号内不需要加引用,因为只是读取它
{
    if (S.top == -1)

        return true;
    else
        return false;
}
// 定义入栈函数
bool Push(SqStack &S, ElemType x) // 把x放进去
{
    if (S.top == MaxSize - 1)
    {
        return false; // 栈满了
    }
    S.data[++S.top] = x;
    return true; // 返回true就是入栈成功
}
// 定义获取栈顶元素
bool GetTop(SqStack S, ElemType &x) // S是没有改变的,无需加引用,而x是通过main函数里面传入进去的,所以需要加引用
{                                   // 判断栈是否为空
    if (StackEmpty(S))
    {
        return false;
    }
    x = S.data[S.top];
    return true;
}
// 定义弹出栈顶元素
bool Pop(SqStack &S, ElemType &x)
{ // 判断栈是否为空
    if (StackEmpty)
    {
        return false;
    }
    x = S.data[S.top--]; // 等价于x=S.data[S.top];再做S.top--;
    return true;
}
int main()
{
    SqStack S;            // 声明一个实例,先进后出
    bool flag;            // 设置一个布尔类型的标志位
    ElemType m;           // 存储拿出来的栈顶元素的
    InitStack(S);         // 初始化
    flag = StackEmpty(S); // 判断栈是否为空
    if (flag)
    {
        printf("栈为空的\n");
    }
    Push(S, 3);          // 入栈元素3
    Push(S, 4);          // 入栈元素4
    Push(S, 5);          // 入栈元素5
    flag = GetTop(S, m); // 获取栈顶元素,但S.top不变
    if (flag)
    {
        printf("栈顶元素是%d\n", m);
    }
    flag = Pop(S, m); // 弹出栈顶元素
    if (flag)
    {
        printf("栈顶元素是%d\n", m);
    }

    return 0;
}

二、队列

 2.1 队列解释

 队列(Queue)简称队,也是一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。向队列中插入元素称为入队或进队;删除元素称为出队或离队。FIFO,先进先出。

队头:允许删除的一端,又称队首,开始删除元素,即出队列。
队尾:允许插入的一端,开始进入元素,即进队列。

在这里插入图片描述

 2.2 元素入队

bool EnQueue(SqQueue &Q,ElemType x)
{
	if((Q.rear+1)%MaxSize==Q.front)//判断循环队列是否队满
		return false;
	Q.data[Q.rear]=x;//放入元素
	Q.rear=(Q.rear+1)%MaxSize;//改变队尾标记
	return true;
}

为什么要取余%MaxSize?
解释一下:结合下图,因为如果Q.rear指向5,在5这个位置放入一个元素f,Q.rear应当加1,则会指向下标为0的位置。但是,如果不%MaxSize,下标就会变成6,但是在下图中没有6的下标,所以在循环队列中,一般都要进行%MaxSize操作。
在这里插入图片描述

 2.3 元素出队

bool DeQueue(SqQueue &Q,ElemType &x)
{
	if(Q.rear==Q.front)
		return false;
	x=Q.data[Q.front];//先进先出
	Q.front=(Q.front+1)%MaxSize;
	return true;	
}

出队取余%MaxSize跟上面进队一样的解释。
在这里插入图片描述

 2.4 查看队列是否为空

Q.rear==Q.front;

 2.5 循环队列代码的实现(链表也可以实现,但是考试中一般不用,就不再写)

//导入包
#include <stdio.h>
#include <stdlib.h>
//声明大小
#define MaxSize 5
//声明类型
typedef int ElemType;
//定义结构体
typedef struct{
    ElemType data[MaxSize];//数组,存储MaxSize-1个元素,因为队尾rear也占一个位置
    int front,rear;//声明队头,队尾
}SqQueue;
//定义初始化函数,让队列为空
void InitQueue(SqQueue &Q)
{
    Q.front=Q.rear=0;
}
//定义判断队列是否为空的函数
bool isEmpty(SqQueue Q)
{
    if(Q.front==Q.rear)//判断队列是否为空
    {    return true;}
    return false;
}
//定义一个入队的函数
bool EnQueue(SqQueue &Q,ElemType x)
{
    if((Q.rear+1)%MaxSize==Q.front)
    {    return false;}//队列满了
    Q.data[Q.rear]=x;
    Q.rear=(Q.rear+1)%MaxSize;//向后移动一格
    return true;
}
//定义出队函数
bool DeQueue(SqQueue &Q,ElemType &x)
{
    //判断队列是否为空
    if(Q.front==Q.rear)//队列为空
    {   return false;}
    x=Q.data[Q.front];
    Q.front=(Q.front+1)%MaxSize;
    return true; 
}
int main()
{
    //首先,先定义一个循环队列
    SqQueue Q;
    bool ret;//存储返回值
    ElemType element;//存储队列元素
    //然后,初始化循环队列
    InitQueue(Q);
    ret = isEmpty(Q);
    if(ret)
    {
        printf("队列为空\n");
    }
    else{
        printf("队列不为空\n");
    }
    //入队操作
    EnQueue(Q,3);
    EnQueue(Q,4);
    EnQueue(Q,5);
    //为什么要在下面进行一个判断? 因为一开始我们定义的是可以存储4个值,看看超过存储的范围是否仍可以存储
    ret=EnQueue(Q,6);
    ret=EnQueue(Q,7);
    if(ret)
    {
        printf("入队成功\n");
    }
    else{
        printf("入队失败\n");
    }
    //出队
    ret=DeQueue(Q,element);
    if(ret)
    {
        printf("出队成功,元素值为%d\n",element);
    }
    else{
        printf("出队失败\n");
    }
    ret=DeQueue(Q,element);
    if(ret)
    {
        printf("出队成功,元素值为%d\n",element);
    }
    else{
        printf("出队失败\n");
    }
    return 0;
}

 2.6 队列的链式存储

队列的链式表示称为链队列,它实际上是一个同时带有队头指针和队尾指针单链表。头指针指向队头结点,尾指针指向队尾结点,即单链表的最后一个结点。

存储结构

typedef int ElemType;
//定义链表节点的结构体
typedef struct LinkNode
{
	ElemType data;
	struct LinkNode *next;
}LinkNode;
//
typedef struct
{
	LinkNode *front,*rear;//链表头 链表尾
}LinkQueue;//先进先出
LinkQueue Q;

初始化队列

void InitQueue(LinkQueue &Q)
{
    Q.front=Q.rear=(LinkNode*)malloc(sizeof(LinkNode));
    Q.front->next=NULL;
}

在这里插入图片描述
入队,尾插法

void EnQueue(LinkList &Q,ElemType x)
{
    LinkNode *s=(LinkNode*)malloc(sizeof(LinkNode));
    s->data=x;
    s->next=NULL; 
    Q.rear->next=s;//rear始终指向尾部
    Q.rear=s;
}

出队,头部删除法

bool DeQueue(LinkList &Q,ElemType &x)
{
    if(Q.rear=Q.front)
        return false;//队列为空
    LinkNode *p=Q.front->next;//头结点什么都没存,所以头结点的下一个节点才有数据
    x=p->data;
    Q.front->next=p->next;//断链
    if(Q.rear==p)//删除的是最后一个元素
        Q.rear=Q.front;//队列置为空
    free(p);
    return true;
}

三、实战作业代码

 3.1 作业1代码

链表的介绍,请点击我
要求:输入34567 9999一串整数,9999代表结束,通过头插法新建链表,并输出,通过尾插法新建链表并输出。
部分代码如下:
//打印印链表中每个结点的值
void PrintList(LinkList L)
{
L=L->next;
while(L!=NULL)
{
printf(“%d”,L->data);//打印当前结点数据
L=L->next;//指向下一个结点
if(L!=NULL)
{
printf(“”);
}
}
printf(“\n”);
}

//导入包
#include <stdio.h>
#include <stdlib.h>
//为int起个别名
typedef int ElemType;
//定义单链表结构
typedef struct LNode{
    ElemType data;
    struct LNode* next;//指向下一个节点
}LNode,*LinkList;
//LNode 是一个结构体类型,包含两个成员:data 和 next。
//data 存储节点的数据,next 是一个指向下一个节点的指针。
//LinkList 是一个指向 LNode 结构体的指针类型,用于表示单链表的头指针。
//这样,我们就可以通过头指针 LinkList,遍历整个单链表,找到其中的每一个节点,并操作它们的数据和 next 指针

//头插法新建链表
LinkList CreatList1(LinkList &L)
{
    LNode *s;int x;
    L=(LinkList)malloc(sizeof(LNode));//带头结点的链表
    L->next=NULL;//L->data里面没有放东西
    scanf("%d", &x);
    while(x != 9999){
        LNode* s = (LNode*)malloc(sizeof(LNode));
        s->data = x;
        s->next = L->next; //将新节点插入到头结点之后
        L->next = s;
        scanf("%d", &x);
    }
    return L;
}
//尾插法新建链表
LinkList CreatList2(LinkList &L)
{
    L=(LinkList)malloc(sizeof(LNode));//带头结点的链表
    L->next = NULL; //头结点指针域置为空
    LNode* tail = L; //tail指向尾节点
    int x;
    scanf("%d", &x);
    while(x != 9999){
        LNode* s = (LNode*)malloc(sizeof(LNode));
        s->data = x;
        tail->next = s; //将新节点插入到尾节点之后
        tail = s; //更新尾节点为新节点
        scanf("%d", &x);
    }
    tail->next = NULL; //尾节点指针域置为空
    return L;
}

//遍历链表中每个结点的值,并输出
void PrintList(LinkList L)
{
L=L->next;//让L指向第一个有数据的节点
while(L!=NULL)
{
printf("%d",L->data);//打印当前结点数据
L=L->next;//指向下一个结点
if(L!=NULL)
{
    printf(" ");
}
}
printf("\n");
}
int main()
{
    LinkList L=NULL;//链表头,是结构体指针类型
    LinkList search;//用来存储拿到的某一个节点
    CreatList1(L);//输入数据可以为3 4 5 6 7 9999,头插法新建链表
    PrintList(L);//链表打印
    CreatList2(L);//输入数据可以为3 4 5 6 7 9999
    PrintList(L);
    return 0;
}

 3.2 作业1代码

要求:新建一个栈,读取标准输入3个整数3 4 5,入栈3 4 5,依次出栈,打印5 4 3,新建循环队列(Maxsize为5),读取标准输入3 4 5 6 7,入队7时,队满,打印false,然后依次出队,输出3 4 5 6

Input
读取标准输入,内容依次是3 4 5,换行后,接着是3 4 5 6 7

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

#define Maxsize 5

// 栈结构体
typedef struct {
int data[Maxsize];
int top;
} Stack;

// 初始化栈
void initStack(Stack *s) {
s->top = -1;
}

// 判断栈是否为空
int isStackEmpty(Stack *s) {
return s->top == -1;
}

// 判断栈是否已满
int isStackFull(Stack *s) {
return s->top == Maxsize - 1;
}

// 入栈
void pushStack(Stack *s, int x) {
if (isStackFull(s)) {
printf("Stack is full.\n");
return;
}
s->data[++s->top] = x;
}

// 出栈
int popStack(Stack *s) {
if (isStackEmpty(s)) {
printf("Stack is empty.\n");
return -1;
}
return s->data[s->top--];
}

// 循环队列结构体
typedef struct {
int data[Maxsize];
int front, rear;
} Queue;

// 初始化队列
void initQueue(Queue *q) {
q->front = q->rear = 0;
}

// 判断队列是否为空
int isQueueEmpty(Queue *q) {
return q->front == q->rear;
}

// 判断队列是否已满
int isQueueFull(Queue *q) {
return (q->rear + 1) % Maxsize == q->front;
}

// 入队
void enqueue(Queue *q, int x) {
if (isQueueFull(q)) {
printf("false\n");
return;
}
q->data[q->rear] = x;
q->rear = (q->rear + 1) % Maxsize;
}

// 出队
int dequeue(Queue *q) {
if (isQueueEmpty(q)) {
printf("Queue is empty.\n");
return -1;
}
int x = q->data[q->front];
q->front = (q->front + 1) % Maxsize;
return x;
}
int main() {
// 栈操作
Stack s;
initStack(&s);
pushStack(&s, 3);
pushStack(&s, 4);
pushStack(&s, 5);
printf("%d %d %d\n", popStack(&s), popStack(&s), popStack(&s));

// 队列操作
Queue q;
initQueue(&q);
enqueue(&q, 3);
enqueue(&q, 4);
enqueue(&q, 5);
enqueue(&q, 6);
enqueue(&q, 7);
dequeue(&q);
printf("%d %d %d %d\n", dequeue(&q), dequeue(&q), dequeue(&q), dequeue(&q));

return 0;
}

四、斐波那契

0 1 1 2 3 5
f(n)=f(n-1)+f(n-2)

递归 函数自身调用

int Fib(int n)
{
	if(n==0)
		return 0;
	else if(n==1)
		return 1;
	else
		return Fib(n-1)+Fib(n-2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值