坐骑图鉴

一、什么是栈和队列

(1)栈:

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素————百度百科

由此可见栈是一种先进后出(First In Last Out)即FILO结构。

(2)队列

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头————百度百科

队列是一种先进先出(First In First Out)的结构即FIFO结构。

二、线性栈的操作实现栈可分为线性栈和链式栈,不同的实现方法匹配于不同的场景。

(1)栈的结构定义:

代码语言:javascript复制typedef int STDataType;

typedef struct Stack {

STDataType* a;//线性栈需要数组

int top;//栈顶指针记录元素个数

int capacity;//栈的容量

}ST;(2)栈的初始化:

代码语言:javascript复制ST* InitNewStack(STDataType n)

{

ST* p = (ST*)malloc(sizeof(ST));//初始化生成栈

p->a = (STDataType*)malloc(sizeof(STDataType) * n);//对栈的数组进行开辟

if (p->a == NULL)//开辟检查

{

perror("malloc");

exit(-1);

}

p->top = -1;//将指针置为-1,也可以为0,只不过后续实现有稍微不同

p->capacity = n;//将容量设置好

return p;//返回新的栈

}(3)入栈操作:

代码语言:javascript复制void StackPush(ST* st, STDataType x)

{

assert(st);

if (st->top == st->capacity)//检查栈是否满了

{

STDataType* tmp = (STDataType*)realloc(st->a, sizeof(st->capacity) * 2 * sizeof(STDataType));//栈满了进行扩容操作

if (tmp == NULL)//扩容检查

{

perror("realloc");

exit(-1);

}

st->a = tmp;

st->capacity *= 2;//将容量记录扩大

}

st->a[++st->top] = x;//对元素进行入栈,栈顶元素+1

}(4)出栈操作:

代码语言:javascript复制void StackPop(ST* st)

{

assert(st);//断言是否为空栈

if (st->top >= 0)

st->top -= 1;//只要不为空栈,仅仅将栈顶指针-1即可

return;

}(5)栈判空:

代码语言:javascript复制bool StackEmpty(ST* st)

{

assert(st);

return st->top == -1;//当top为-1表示空栈

}(6)取栈顶元素:

代码语言:javascript复制STDataType StackFront(ST* st)

{

assert(st);

return st->a[st->top];//直接返回栈顶元素即可

}(7)打印栈:

代码语言:javascript复制void Print(ST* st)//进行打印

{

assert(st);

int len = sizeof(st->a) / sizeof(STDataType);

int i = 0;

for (i = 0; i <= st->top; i++)

{

printf("%3d", i);

}

printf("\n");

for (i = 0; i <= st->top; i++)

{

printf("---");

}

printf("\n");

for (i = 0; i <= st->top; i++)

{

printf("%3d", st->a[i]);

}

printf("\n\n\n");

return;

}(8)销毁栈:

代码语言:javascript复制void StackClear(ST* st)//由内而外进行销毁,防止内存泄漏

{

assert(st);

free(st->a);

st->a = NULL;

free(st);

st = NULL;

return;

}(9)测试栈:

代码语言:javascript复制void Test()

{

ST* st = InitNewStack(MAX_OP);

int i = 0;

StackPush(st, 1);

if (!StackEmpty(st))

{

StackPush(st, 2);

StackPush(st, 3);

StackPush(st, 4);

StackPush(st, 5);

StackPush(st, 6);

StackPush(st, 7);

StackPush(st, 8);

Print(st);

}

if (!StackEmpty(st))

{

StackPop(st);

StackPop(st);

StackPop(st);

Print(st);

}

printf("StackFront: %d\n", StackFront(st));

StackClear(st);

return;

}结果打印为:

怎么样,栈的操作实现是不是挺简单的,也没有想象的那么难,下面我们看看链式栈的实现方式吧。

三、链式栈的操作实现(1)栈的结构定义:

代码语言:javascript复制typedef int STDataType;

typedef struct STNode {//栈的元素

struct STNode* next;//指针域

STDataType data;//数据域

}Node;

typedef struct Stack {//栈顶指针

Node* top;

}ST;(2)栈的初始化:

代码语言:javascript复制Node* InitNewNode(STDataType x)//栈节点的初始化

{

Node* p = (Node*)malloc(sizeof(Node));//开辟新节点

if (p == NULL)//节点检查

{

perror("malloc");

exit(-1);

}

p->next = NULL;//指针域置空

p->data = x;//数据域赋值

return p;//返回新节点

}

void InitNewST(ST* st)

{

assert(st);

st->top = NULL;//将栈顶指针置空

return;

}(3)入栈操作:

代码语言:javascript复制void StackPush(ST* st, STDataType x)

{

assert(st);

if (st->top == NULL)//栈顶指针为空时

{

Node* node = InitNewNode(x);

st->top = node;

return;

}

Node* node = InitNewNode(x);

node->next = st->top;//头插法入栈

st->top = node;//栈顶指针指向头节点

return;

}(4)出栈操作:

代码语言:javascript复制void StackPop(ST* st)

{

assert(st);

if (st->top == NULL)//空栈直接返回

return;

if (st->top->next)//栈的元素大于1个

{

Node* tmp = st->top;//直接进行头删

st->top = tmp->next;//指针指向下一个节点

free(tmp);

return;

}

free(st->top);//只有一个节点直接删除,并将top置空

st->top = NULL;

return;

}(5)栈判空:

