栈与队列
栈
栈:线性存储结构;先进后出;开口端为栈顶,封口段为栈底。
相关操作:进栈(压栈或者入栈),即为往里面存数据;出栈(弹栈);
相关实现方法:1.顺序栈;2.链栈。
1.顺序栈
利用数组存储数据,进栈操作和出栈操作和数组的数据存储和输出方法是一样的,而为了出栈操作所以才定义了一个top变量,top最初指向栈顶元素,此时top=-1,需要进栈时top+1,向数组存入一个数据,需要出栈时top-1,从数组中输出一个数据,当top=-1时,就表示此时为空栈,没有存储任何数据,下面看c代码。
#include<stdio.h>
#include<stdlib.h>
int top = -1;//我这里把top和stack数组定义为了全局变量,比较直观
int stack[100000];
//入栈n个数据,然后依次出栈
void push(int data)
{
top++;
stack[top] = data;
}
void pop()
{
if (top == -1)
{
printf("空栈,无数据元素\n");
}
else
{
printf("出栈元素:%d\n", stack[top]);
top--;
}
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &stack[i]);
push(stack[i]);
}
for (int i = 1; i <= n + 1; i++)
{
pop();
}
return 0;
}
2.链栈
链栈当然就是用链表来表示栈的基本操作了,依据栈是“先进后出”的原则,我们用头插法来建立链表,将链表头部作为栈顶,这样避免大量入栈出栈的循环操作。
出栈操作:
1.判断栈顶是否为空
2.输出数据
3.释放节点
#include<stdio.h>
#include<stdlib.h>
typedef struct stack
{
int data;
struct stack* next;
}stack;
stack* Push(stack* s, int data)
{
stack* p;
p = (stack*)malloc(sizeof(stack));
p->data = data;
p->next = s;
s = p;
return s;
}
stack* Pop(stack* s)
{
if (s)
{
stack* p = s;
s = s->next;
printf("出栈元素:%d\n", p->data);
if (s)
{
printf("此时栈顶元素:%d\n", s->data);
}
else
{
printf("栈已空\n");
}
free(p);
}
else
{
printf("栈内没有元素\n");
return s;
}
return s;
}//if语句是为了判断栈是否为空,防止栈空的时候输出数据
int main()
{
stack* s = NULL;
int n, m;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &m);
s = Push(s, m);
}
for (int i = 0; i <= n; i++)
{
s = Pop(s);
}
return 0;
}
队列
队列:线性存储结构,先进先出原则,一端进,另一端出。进数据端叫队尾,出数据端叫队头。
队列操作:入队与出队,这是我们会定义两个变量top和rear初始时分别指向队头和队尾,进数据时rear+1,出数据时top+1。
实现方法:1.顺序队列;2.链队列
1.顺序队列
还是用数组来操作,下面看原始操作方法
#include<stdio.h>
#include<stdlib.h>
int top, rear;
int q[100000];
void pushQueue(int data)
{
q[rear] = data;
rear++;
}
void popQueue()
{
if (rear == top)
{
printf("队列已空\n");
}
else
{
printf("出队元素:%d\n", q[top]);
top++;
}
}
int main()
{
int n, m;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &m);
pushQueue(m);
}
for (int i = 0; i <= n; i++)
{
popQueue();
}
return 0;
}
通过上面的代码我们发现两个问题:
1.我们为数组申请的存储空间使用过后将无法再进行使用,造成浪费;
2.若我们为数组申请的空间不够大时,会造成数组元素溢出。
再看改良方法(转自c语言中文网),我们把数组形成了一个环形表的结构…
#include <stdio.h>
#define max 5//表示顺序表申请的空间大小
int enQueue(int *a,int front,int rear,int data){
//添加判断语句,如果rear超过max,则直接将其从a[0]重新开始存储,如果rear+1和front重合,则表示数组已满
if ((rear+1)%max==front) {
printf("空间已满");
return rear;
}
a[rear%max]=data;
rear++;
return rear;
}
int deQueue(int *a,int front,int rear){
//如果front==rear,表示队列为空
if(front==rear%max) {
printf("队列为空");
return front;
}
printf("%d ",a[front]);
//front不再直接 +1,而是+1后同max进行比较,如果=max,则直接跳转到 a[0]
front=(front+1)%max;
return front;
}
int main() {
int a[max];
int front,rear;
//设置队头指针和队尾指针,当队列中没有元素时,队头和队尾指向同一块地址
front=rear=0;
//入队
rear=enQueue(a,front,rear, 1);
rear=enQueue(a,front,rear, 2);
rear=enQueue(a,front,rear, 3);
rear=enQueue(a,front,rear, 4);
//出队
front=deQueue(a, front, rear);
//再入队
rear=enQueue(a,front,rear, 5);
//再出队
front=deQueue(a, front, rear);
//再入队
rear=enQueue(a,front,rear, 6);
//再出队
front=deQueue(a, front, rear);
front=deQueue(a, front, rear);
front=deQueue(a, front, rear);
front=deQueue(a, front, rear);
return 0;
}
2.链队列
定义两个指针top和rear分别指向队头和队尾,尾插法建立链表,之后进入出队操作
出队操作:
#include<stdio.h>
#include<stdlib.h>
typedef struct queue
{
int data;
struct queue* next;
}queue;
queue* initQueue()//创建头节点
{
queue* head = (queue*)malloc(sizeof(queue));
head->next = NULL;
return head;
}
queue* puchQueue(queue* rear, int data)
{
queue* p = (queue*)malloc(sizeof(queue));
p->data = data;
p->next = NULL;
rear->next = p;
rear = p;
return rear;
}
queue* popQueue(queue* top,queue* rear)
{
if (top->next == NULL)
{
printf("空队列\n");
return rear;
}
else
{
queue* p = top->next;
printf("出队元素:%d\n", p->data);
top->next = p->next;
if (rear == p)
{
rear = top;
}
free(p);
return rear;
}
}
int main()
{
queue* top, * rear, * queue;//这里面的queue就是建立的头节点,方便以下的操作
queue = top = rear = initQueue();
int n, m;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &m);
rear = puchQueue(rear, m);
}
for (int i = 0; i <= n; i++)
{
rear = popQueue(top, rear);
}
return 0;
}