文章目录
我们为什么需要在形参的地方使用引用?
在子函数中去给对应的形参赋值后,子函数结束,主函数中对应的实参就发生了变化。
如果没有使用引用,那么在子函数中给形参赋值后,子函数结束,主函数中对应的实参不会变化的。
一、栈
栈:只允许一端进行插入或删除操作的线性表。
先进后出,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);
}