数据结构复习3。
复习到了这一块, 感觉写完链表、线性表,写这块很轻松!
栈、队列的实现是很简单的,因为它提供功能比较单一。 因此实现上就很简单。
栈、队列的性质在很多场合都会使用到。在解决一些问题的时候会独显它的特色。
1、栈呢,先进后出。
设计的时候需要把握这个点, 线性表的尾插、尾删就很好的满足栈的特性, 而且线性表尾部操作效率为o(1), 因此栈的设计我会用线性表去做。
(当然队列也可以做哦, 不过设计起来方不方便就是一回事了。)
2、队列呢, 先进先出。
线性表就不行了,效率不太好, 而链表却可以很好胜任。
链表的头插、头删可以满足先进先出的特点, 并且加以优化链表尾部操作可以为o(1)(其实就是加个尾部指针,专门负责尾部插入、删除), 因此队列我会用链表去做。
3、双向循环链表。
我会采用定长的数组去做。不过在双向循环链表这块, 会有一些考点,也是设计需要考虑的问题。
既然是定长,就会存在队满、队空的判断条件。
队满条件:(rear + 1) % (N + 1) == front;
队空条件: rear == front;
这是面试很多都会考到的。 我说一些别的,在设计双向循环链表的时候,假如说我们开辟N大小空间, 实际上真正开辟时我们会开辟N+1大小空间。 方便设计! 这也是为什么上面写的队满条件里面是N+1。
写完这些我会在下一篇我会写往年栈、队列的考题,像链表题目一样去做!
下面是实现代码:
heap.h
#pragma once
/*
project: 模拟实现栈
date: 2020-6-12
name: HMW
*/
#include<stdio.h>
#include<stdlib.h>
//#include<vld.h>
typedef int DataType; //重定义int
//构建栈的结构 -- 采用线性表实现
typedef struct Stack
{
DataType *_data; //基地址
size_t _top; //栈顶
size_t _capcity; //容量
}Stack;
void StackInit(Stack* ps); //栈的初始化
void StackDestory(Stack* ps); //栈的释放
void StackPush(Stack* ps, DataType x); //入栈操作
void StackPop(Stack* ps); //出栈操作
DataType StackTop(Stack* ps); //返回栈顶元素
int StackEmpty(Stack* ps); //判断栈是否为空
heap.c
#include"heap.h"
//栈的初始化
void StackInit(Stack* ps)
{
if (!ps)
return;
ps->_data = NULL; //我不想在这里分配空间, 在插入操作进行分配空间操作
ps->_capcity = ps->_top = 0; //top 为存放的个数
}
//栈的释放
void StackDestory(Stack* ps)
{
if (!ps)
return;
free(ps->_data);
ps->_data = NULL;
}
//栈的插入操作
void StackPush(Stack* ps, DataType x)
{
if (!ps)
return;
if (ps->_capcity == ps->_top) //栈满
{
//开辟空间
int news = ps->_data == NULL ? 10 : 2*ps->_capcity; //三目运算开辟容量大小
ps->_data = (DataType *)realloc(ps->_data, sizeof(DataType) * news);
if (!ps->_data)
exit(0);
ps->_capcity = news;
}
ps->_data[ps->_top++] = x; //插入元素
}
//出栈操作
void StackPop(Stack* ps)
{
//在出栈操作就应该进行打印, 出一个元素打印一个元素!
if (!ps)
return;
if (ps->_top != 0)
{
ps->_top--;
}
else
{
//相当于没有元素可删
printf("\n当前栈中没有元素!\n");
return;
}
}
//返回栈顶元素
DataType StackTop(Stack* ps)
{
if (!ps)
return;
return ps->_data[ps->_top - 1];
}
//判断栈是否为空
int StackEmpty(Stack* ps)
{
if (!ps)
exit(0);
return ps->_top == 0; //默认返回1为为空, 0不为空
}
test.c
#include"heap.h"
void test_Stack()
{
Stack ps;
StackInit(&ps);
StackPush(&ps, 1);
StackPush(&ps, 2);
StackPush(&ps, 4);
StackPush(&ps, 5);
StackPush(&ps, 6);
printf("%d\n", StackTop(&ps));
StackPop(&ps);
printf("%d\n", StackTop(&ps));
StackPop(&ps);
printf("%d\n", StackTop(&ps));
StackPop(&ps);
printf("%d\n", StackTop(&ps));
StackDestory(&ps);
}
int main()
{
test_Stack();
system("pause");
return 0;
}
测试结果:
队列实现:
queue.h
#pragma once
/*
project: 模拟队列实现(采用不带头的单链表)
date: 2020-6-12
name: HMW
*/
#include<stdio.h>
#include<stdlib.h>
//#include<vld.h>
typedef int QUDataType;
//采用链表实现队列, 不带头
typedef struct QueueNode
{
QUDataType _data;
struct Queue *_next;
}QueueNode;
//创建两个结构体指针 front, rear, 进行操作!
typedef struct Queue
{
QueueNode *front;
QueueNode *rear;
size_t lengh;
}Queue;
void QueueInit(Queue* pq); //队列的初始化
void QueueDestory(Queue* pq); //队列的销毁
QueueNode* BuyQueueNode(QUDataType x); //构建节点
void QueuePush(Queue* pq, QUDataType x); //队列的插入操作
void QueuePop(Queue* pq); //队列的删除操作
QUDataType QueueFront(Queue* pq); //返回队头元素
QUDataType QueueBack(Queue* pq); //返回队尾元素
int QueueEmpty(Queue* pq); //判断队列是否为空
queue.c
#include"queue.h"
//队列的初始化, 切记: 链表实现
void QueueInit(Queue* pq)
{
if (!pq)
return;
pq->front = pq->rear = NULL; //头结点都不会在初始化开辟空间
pq->lengh = 0;
}
//队列的销毁
void QueueDestory(Queue* pq)
{
if (!pq)
return;
if (pq->front == NULL)
return;
else
{
while (pq->front != NULL)
{
QueueNode *p = pq->front->_next;
free(pq->front);
pq->front = p;
}
}
}
//构建节点
QueueNode* BuyQueueNode(QUDataType x)
{
QueueNode *pq = (QueueNode *)malloc(sizeof(QueueNode));
if (!pq)
exit(0);
pq->_data = x;
pq->_next = NULL;
return pq;
}
//队列的入队操作
void QueuePush(Queue* pq, QUDataType x)
{
if (!pq)
return;
if (pq->front == NULL)
{
pq->front = pq->rear = BuyQueueNode(x);
++pq->lengh;
}
else
{
pq->rear->_next = BuyQueueNode(x);
pq->rear = pq->rear->_next;
++pq->lengh;
}
}
//队列的出队操作
void QueuePop(Queue* pq)
{
if (!pq)
return;
if (pq->front == NULL) //队空
{
return;
}
else
{
QueueNode *p = pq->front->_next;
free(pq->front);
pq->front = p;
if (pq->front == NULL)
pq->rear = NULL; //最后一个元素删除,rear也要进行操作!
}
}
//返回队头元素
QUDataType QueueFront(Queue* pq)
{
if (!pq)
return;
if (pq->front != NULL)
return pq->front->_data;
else
return -1;
}
//返回队尾元素 -- 这个没必要吧???
QUDataType QueueBack(Queue* pq)
{
if (!pq)
return;
if (pq->rear != NULL)
return pq->rear->_data;
else
return -1; //这个返回值不太好, 如果是unsigend类型倒可以。
}
//判断队列是否为空
int QueueEmpty(Queue* pq)
{
if (!pq)
if (pq->front == NULL)
return 0;
else
return 1;
}
//队列的长度
int QueueSize(Queue* pq)
{
return pq->lengh;
}
test.c
#include"queue.h"
void testQueue()
{
Queue pq;
QueueInit(&pq);
printf("插入元素1,2,3,4,5,6,7\n");
QueuePush(&pq, 1);
QueuePush(&pq, 2);
QueuePush(&pq, 3);
QueuePush(&pq, 4);
QueuePush(&pq, 5);
QueuePush(&pq, 6);
QueuePush(&pq, 7);
printf("队顶元素\n");
printf("%d\n", QueueFront(&pq));
printf("出队\n");
QueuePop(&pq);
printf("队顶元素\n");
printf("%d\n", QueueFront(&pq));
QueueDestory(&pq);
}
int main()
{
testQueue();
system("pause");
return 0;
}
测试结果:
双向循环队列:
rqueue.h
#pragma once
/*
project: 模拟实现环形队列
date: 2020-6-12
name: HMW
description: 环形队列使用定长的数组实现。
*/
#include<stdio.h>
#include<stdlib.h>
//#include<vld.h>
#define N 5 //循环队列的存储个数
typedef int DataType; //重定义int
//循环队列 -- 线性表实现
typedef struct RNode
{
DataType *_data; //基地址
int _front; //front下标
int _rear; //rear下标
}RQueue;
void RQueueInit(RQueue *qu); //初始化
void RQueueDestroy(RQueue *qu); //销毁
void RQueuePush(RQueue *qu, DataType x); //插入
void RQueuePop(RQueue *qu); //删除
void RQueuePrint(RQueue *qu); //打印
rqueue.c
#include"rqueue.h"
//初始化
void RQueueInit(RQueue *qu)
{
if (!qu) //刚进来会是随机值
return;
qu->_data = (DataType *)malloc(sizeof(DataType) * (N + 1)); //分配N+1个空间 - 有一个空间是不会存储元素的
if (!qu)
return; //分配失败
qu->_front = qu->_rear = 0; //起始front、rear下标置为0
}
//销毁
void RQueueDestroy(RQueue *qu)
{
if (!qu)
return;
free(qu->_data); //释放空间
qu->_data = NULL;
}
//判断是否队满
int RQueueOver(RQueue * qu)
{
if (!qu)
exit(0);
if ((qu->_rear + 1) % (N + 1) == (qu->_front))
return 1; //队满条件 -- (read + 1) % (N + 1) == front
else
return 0;
}
//插入
void RQueuePush(RQueue *qu, DataType x)
{
if (!qu)
return;
if (RQueueOver(qu)) //判断是否队满
{
//printf("队满啦!\n");
return;
}
qu->_data[qu->_rear++] = x; //插入
if (qu->_rear == N + 1)
{
qu->_rear = 0; //重置rear下标
printf("队满, 更新rear下标!\n");
}
}
//判断是否队空
int RQueueEmpty(RQueue *qu)
{
if (!qu)
return;
if (qu->_front == qu->_rear) //队满条件front == rear
return 1;
else
return 0;
}
//删除
void RQueuePop(RQueue *qu)
{
if (!qu)
return;
if (RQueueEmpty(qu)) //判断是否队满
{
printf("队空!\n");
return;
}
++qu->_front; //front偏移
if (qu->_front == N + 1)
{
qu->_front = 0; //重置front
printf("更新front下标\n");
}
}
//打印
void RQueuePrint(RQueue *qu)
{
if (!qu)
return;
int i = qu->_front;
for (i; i < qu->_rear; ++i)
{
printf("%d ", qu->_data[i]);
}
printf("\n");
}
test.c
#include"rqueue.h"
void test_RQueue()
{
RQueue qu;
RQueueInit(&qu);
printf("N = 5, 存放元素1,2,3,4,5\n");
RQueuePush(&qu, 1);
RQueuePush(&qu, 2);
RQueuePush(&qu, 3);
RQueuePush(&qu, 4);
RQueuePush(&qu, 5);
RQueuePrint(&qu);
printf("后续每出一个元素,打印一次\n");
RQueuePop(&qu);
RQueuePrint(&qu);
RQueuePop(&qu);
RQueuePrint(&qu);
RQueuePop(&qu);
RQueuePrint(&qu);
RQueuePop(&qu);
RQueuePrint(&qu);
RQueuePop(&qu);
RQueuePrint(&qu);
RQueuePop(&qu);
RQueueDestroy(&qu);
}
int main()
{
test_RQueue();
system("pause");
return 0;
}
测试结果:
好了。 算是将以前写的代码总结了! 既圆了发博客的心, 又填补了最近复习的知识。