目录
前言
今天我们来修炼内功,一个厉害的武学宗师一点是内外兼修的。
栈和队列本身在数据结构中作用不是很大,但是它常常在更为复杂的数据结构中作为分支使用。所以懂得栈和队列是很重要的,它是在为后面更为复杂的数据结构打基础的。
提示:以下是本篇文章正文内容,下面案例可供参考
一、栈
1.1:栈的基本概念
栈是常见的数据结构,只允许在固定的一端进行插入和删除元素操作。这一端为栈顶,另一端为栈底,插入数据和删除数据的操作。遵循后进先出原则。插入操作被称为入栈(压栈),入数据在栈顶,删除数据叫做出栈出数据也在栈顶。
入栈顺序是 1,2,3,4 出栈顺序可以为4,3,2,1或先进4出4,进3出3,进2出2,进1出1。它也可以进4,3出4,在进2出2,再出3,进1出1。我们可以将最后一个进来的数据当作栈顶,当栈顶的数据出去以后,原先栈顶的原始就变成了栈顶数据,它就可以出去了。
1.2:栈的格式
栈可以用顺序表储存,链表储存,我们这里用的是动态顺序表储存。数组储存是静态顺序表的栈,而动态顺序表是动态的栈 。
typedef int sta_type;
typedef struct stack
{
sta_type* a; //数组
int top; //数组下标
int capacity; //数组容量大小
}st;
1.3:栈的实现
栈很简单的,无非就一个增加数据和删除数据 ,以及对栈的初始化,和顺序表意义就是扩展几个空间。其实现方法和顺序表一样,懂了顺序表就可以很容易的看懂。
动态顺序表的本质就是模仿实现数组
void st_init(st* p) //初始化
{
assert(p); //结构体首先不能为空
p->a = malloc(sizeof(sta_type) * 4); //给它四个类型的空间
if (p->a == NULL)
{
perror("malloc fail");
return;
}
p->capacity = 4; //数组(指针)容量的大小
p->top = 0; //栈顶元素下一个位置的元素 ,即下标
// p->top = -1; //这里表示栈顶元素
// 这是因为当他放元素的时候top的元素是放在下一个地方
}
void st_push(st* p, sta_type x) //插入
{
assert(p);
if (p->top == p->capacity)
{
sta_type* tmp = (sta_type*)realloc(p->a, sizeof(sta_type)* p->capacity*2); // 扩容到当前数组的二倍
if (tmp == NULL)
{
perror("realloc fail");
return;
}
p->a = tmp; //根据realloc的特性,直接将地址赋予p其数组也被保存
p->capacity *= 2;
}
p->a[p->top] = x;
p->top++;
}
void st_pop(st* p) //删除
{
assert(p);
assert(! st_empty(p));
p->top--;
}
int st_size(st* p) //
{
assert(p);
return p->top;
}
bool st_empty(st* p)
{
assert(p);
return p->top == 0; //当top是0为空
}
void st_destroy(st* p) //释放内存
{
assert(p);
free(p->a);
p->a = NULL;
p->capacity = 0;
p->top = 0;
}
sta_type st_top(st* p) //访问栈顶元素
{
assert(p);
assert(!st_empty(p));
return p->a[p->top - 1]; // 这里是因为前面top为0表示栈顶元素的下一个,所以要减1
}
二、队列
1.1:队列的基本概念
队列是只允许在一端进行插入数据,在另一端删除数据的特殊性表,队列的特性是先进先出(FIFO)。插入数据一端叫做队尾出数据的一端叫队头。
1.1.1:队列的基本结构
对于队列我们用单链表表示。并且用尾插进行实现。
typedef int q_type;
typedef struct queue_node // 链表结构
{
struct queue_node* next;
q_type data;
}q_node;
typedef struct queue //队列结构
{
q_node* head; //队头
q_node* tail; //队尾
int size; //数据数量
}queue;
2.1:队列的实现
只要懂得单链表的尾插和头删,下面的代码就很容易实现。首先根据上面的结构对队列进行初始化,初始化没有数据头和尾都为空。
入队列就是尾插,不过有两种情况,队列没有数据的时候,和队列有数据的时候。没有数据在第一个数据插入的时候头尾都是一个数据。其余不变。
出队列就是头删,也就考虑两种情况,一种是只有一个数据,在只有一个数据的时候释放这个数据,将头尾都指向NULL即可。另种是多个元素正常头删即可。
void QueueInit(queue* p)// 初始化队列
{
assert(p);
p->head = p->tail = NULL;
p->size = 0;
}
void QueuePush(queue* p, q_type x)// 队尾入队列
{
// 这里用的尾插
assert(p);
q_node* new = (q_node*) malloc(sizeof(q_node));
if(new == NULL)
{
perror("malloc fail");
return;
}
new->data = x;
new->next = NULL;
if (p->head == NULL)
{
p->head = p->tail = new;
}
else
{
p->tail->next = new;
p->tail = new;
}
p->size++;
}
void QueuePop(queue* p)// 队头出队列
{
assert(p);
assert(p->head != NULL);
q_node* del = p->head;
if (del->next == NULL)
{
free(p->head);
p->head = p->tail = NULL;
}
else
{
p->head = del->next;
free(del);
del->next = NULL;
}
p->size--;
}
bool QueueEmpty(queue* p)// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
{
assert(p);
return p->head = p->tail = NULL;
}
q_type QueueFront(queue* p)// 获取队列头部元素
{
assert(p);
assert(!QueueEmpty(p));
return p->head->data;
}
q_type QueueBack(queue* p)// 获取队列队尾元素
{
assert(p);
assert(!QueueEmpty);
return p->tail->data;
}
void QueueSize(queue* p)// 获取队列中有效元素个数
{
assert(p);
return p->size;
}
void QueueDestroy(queue* p)// 销毁队列
{
assert(p);
q_node* cur = p->head;
while (cur)
{
q_node* next = cur->next;
free(cur);
cur = next;
}
p->head = p->tail = NULL;
p->size = 0;
}