3)数据结构之线性表(栈与队列)
Posted 流浪孤儿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3)数据结构之线性表(栈与队列)相关的知识,希望对你有一定的参考价值。
目录
栈与队列的本质
栈与队列都是属于线性表,那么它们的逻辑结构必定是线性结构的,线性表的概念可以参见
数据结构之线性表,它们的物理结构依赖于它们的实现方式
栈的基本概念
栈(Stack):是只允许在一端进行插入或者删除操作的线性表。
栈一共有两端:
允许进行插入删除的那一端称为栈顶(Top)
固定的,不允许进行插入删除的另一端称为栈底(Bottom)
因此栈具有后进先出的特性
压栈:栈的插入操作,入数据在栈顶。
出栈:栈的删除操作,出数据也在栈顶。
入栈、出栈的方式与栈顶指针的初始化不同而有所不同
下面是栈顶指针初始化为-1的情况
a、栈空时
b、入栈与出栈的过程
进栈操作:栈不满时,栈顶指针先加1,再送值到栈顶元素;
出栈操作:栈非空时,先取栈顶元素值,再将栈顶指针减1。
下面是栈顶指针初始化为0的情况
栈的物理结构
栈的物理结构依赖于它的实现,栈可以用数组或者链表实现,使用数组实现其物理结构是连续的,使用链表实现其物理结构非连续的。相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。(栈的实现推荐用数组)
栈的顺序存储结构(用数组实现)
采用顺序存储的栈称为顺序栈,其又分为
静态栈:使用定长数组实现
动态栈:使用动态开辟的数组实现
静态栈不实用,因为栈需要进行大量的入栈与出栈操作,静态栈的空间大小难以改变,容易出现空间不足或者大量空间浪费的情况,因此顺序栈一般指的就是动态栈的实现
静态栈的实现:
实现的思路:
1、静态栈的定义
2、栈的初始化
3、判空(判断栈是否为空)、进栈、出栈、取栈顶元素
在visual studio 2017下新建一个工程,并建立三个文件如图
Stack.h中
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
//静态栈:采用定长数组实现的栈
#define MaixSize 100
typedef int StackDataType;
typedef struct Stack
{
StackDataType _array[MaixSize];//定长数组
int _top;//表示栈顶指针,该栈顶指针是抽象化的概念,用_top整型变量就可以表示了
}Stack;
void StackInit(Stack*pStack);//栈的初始化
bool StackIsEmpty(Stack*pStack);//判断栈是否为空
void StackPush(Stack*pStack, StackDataType data);//入栈
StackDataType StackPop(Stack*pStack);//出栈
StackDataType StackGetTop(Stack *pStack);//获取栈顶元素
Test/c中
#include"Stack.h"
void Test()
{
Stack S;
StackInit(&S);
printf("S._top=%d\\n", S._top);
printf("0表示栈不为空,1表示空:");
printf("%d\\n", StackIsEmpty(&S));
printf("**************************\\n");
printf("入栈:\\n");
StackPush(&S, 1);
printf("栈顶元素:%d\\n", StackGetTop(&S));
printf("**************************\\n");
printf("入栈:\\n");
StackPush(&S, 2);
printf("栈顶元素:%d\\n", StackGetTop(&S));
printf("**************************\\n");
printf("入栈:\\n");
StackPush(&S, 3);
printf("栈顶元素:%d\\n", StackGetTop(&S));
printf("**************************\\n");
printf("入栈:\\n");
StackPush(&S, 4);
printf("栈顶元素:%d\\n", StackGetTop(&S));
printf("**************************\\n");
printf("入栈:\\n");
StackPush(&S, 5);
printf("S._top=%d\\n", S._top);
printf("0表示栈不为空,1表示空:");
printf("%d\\n", StackIsEmpty(&S));
printf("栈顶元素:%d\\n", StackGetTop(&S));
printf("**************************\\n");
printf("出栈:%d\\n", StackPop(&S));
printf("S._top=%d\\n", S._top);
printf("0表示栈不为空,1表示空:");
printf("%d\\n", StackIsEmpty(&S));
printf("栈顶元素:%d\\n",StackGetTop(&S));
}
int main()
{
Test();
return 0;
}
Stack.c中
#include"Stack.h"
void StackInit(Stack*pStack)
{
assert(pStack);
pStack->_top = -1;//初始化栈顶指针,-1表示栈为空
}
bool StackIsEmpty(Stack*pStack)
{
assert(pStack);
if (pStack->_top == -1)
return true;
else
return false;
}
void StackPush(Stack*pStack,StackDataType data)
{
assert(pStack);
assert(pStack->_top+1 != MaixSize);//防止栈溢出
pStack->_top += 1;//入栈指针先加1
pStack->_array[pStack->_top] = data;//再将元素放入栈中
}
StackDataType StackGetTop(Stack *pStack)
{
assert(pStack);
assert(pStack->_top != -1);//栈空报错
return pStack->_array[pStack->_top];
}
StackDataType StackPop(Stack*pStack)
{
StackDataType tmp = StackGetTop(pStack);//先取出栈顶元素
pStack->_top -= 1;//再将指针减1
return tmp;
}
输出结果
动态栈的实现
实现的思路:
1、动态栈的定义
2、动态栈的初始化
3、判空、栈的扩容、入栈、出栈、获取栈顶元素
4、栈的销毁
同理
Stac.h中
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#define InitSize 3
typedef int StackDataType;
typedef struct Stack
{
StackDataType*S;//定义指向动态开辟的数组的指针
int _top;//代表了栈顶指针
int _capacity;//记录了当前数组的大小
}Stack;
void StackInit(Stack*pStack);//栈的初始化,将_top初始化为0
static void StackAppend(Stack *pStack);//栈的扩容
void StackPush(Stack*pStack,StackDataType data); //入栈
bool StackIsEmpty(Stack*pStack);//判断栈是否为空,空返回1,非空返回0
StackDataType StackPop(Stack*pStack);//出栈
StackDataType StackGetTop(Stack*pStack);//获取栈顶元素
void StackDestroy(Stack*pStack);//栈的销毁
Test.c中
#include"Stack.h"
void Test()
{
Stack A;
printf("初始化动态栈:\\n");
StackInit(&A);
printf("是否为空:%d\\n", StackIsEmpty(&A));
printf("***********************\\n");
printf("入栈:\\n");
StackPush(&A,1);
printf("栈顶元素为:%d\\n", StackGetTop(&A));
printf("是否为空:%d\\n", StackIsEmpty(&A));
printf("***********************\\n");
printf("入栈:\\n");
StackPush(&A,2);
printf("栈顶元素为:%d\\n", StackGetTop(&A));
printf("***********************\\n");
printf("入栈:\\n");
StackPush(&A,3);
printf("栈顶元素为:%d\\n", StackGetTop(&A));
printf("***********************\\n");
printf("入栈:\\n");
StackPush(&A,4);
printf("栈顶元素为:%d\\n", StackGetTop(&A));
printf("***********************\\n");
printf("入栈:\\n");
StackPush(&A,5);
printf("栈顶元素为:%d\\n", StackGetTop(&A));
printf("***********************\\n");
printf("入栈:\\n");
printf("出栈:%d\\n", StackPop(&A));
printf("栈顶元素为:%d\\n", StackGetTop(&A));
printf("***********************\\n");
printf("栈的销毁:\\n");
StackDestroy(&A);
}
int main()
{
Test();
return 0;
}
Stack.c中
#include"Stack.h"
void StackInit(Stack*pStack)
{
assert(pStack);
pStack->S = (StackDataType*)malloc(sizeof(StackDataType)*InitSize);
if (pStack->S == NULL)
return;
pStack->_top = 0;
pStack->_capacity = InitSize;
}
static void StackAppend(Stack *pStack)
{
assert(pStack);
if (pStack->_top == pStack->_capacity)
{
pStack->_capacity *= 2;
StackDataType*tmp = (StackDataType*)malloc(sizeof(StackDataType)*(pStack->_capacity));
if (tmp == NULL)
{
pStack->_capacity /= 2;
return;
}
pStack->S = tmp;
}
}
void StackPush(Stack*pStack, StackDataType data)
{
StackAppend(pStack);
pStack->S[pStack->_top] = data;//先放数据
pStack->_top++;//栈顶指针加1
}
bool StackIsEmpty(Stack*pStack)
{
assert(pStack);
if (pStack->_top == 0)
return true;
else
return false;
}
StackDataType StackPop(Stack*pStack)
{
assert(!StackIsEmpty(pStack));
pStack->_top--;//出栈先栈顶指针减1
StackDataType tmp = pStack->S[pStack->_top];//再将数据移除
return tmp;
}
StackDataType StackGetTop(Stack*pStack)
{
assert(!StackIsEmpty(pStack));
return pStack->S[pStack->_top - 1];
}
void StackDestroy(Stack*pStack)
{
assert(pStack);
assert(pStack->S);
free(pStack->S);
pStack->S = NULL;
}
运行结果
栈的链式存储结构(使用链表实现)
采用链式存储的栈称为链栈,通常采用不带头结点的单链表实现规定所有操作都是在单链表的表头进行的,链栈不存在栈溢出的情况,但使用单链表尾插代价不小。
实现思路:
1、链栈的定义与初始化
2、入栈‘、出栈、获取栈顶元素、统计栈中元素个数
3、链栈的销毁
同上
Stack.h中
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int StackDataType;
typedef struct Stack
{
struct Stack *_next;
StackDataType _data;
}Stack;
void StackInit(Stack**ppStack,StackDataType data);//链栈初始化
void StackPush(Stack**ppStack, StackDataType data);//入栈
StackDataType StackPop(Stack**ppStack);//出栈
StackDataType StackGetTop(Stack*pStack);//获取栈顶元素
size_t StackDataNum(Stack*pStack);//获取栈的数据数量
void StackDestroy(Stack**ppStack);//栈的销毁
Test.c中
#include"Stack.h"
void Test()
{
printf("栈的初始化:\\n");
Stack *Head = NULL;
StackInit(&Head, 1);
printf("栈顶元素:%d\\n", StackGetTop(&Head));
printf("栈中总元素个数:%d\\n", StackDataNum(Head));
printf("*************************************\\n");
printf("入栈\\n");
StackPush(&Head, 2);
printf("栈顶元素:%d\\n", StackGetTop(&Head));
printf("栈中总元素个数:%d\\n", StackDataNum(Head));
printf("*************************************\\n");
printf("入栈\\n");
StackPush(&Head, 3);
printf("栈顶元素:%d\\n", StackGetTop(&Head));
printf("栈中总元素个数:%d\\n", StackDataNum(Head));
printf("*************************************\\n");
printf("入栈\\n");
StackPush(&Head, 4);
printf("栈顶元素:%d\\n", StackGetTop(&Head));
printf("栈中总元素个数:%d\\n", StackDataNum(Head));
printf("*************************************\\n");
printf("入栈\\n");
StackPush(&Head, 5);
printf("栈顶元素:%d\\n", StackGetTop(&Head));
printf("栈中总元素个数:%d\\n", StackDataNum(Head));
printf("*************************************\\n");
printf("出栈\\n");
StackPop(&Head);
printf("栈顶元素:%d\\n", StackGetTop(&Head));
printf("栈中总元素个数:%d\\n", StackDataNum(Head));
printf("*************************************\\n");
printf("栈的销毁:\\n");
StackDestroy(&Head);
}
int main()
{
Test();
return 0;
}
Stack.c
#include"Stack.h"
void StackInit(Stack **ppStack, StackDataType data)
{
*ppStack = (Stack*)malloc(sizeof(Stack));
assert(*ppStack);
(*ppStack)->_data = data;
(*ppStack)->_next = NULL;
}
void StackPush(Stack**ppStack, StackDataType data)
{
if (*ppStack == NULL)
{
StackInit(ppStack, data);
return;
}
Stack*cur = *ppStack;
while (cur->_next != NULL)
{
cur = cur->_next;
}
StackInit(&cur->_next, data);
}
StackDataType StackPop(Stack**ppStack)
{
assert(*ppStack);
if ((*ppStack)->_next == NULL)
{
StackDataType tmp = (*ppStack)->_data;
free(*ppStack);
*ppStack = NULL;
return tmp;
}
Stack*prev = *ppStack;
Stack*cur = (*ppStack)->_next;
while (cur->_next != NULL)
{
prev = cur;
cur = cur->_next;
}
StackDataType tmp = cur->_data;
free(cur);
prev->_next = NULL;
return tmp;
}
StackDataType StackGetTop(Stack*pStack)//注意这里使用了一级指针,因此pStack为链表头指针的拷贝
{
assert(pStack);
while (pStack->_next != NULL)
{
pStack = pStack->_next;//pStack的改变不会影响头指针的变化
}
return pStack->_data;
}
//StackGetTop这个函数建议接口和其它函数一样这样出错率低而且可以使代码高内聚
size_t StackDataNum(Stack*pStack)
{
size_t count = 0;
while (pStack != NULL)
{
count++;
pStack = pStack->_next;
}
return count;
}
void StackDestroy(Stack**ppStack)
{
size_t count = StackDataNum(*ppStack);
while (count)
{
StackPop(ppStack);
count--;
}
}
运行结果
队列的概念:
队列(Queue)简称队,队列也是一种线性表,因此其逻辑结构必定是连续的,其物理结构依赖于它的实现,队的实现可以用数组或者链表,与栈不同的是队列可以使用两个端口,队列只允许在一端进行插入,在另一端进行删除。
入队(进队):向队列中插入元素的操作
出队(离队):删除队列元素的操作
因此队具有先进先出的特性
队列的实现
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
队列只介绍其链式存储结构(使用链表实现)
队的链式存储
使用不带头结点的单链表实现,有两个指针分别指向两个端口,分别为队头指针、队尾指针
思路:
1、队列的定义与初始化与管家的使用
2、判空、入队、出队、取队头元素
3、队的销毁
同理
Queue.h中
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QueueDataType;
typedef struct QueueNode//链式队列结点
{
struct QueueNode *_next;
QueueDataType _data;
}QueueNode;
typedef struct Queue//队列的队头与队尾指针用Queue结构体保管,将其称为管家
{
QueueNode *_front;
QueueNode *_rear;
}Queue;
void QueueInit(Queue *pQueue, QueueDataType data);//队列的初始化
bool QueueIsEmpty(Queue *pQueue);//判断队列是否为空
void QueuePush(Queue *pQueue, QueueDataType data);//入队
QueueDataType QueuePop(Queue *pQueue);//出队
QueueDataType QueueGetTOP(Queue *pQueue);//读队头元素
size_t QueueGetNum(Queue*pQueue);//统计队中元素个数
void QueueDestroy(Queue *pQueue);//队的销毁
Queue.c中
#include"Queue.h"
void QueueInit(Queue *pQueue,QueueDataType data)
{
assert(pQueue);
pQueue->_front = (QueueNode *)malloc(sizeof(QueueNode));
if (pQueue->_front == NULL)
return;
pQueue->_rear = pQueue->_front;
pQueue->_front->_data = data;//结点的数据域
pQueue->_front->_next = NULL;//结点的指针域
}
bool QueueIsEmpty(Queue *pQueue)
{
assert(pQueue);
if (pQueue->_front == NULL)
return true;
else
return false;
}
void QueuePush(Queue *pQueue, QueueDataType data)
{
if (QueueIsEmpty(pQueue))
{
QueueInit(pQueue, data);
return;
}
pQueue->_rear->_next = (QueueNode*)malloc(sizeof(QueueNode));//入队从rear端入
if (pQueue->_rear == NULL)
return;
pQueue->_rear = pQueue->_rear->_next;
pQueue->_rear->_data = data;
pQueue->_rear->_next = NULL;
}
QueueDataType QueuePop(Queue *pQueue)
{
assert(!QueueIsEmpty(pQueue));
if (pQueue->_front == pQueue->_rear)//此时队列不为空,而且两指针又相等,那么该队列只有一个元素
{
QueueDataType data = pQueue->_front->_data;
free(pQueue->_front);
pQueue->_front = pQueue->_rear = NULL;//目的就是为了使队尾指针置空
return data;
}
QueueDataType data = pQueue->_front->_data;//出队从front端出
Queue *tmp = pQueue->_front->_next;
free(pQueue->_front);
pQueue->_front = tmp;
return data;
}
QueueDataType QueueGetTOP(Queue *pQueue)
{
assert(!QueueIsEmpty(pQueue));
return pQueue->_front->_data;
}
size_t QueueGetNum(Queue*pQueue)
{
assert(pQueue);
if (pQueue->_front == NULL)
return 0;
size_t count = 0;
QueueNode *cur = pQueue->_front;
do
{
count++;
cur = cur->_next;
} while (cur!=NULL);
return count;
}
void QueueDestroy(Queue *pQueue)
{
assert(pQueue);
assert(!QueueIsEmpty(pQueue));//如果为空队列就不用再销毁了
size_t count = QueueGetNum(pQueue);
while (count)
{
QueuePop(pQueue);
count--;
}
}
Test.c中
#include"Queue.h"
void Test()
{
Queue Q;//定义一个管家
QueueInit(&Q, 1);
printf("队头元素:%d\\n", QueueGetTOP(&Q));
printf("0表示非空队列1表示空队列:%d\\n", QueueIsEmpty(&Q));
printf("栈中元素个数:%d\\n", QueueGetNum(&Q));
printf("***********************************\\n");
printf("入队:\\n");
QueuePush(&Q, 2);
printf("队头元素:%d\\n", QueueGetTOP(&Q));
printf("栈中元素个数:%d\\n", QueueGetNum(&Q));
printf("***********************************\\n");
printf("入队:\\n");
QueuePush(&Q, 3);
printf("队头元素:%d\\n", QueueGetTOP(&Q));
printf("栈中元素个数:%d\\n", QueueGetNum(&Q));
printf("***********************************\\n");
printf("入队:\\n");
QueuePush(&Q, 4);
printf("队头元素:%d\\n", QueueGetTOP(&Q));
printf("栈中元素个数:%d\\n", QueueGetNum(&Q));
printf("***********************************\\n");
printf("入队:\\n");
QueuePush(&Q, 5);
printf("队头元素:%d\\n", QueueGetTOP(&Q));
printf("栈中元素个数:%d\\n", QueueGetNum(&Q));
printf("***********************************\\n");
printf("入队:\\n");
QueuePush(&Q, 6);
printf("队头元素:%d\\n", QueueGetTOP(&Q));
printf("栈中元素个数:%d\\n", QueueGetNum(&Q));
printf("***********************************\\n");
printf("出队:%d\\n", QueuePop(&Q));
printf("队头元素:%d\\n", QueueGetTOP(&Q));
printf("栈中元素个数:%d\\n", QueueGetNum(&Q));
printf("***********************************\\n");
printf("出队:%d\\n", QueuePop(&Q));
printf("队头元素:%d\\n", QueueGetTOP(&Q));
printf("栈中元素个数:%d\\n", QueueGetNum(&Q));
printf("***********************************\\n");
printf("出队:%d\\n", QueuePop(&Q));
printf("队头元素:%d\\n", QueueGetTOP(&Q));
printf("栈中元素个数:%d\\n", QueueGetNum(&Q));
printf("***********************************\\n");
printf("栈的销毁\\n");
QueueDestroy(&Q);
}
int main()
{
Test();
return 0;
}
运行结果
以上是关于3)数据结构之线性表(栈与队列)的主要内容,如果未能解决你的问题,请参考以下文章