在数据结构体系中,栈和队列是经常使用的两个数据结构,本文主要阐述如何使用C语言实现链式和顺式的栈和队列
队列
队列的定义
队列是一种只允许在一端进行添加,在另一端删除的一种数据结构。
在整体的数据输入和输出中,一般采用先进先出的思想,例如计算机的应用线程就是采用了队列的实现,先向计算机发出申请的一般而言会优先处理
链式结构
首先在完成队列的链式结构之前,先得搞明白什么叫做链式结构,什么叫做链表,以及链表的头删和尾插的操作是如何进行的
链表的复习
链表的定义和初始化
typedef struct Listnode
{
int data;
struct Listnode* next;
}Listnode;
void ListInit(Listnode* head)
{
head->data = 0;
head->next = NULL;
}
int main()
{
Listnode head;
ListInit(&head);
return 0;
}
这里需要注意的就是,在我们对链表进行修改的时候,函数内部必须传入头结点的地址
链表的尾插函数
void ListPushBack(Listnode* head,int x)
{
Listnode* newnode = CreatNewNode(x);
//寻找尾巴节点
if (head->next == NULL)
{
head->next = newnode;
}
else
{
Listnode* cur = head->next;
while (cur->next)
{
cur = cur->next;
}
cur->next = newnode;
}
}
这里需要注意的就是在我们进行尾插的时候需要先判断链表内的元素是否为空
链表的头删
void ListPopFront(Listnode* head)
{
Listnode* tmp = head->next->next;
free(head->next);
head->next = tmp;
}
这里需要注意的就是,当我们删除第一个元素的时候,为了保持链表的连续性,我们必须先要记下链表的下一个节点
用链表实现队列
链表和队列的初始化以及定义
typedef struct Node
{
int data;
struct Node* next;
}Node;
typedef struct Quene
{
Node* head;
Node* front;
Node* tail;
}Quene;
void QueneInit(Quene* myquene)
{
myquene->head = (Node*)malloc(sizeof(Node));
myquene->head->data = 0;
myquene->head->next = NULL;
myquene->front = myquene->head;
myquene->tail = myquene->head;
}
一般情况下,队列的结构体中会记录队列中的第一个元素和队列中的最后一个元素
队列的插入函数
Node* CreatNewNode(int x)
{
Node* newnode = (Node*)malloc(sizeof(Node));
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void QuenePush(Quene* myquene,int x)
{
Node* newnode = CreatNewNode(x);
Node* cur = myquene->head;
if (cur->next == NULL)
{
myquene->head->next = newnode;
myquene->tail = newnode;
}
else
{
while (cur->next)
{
cur = cur->next;
}
cur->next = newnode;
myquene->tail = newnode;
}
}
队列的删除函数
void QuenePop(Quene* myquene)
{
Node* tmp2 = myquene->head->next;
Node* tmp = myquene->head->next->next;
free(myquene->head->next);
myquene->head->next = tmp;
myquene->front = tmp;
}
由于队列的特殊性,在这几个代码块中,队列的插入函数就和链表的尾插十分的相似,队列的删除函数就和链表的头删十分的相似,并且由于我们每一次都记录下了front和tail节点,所以我们可以在写代码的时候对代码进行一定程度的修改,用front和tail节点来代替链表中的某些节点
以链表的插入函数作为例子:
void QuenePop(Quene* myquene)
{
Node* tmp2 = myquene->head->next;
Node* tmp = myquene->head->next->next;
free(myquene->head->next);
myquene->head->next = tmp;
myquene->front = tmp;
}
用顺序表实现栈
复习顺序表
和队列的链表实现形式相似的,我们只需要复习顺序表的尾插和头删即可完成顺序表实现栈
顺序表的初始化
typedef struct SeqList
{
int* a;
int max;//最大容量
int eff;//有效容量
}SeqList;
void ListInit(SeqList* ps)
{
ps->a = (int*)malloc(sizeof(int) * 4);
ps->eff = 0;
ps->max = 4;
}
顺序表的尾插函数
void ListPushBack(SeqList* ps,int x)
{
if (ps->eff == ps->max)
{
int* tmp = (int*)realloc(ps->a, sizeof(int) * 4);
ps->max = ps->max + 4;
ps->a = tmp;
}
ps->a[ps->eff] = x;
ps->eff++;
}
顺序表的头删函数
void ListPopFront(SeqList* ps)
{
int begin = 1;
while (begin < ps->eff)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->eff--;
}
这里需要注意的是顺序表的头删函数需要对整个顺序表进行一个挪动,具体的可以看到顺序表专门的博客
https://blog.csdn.net/EchoToMe/article/details/129541102?spm=1001.2014.3001.5501
队列的顺序表实现
队列的顺序表初始化
typedef struct Quene
{
int* a;
int max;
int eff;
}Quene;
void QueneInit(Quene* ps)
{
ps->a = (int*)malloc(sizeof(int) * 4);
ps->eff = 0;
ps->max = 4;
}
队列的入队操作
void QuenePush(Quene* ps,int x)
{
if (ps->eff == ps->max)
{
int* tmp = (int*)realloc(ps->a, sizeof(int) * 4);
ps->a = tmp;
ps->max += 4;
}
ps->a[ps->eff] = x;
ps->eff++;
}
队列的出队操作
void QuenePop(Quene* ps)
{
int begin = 1;
while (begin < ps->eff)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->eff--;
}
由于队列的顺序表实现方式和普通的顺序表的实现差距并不大,所以在这里不过多的对顺序表结构进行讲解,如果有问题的话可以观看之前的顺序表专门的博客
栈
栈的定义
栈是一种顺序/链式的数据结构,其满足的条件为后进先出
同样的,栈分为链表实现和顺序表实现,我们依旧先进行顺序表实现再进行链表实现的顺序
无论是栈的顺序表还是链表的实现方式,我们都需要对这两个结构的尾删和尾插有一定的了解
由于上面文章已经回顾了链表和顺序表的基本概念,所以这里不再赘述
在进行栈的实现之前,我们采用和队列不同的结构进行实现栈,我们一般常用的两种结构是逻辑结构和存储结构
逻辑结构和存储结构
逻辑结构:在数据结构这一门课里面,逻辑结构通常指的是数据结点之间的关系
存储结构:存储结构又叫做物理结构,其中主要部分是用来存储数据的,存储结构分为:顺序存储结构、链式存储结构、索引结构等等
栈的顺序表实现
栈的逻辑存储结构的定义和初始化
typedef struct StackNode
{
int* a;
int max;
int eff;
}SNode;//存储结构
typedef struct Stack
{
SNode stack;
int top;
int bottom;
}Stack;//逻辑结构
void StackInit(Stack* ps)
{
ps->stack.a = (int*)malloc(sizeof(int) * 4);
ps->stack.eff = 0;
ps->stack.max = 4;
ps->top = 0;
ps->bottom = 0;
}
结合上文所描述的,这个代码块有两个结构体,第一个结构体是代码的存储结构,第二个结构体是代码的逻辑结构
入栈操作
void StackPush(Stack* ps,int x)
{
if (ps->stack.eff == ps->stack.max)
{
int* tmp = (int*)realloc(ps->stack.a, sizeof(int) * 4);
ps->stack.a = tmp;
ps->stack.max = ps->stack.max + 4;
}
ps->stack.a[ps->top] = x;
ps->top++;
}
出栈操作
void StackPop(Stack* ps)
{
int begin = 1;
while (begin < ps->top)
{
ps->stack.a[begin - 1] = ps->stack.a[begin];
begin++;
}
ps->top--;
}
栈的链表实现
栈的存储结构和逻辑结构的定义和初始化
typedef struct SNode
{
int data;
struct SNode* next;
}Snode;
typedef struct stack
{
Snode* stack;
int top;
int bottom;
}Stack;
void StackInit(Stack* ps)
{
ps->stack = (Snode*)malloc(sizeof(Snode));
if (ps->stack == NULL)
{
printf("malloc fail\n");
exit(-1);
}
ps->stack->data = 0;
ps->stack->next = NULL;
ps->top = 0;
ps->bottom = 0;
}
出栈操作
void StackPop(Stack* ps)
{
Snode* cur = ps->stack;
Snode* before = ps->stack;
while (cur->next)
{
before = cur;
cur = cur->next;
}
before->next = NULL;
free(cur);
}
入栈操作
Snode* CreatNewNode(int x)
{
Snode* newnode = (Snode*)malloc(sizeof(Snode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void StackPush(Stack* ps, int x)
{
Snode* newnode = CreatNewNode(x);
if (ps->stack->next == NULL)
{
ps->stack->next = newnode;
}
else
{
Snode* cur = ps->stack;
while (cur->next)
{
cur = cur->next;
}
cur->next = newnode;
}
ps->top++;
}