一、栈
1.栈的定义
栈是一种限定性线性表,它的插入和删除运算限制为仅在表的一端进行。通常将表中允许进行插入、删除操作的一端称为栈顶,表的另一端称为栈底。当栈中没有元素时称为空栈。栈的插入操作被形象地称为入栈或进栈,删除操作称为出栈或退栈。
2.顺序栈
顺序栈是指利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。顺序栈的结构体定义如下:
// 顺序栈的描述
typedef char ElementType;
typedef struct
{
ElementType *array; // 存放元素的数组
int top; // 栈的栈顶下标
int capacity; // 容量
}SeqStack;
3.主要操作的实现
// 1.一般顺序栈的创建
SeqStack* createStack(int capacity)
{
SeqStack *S;
S = (SeqStack*)malloc(sizeof(SeqStack));
S->top = -1;
S->capacity = capacity;
S->array = (ElementType*)malloc(capacity*sizeof(ElementType));
return S;
}
// 2.判断栈是否空
int empty(SeqStack *S)
{
if (S->top == -1)
return 1;
else
return 0;
}
// 3.判断栈是否满
int full(SeqStack *S)
{
if (S->top >= S->capacity - 1)
return 1;
else
return 0;
}
// 4.进栈
int push(SeqStack *S, ElementType x)
{
if (full(S))
return 0; // 栈满
else
{
S->top++;
S->array[S->top] = x;
return 1;
}
}
// 5.出栈
int pop(SeqStack *S, ElementType *x)
{
if (empty(S))
return 0; // 栈空
else
{
*x = S->array[S->top];
S->top--;
return 1;
}
}
二、队列
1.队列的定义
队列是另一种限定性的线性表,它只允许在表的一端插入元素,而在另一端删除元素。允许删除的一端称为队头,允许插入的一端称为队尾。
2.循环队列和链队列
循环队列的结构体定义如下:
typedef int ElementType;
typedef struct
{
ElementType *array;
int front;
int rear;
int capacity; // 队列总容量
}SeqQueue;
链队列的结构体定义如下:
typedef char ElementType;
typedef struct Node
{
ElementType data;
struct Node *next;
}Qnode;
typedef struct // 链队列结构
{
Qnode *front;
Qnode *rear;
}LinkQueue;
3.主要操作的实现
(1)循环队列
// 循环队列的创建
SeqQueue *createQueue(int capacity)
{
SeqQueue *q;
q=(SeqQueue *)malloc(sizeof(SeqQueue));
q->front=q->rear=0;
q->capacity=capacity;
q->array=(ElementType *)malloc(sizeof(ElementType)*capacity);
return q;
}
// 入队
int push(SeqQueue *q,ElementType x)
{
if( (q->rear+1) % q->capacity == q->front) // 队列已满
return 0;
q->array[q->rear]=x;
q->rear=(q->rear+1) % q->capacity;
return 1;
}
// 出队
int pop(SeqQueue *q,ElementType *x)
{
if(q->rear==q->front) // 队列为空
return 0;
*x=q->array[q->front];
q->front=(q->front+1) % q->capacity;
return 1;
}
(2)链队列
// 创建一个带头结点的空的链队列
LinkQueue *createQueue()
{
LinkQueue *q;
q = (LinkQueue*)malloc(sizeof(LinkQueue));
q->front = (Qnode*)malloc(sizeof(Qnode));
q->rear = q->front;
q->front->next = NULL;
return q;
}
// 入队
int push(LinkQueue *q, ElementType x)
{
Qnode *s;
s = (Qnode*)malloc(sizeof(Qnode));
if (s == NULL)
return 0; // 开辟空间失败
s->data = x;
s->next = NULL;
// 元素从队尾入队
q->rear->next = s;
q->rear = s;
return 1;
}
// 出队
int pop(LinkQueue *q, ElementType *x)
{
Qnode *temp;
if (q->front == q->rear)
return 0;
// 元素从队头出队
temp = q->front->next;
*x = temp->data;
q->front->next = temp->next;
if (q->rear == temp) // 将要删除的结点是尾结点
q->rear = q->front;
free(temp);
return 1;
}
三、应用
1.利用栈具有“先进后出”的特点,实现数制转换的功能。
// 二进制转换成十进制
int main()
{
ElementType c;
SeqStack *s;
int i, j, N, sum = 0;
s = createStack(size);
printf("请输入数组的容量,最大不能超过100:\n");
scanf_s("%d", &N);
getchar();
printf("请输入二进制数:\n");
for (i = 0;i<N;i++)
{
scanf_s("%c", &c);
push(s, c);
}
printf("栈的当前容量是:%d\n", N);
for (j = 0;j<N;j++)
{
pop(s, &c);
sum = sum + (c - 48)*pow(2, j);
}
printf("转换后的十进制数为:%d\n", sum);
return 0;
}
2.如果希望循环队列中的元素都能得到利用,则需设置一个标志域tag,并以tag的值为0或1来区分尾指针和头指针相同时的队列状态是“空”还是“满”。试编写与此结构相应的入队算法和出队算法。
// 入队
int push(SeqQueue *q, ElementType x)
{
int tag = 0; // 队列非满时为0
if (!tag)
{
q->array[q->rear] = x;
q->rear = (q->rear + 1) % q->capacity;
if (q->rear == q->front)
{
tag = 1; // 队列满时为1
}
}
return tag;
}
// 出队
int pop(SeqQueue *q, ElementType *x)
{
int tag = 1; // 队列非空时为1
if (tag)
{
*x = q->array[q->front];
q->front = (q->front + 1) % q->capacity;
if (q->front == q->rear)
{
tag = 0; // 队列空时为0
}
}
return tag;
}
主函数
int main()
{
int b, i, N,tag;
SeqQueue *p;
printf("请输入数组的大小:\n");
scanf_s("%d", &N);
printf("\n");
p = createQueue(N);
printf("请输入数字:\n");
for (i = 0;i<N;i++)
{
scanf_s("%d", &b);
tag=push(p, b);
}
if (tag == 1)
printf("入队的队列已满\n");
while (tag)
{
tag=pop(p, &b);
printf("%d ", b);
}
printf("\n");
if (tag == 0)
printf("出队的队列已空\n");
return 0;
}