代码语言:javascript复制bool StackEmpty(ST* st)

{

assert(st);

return st->top == NULL;//top不为空指针

}(6)取栈顶元素:

代码语言:javascript复制Node* StackTop(ST* st)

{

assert(st);

return st->top;//直接返回栈顶节点

}(7)打印栈:

代码语言:javascript复制void output(ST* st)

{

assert(st);

Node* p = st->top;

int len = 0;

while (p)

{

p = p->next;

len += 1;

}

int i = 0;

for (i = 0; i < len; i++)

{

printf("%3d", i);

printf(" ");

}

printf("\n");

for (i = 0; i < len; i++)

{

printf("-----");

}

printf("\n");

p = st->top;

while (p)

{

printf("%3d", p->data);

printf("->");

p = p->next;

}

printf("\n\n\n");

return;

}(8)销毁栈:

代码语言:javascript复制void StackDestroy(ST* st)

{

assert(st);

while (st->top)

{

Node* tmp = st->top->next;//记录栈顶指针的下一个元素

free(st->top);//释放掉当前元素

st->top = tmp;//再把栈顶指针指向下一个元素

}

return;

}(9)测试栈:

代码语言:javascript复制void Test()

{

ST st;

InitNewST(&st);

StackPush(&st, 5);

StackPush(&st, 4);

StackPush(&st, 3);

StackPush(&st, 2);

StackPush(&st, 1);

output(&st);

if (!StackEmpty(&st))

{

StackPop(&st);

StackPop(&st);

output(&st);

}

Node* p = StackTop(&st);

printf("The Stack Top Element is %d\n", p->data);

StackDestroy(&st);

return;

}测试结果:

四、队列的操作实现这里队列就不实现线性队列了,线性队列可以参考上面的线性栈来写,这里主要实现一下链式队列

(1)队列的结构定义:

代码语言:javascript复制typedef int QDataType;

typedef struct QueueNode {//链式队列节点

struct QueueNode* next;

QDataType data;

}QNode;

typedef struct Queue {//队列

QNode* head;//队列头指针

QNode* tail;//队列尾指针

int size;//大小

}Que;(2)队列的初始化:

代码语言:javascript复制void InitNewQueue(Que* q)

{

assert(q);

q->head = q->tail = NULL;//头尾节点置空

q->size = 0;//初始化长度为0

return;

}(3)入队操作:

代码语言:javascript复制void QueuePush(Que* q, QDataType x)

{

assert(q);

QNode* newnode = (QNode*)malloc(sizeof(QNode));//开辟新的链式队列节点

if (newnode == NULL)//检测是否开辟失败

{

perror("malloc");

exit(-1);

}

newnode->data = x;

newnode->next = NULL;

if (q->tail == NULL)//尾指针为空即队列为空

{

q->head = q->tail = newnode;//头尾指针指向同一个元素

}

else//队列不为空

{

q->tail->next = newnode;//入队

q->tail = newnode;

}

q->size++;//记录大小

return;

}(4)出队操作:

代码语言:javascript复制void QueuePop(Que* q)

{

assert(q);

if (q->head->next == NULL)//只有一个节点

{

free(q->head);

q->head = q->tail = NULL;//将头尾节点置空

}

else//大于一个节点数量

{

QNode* next = q->head->next;//记录头指针下一个节点

free(q->head);//释放当前节点

q->head = next;//头指针移动

}

q->size -= 1;//记录大小

return;

}(5)队列判空:

代码语言:javascript复制bool QueueEmpty(Que* q)

{

assert(q);

return q->head == NULL;

}(6)取队首队尾元素:

代码语言:javascript复制QDataType QueueFront(Que* q)

{

assert(q);

assert(!QueueEmpty(q));//不为空队列直接返回头指针数据域

return q->head->data;

}

QDataType QueueBack(Que* q)

{

assert(q);

assert(!QueueEmpty(q));//不为空队列直接返回尾指针数据域

return q->tail->data;

}(7)队列大小:

代码语言:javascript复制QDataType QueueSize(Que* q)

{

assert(q);

return q->size;

}(8)销毁队列:

代码语言:javascript复制void QueueDestroy(Que* q)

{

assert(q);

while (q->head)//头指针不为空

{

QNode* tmp = q->head->next;//记录下头指针下一个节点

free(q->head);//释放当前节点

q->head = tmp;

}

q->head = q->tail = NULL;//头尾指针置空

q->size = 0;//数量清零

return;

}(9)测试队列:

代码语言:javascript复制void Test()

{

Que q;

InitNewQueue(&q);

QueuePush(&q, 1);

QueuePush(&q, 2);

QueuePush(&q, 3);

QueuePush(&q, 4);

QueuePush(&q, 5);

QueuePush(&q, 6);

printf("QueueSize : %d\n", QueueSize(&q));

while (!QueueEmpty(&q))

{

printf("%d ", QueueFront(&q));

QueuePop(&q);

}

printf("\n");

QueueDestroy(&q);

return;

}输出结果为:

如果你看到这里,觉得这篇文章对你有帮助的话,还望能三连支持一下~