栈和队列是特殊的线性表,其特殊之处是他们的基本操作只是线性表操作的一个子集。下面分别介绍这两种数据结构。
1. 栈(Stack)
栈(Stack)是只能在表尾进行插入或删除的线性表,是后进先出(LIFO)的线性表。
跟线性表相似,他的存储结构也包括顺序和链式两种。
顺序栈的定义及初始化如下:
//顺序栈
#define INIT_SIZE 100
#define INCREMENT 10
typedef struct {
ElemType *base; //栈底指针
ElemType *top; //栈顶指针
int stacksize; //目前已分配的空间
}SqStack;
int InitStack(SqStack *s) {
s.base = (ElemType *)malloc(INIT_SIZE * sizeof(ElemType));
if(s.base == NULL) return -1;
s.top = s.base;
s.stacksize = INIT_SIZE;
return 1;
}
当栈满时添加内存分配:
int isFull(SqStack *s){
if(s.top - s.base == s.stacksize)
s.base = (ElemType *) realloc (s, (s.stacksize+INCREMENT) * sizeof(ElemType));
if(s.base == NULL) return -1;
s.stacksize += INCREMENT;
return 1;
}
插入时s.top+1, 删除时s.top-1。
栈的链式结构及基本操作如下:
/*
对栈实现初始化(链式结构),插入栈顶元素,删除栈顶元素,遍历栈,清空栈等基本操作
*/
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define true 1
#define false 0
typedef struct Node
{
int data;
struct Node *pNext;
}NODE, *PNODE;
typedef struct Stack
{
PNODE pTop;
PNODE pBottom;
}STACK, *PSTACK;
void init(PSTACK pS);
void push(PSTACK pS, int val);
void traverse(PSTACK pS);
int pop(PSTACK pS , int *val);
void clear(PSTACK pS);
int empty(PSTACK pS);
int main(void)
{
STACK S ;
int val;
int i;
init(&S);
push(&S,1);
push(&S,2);
push(&S,3);
push(&S,4);
push(&S,5);
push(&S,6);
traverse(&S);
if(pop(&S ,&val))
{
printf("遍历成功,出栈元素为%d\n",val);
}
else
{
printf("出栈失败!\n");
}
traverse(&S);
clear(&S);
traverse(&S);
return 0 ;
}
//栈的初始化
void init(PSTACK pS)
{
pS -> pTop = (PNODE)malloc(sizeof(NODE));
if(NULL == pS -> pTop)
{
printf("动态内存分配失败!");
exit(-1);
}
else
{
pS -> pBottom = pS -> pTop;
pS -> pTop -> pNext = NULL;
}
return ;
}
//插入元素到栈顶
void push(PSTACK pS , int val)
{
PNODE pNew = (PNODE)malloc(sizeof(NODE));
pNew -> data = val;
pNew -> pNext = pS -> pTop;
pS -> pTop = pNew;
return ;
}
//遍历栈S
void traverse(PSTACK pS)
{
PNODE p = pS -> pTop;
printf("栈内元素为:");
while(p != pS -> pBottom)
{
printf("%d\t", p -> data);
p = p -> pNext;
}
printf("\n");
return ;
}
//判断栈是否为空
int empty(PSTACK pS)
{
if(pS -> pTop == pS -> pBottom)
{
return true;
}
else
return false;
}
//删除栈顶元素并将其值赋给*val
int pop(PSTACK pS , int *val)
{
if(empty(pS))
{
return false;
}
else
{
PNODE r = pS -> pTop;
*val = r -> data;
pS -> pTop = r -> pNext;
free(r);
r = NULL;
}
}
//清空栈S
void clear(PSTACK pS)
{
if(empty(pS))
{
return;
}
else
{
PNODE p = pS -> pTop;
PNODE q = NULL;
while(p != pS -> pBottom)
{
q = p -> pNext;
free(p);
p = q ;
}
pS -> pTop = pS -> pBottom;
return;
}
}
2 队列
队列(Queue)是在一段插入而在另一端删除的线性表,其数据元素满足先进先出(FIFO)的操作。
队列也包括链式和顺序结构,队列长度确定的情况下用顺序结构来表示。
用顺序结构表示队列时,由于插入和删除操作分别在不同端进行,因而可能出现插入端已位于预分配内存的末端,而实际内存中还有较多空间未使用。此时通过增加内存的方式来继续添加数据的方式会造成内存的浪费,故采用循环队列的方式来操作比较好。
循环队列的定义和基本操作如下:
/*
循环队列的基本操作,初始化,入队,遍历,出队等操作
*/
#include <stdio.h>
#include <malloc.h>
#define true 1
#define false 0
typedef struct Queue
{
int *pBase; //定义数组pBase
int front;
int rear;
}QUEUE;
void init(QUEUE *pQ);
int en_queue(QUEUE *pQ , int val);
void traverse_queue(QUEUE *pQ);
int out_queue(QUEUE *pQ , int *pVal);
//循环队列的初始化
void init(QUEUE *pQ)
{
pQ -> pBase = (int *)malloc(sizeof(int)*6); //分配内存,数组长度为6
pQ -> front = 0;
pQ -> rear = 0;
return;
}
//判断循环队列是否为满
int full_queue(QUEUE *pQ)
{
if((pQ -> rear + 1) % 6 == pQ -> front)
{
return true;
}
else
return false;
}
//入队操作
int en_queue(QUEUE *pQ , int val)
{
if(full_queue(pQ))
{
return false;
}
else
{
pQ -> pBase[pQ -> rear] = val;
pQ -> rear = (pQ -> rear + 1) % 6;
return true;
}
}
//遍历循环队列
void traverse_queue(QUEUE *pQ)
{
int i = pQ -> front;
printf("遍历队列:");
while(i != pQ -> rear)
{
printf("%d\t", pQ -> pBase[i]);
i = (i + 1) % 6 ;
}
printf("\n");
return;
}
//判断循环队列是否为空
int empty_queue(QUEUE *pQ)
{
if(pQ -> front == pQ -> rear)
{
return true;
}
else
return false;
}
//循环队列的出队操作
int out_queue(QUEUE *pQ , int *pVal)
{
if(empty_queue(pQ))
{
return false;
}
else
{
*pVal = pQ -> pBase[pQ -> front];
pQ -> front = (pQ -> front + 1) % 6;
return true;
}
}