栈
栈是一种特殊的线性数据结构,只能在一端进行数据的插入和删除操作。数据插入和删除的一端称为栈顶,另一端称为栈底。数据插入的操作称为入栈,数据删除的操作称为出栈。栈中的元素有后进先出的特性。
顺序栈的实现
定义一个栈的结构,声明数据的类型,栈结构中定义存储元素的数组。栈的初始容量,当前栈中元素的个数。
typedef int Datatype;
typedef struct Stack {
Datatype* array;//数组
int capacity;//容量
int Size;//栈中的元素个数
}Stack;
栈初始化
对定义好的栈结构进行初始化,首先要给它开辟连续空间,让栈中字段附上初始值。
void StackInit(Stack* ps) {
//初始化
assert(ps);
ps->array = (Datatype*)malloc(sizeof(Datatype) * 3);
if (NULL == ps->array) {
assert(0);
return;
}
ps->capacity = 3;
ps->Size = 0;
}
判断栈是否为空,
int StackEmpty(Stack* ps) {
assert(ps);
return 0 == ps->Size;
}
元素的插入
如果栈中当前容量足够,将待插入的元素放到栈中存储元素数组的下标为Size的位置。然后给Size的值加1。如果容量不够需要进行扩容。
void StackPush(Stack* ps, Datatype data) {
assert(ps);
Checkcapacity(ps);
ps->array[ps->Size++] = data;
}
扩容
如果栈中元素个数与栈的容量相等了,说明没有空间了,用realloc将数组容量从原有值扩大2倍。
void Checkcapacity(Stack* ps) {
assert(ps);
if (ps->Size == ps->capacity) {
ps->array = (Datatype*)realloc(ps->array, sizeof(Datatype) * ps->capacity * 2);
if (ps->array == NULL) {
assert(0);
return;
}
ps->capacity *= 2;
}
}
元素出栈
如果栈不为空,将栈中表示元素个数的size值减1,访问不到了
void StackPop(Stack* ps) {
assert(ps);
if (StackEmpty(ps)) {
return;
}
ps->Size--;
}
获取栈顶元素
栈的数据插入和删除只能在栈顶进行,直接访问数组下标为size-1的元素,将它作为返回值取到。
Datatype StackTop(Stack* ps) {
assert(ps && !StackEmpty(ps));
return ps->array[ps->Size - 1];
}
栈的销毁
直接将保存元素的数组array释放掉。
void StackDestroy(Stack* ps) {
assert(ps);
if (ps->array) {
free(ps->array);
ps->array = NULL;
ps->capacity = 0;
ps->Size = 0;
}
}
栈后进先出的特性可以用来改变元素序列,比如链表逆置,括号匹配问题逆波兰表达式求值等问题。函数递归调用也是层层压栈的过程,可以用栈将递归转化为循环。
队列
同时一种特殊的线性数据结构,队列只能在一端进行数据的插入,在另一端进行数据删除,插入数据的一端称为队尾,删除数据的一端称为队头。队列中的元素具有先进先出的特性。
带头单链表实现队列
定义队列的结构,队列结构中有两个字段,一个指针指向队头,一个指向队尾
typedef int Datatype;
//队列中的一个节点
typedef struct QNode {
Datatype data;
struct QNode* next;
}QNode;
//队列的结构
typedef struct Queue {
struct QNode* front;
struct QNode* Tail;
}Queue;
队列初始化
往队列中中种一个头结点,头结点不作为数据计入队列,队头和队尾都指向它。
void QueueInit(Queue* pq){
pq->front = pq->Tail = BuyQueueNode(0) ;
}
QNode* BuyQueueNode(Datatype data) {
//开辟一块空间,存着一个链表节点的两个字段,
QNode* node = (QNode*)malloc(sizeof(QNode));
if (NULL == node) {
assert(0);
return NULL;
}
node->data = data;
node->next = NULL;
return node;
}
数据插入 入队列
插入一个元素是从队尾进行的,类似于单链表的尾插,那就让单链表中最后一个节点的next指针指向它,队列尾指针指向的就是最后一个节点。然后改变队尾只针的指向,指向新插入的节点,就是先满足链表的结构,再满足队列的结构。
void QueuePush(Queue* pq, Datatype data) {
assert(pq);
pq->Tail->next = BuyQueueNode(data);
pq->Tail = pq->Tail->next;
}
数据的删除 出队列
定义一个指针,将队列中第一个元素的节点保存起来,因为等会它要被释放掉,释放掉之后就破坏了链表的结构,找不到下一个元素了,第一个元素的节点就是头结点的下一个节点。然后改变队头的指向,再将要删除的节点释放掉。
void QueuePop(Queue* pq) {
//删除操作判空
//定义一个链表节点类型的指针
QNode* delNode = NULL;
if (QueueEmpty(pq)) {
return;
}
delNode = pq->front->next;
pq->front->next = delNode->next;
//如果链表中只有一个元素。
if (delNode == NULL) {
pq->Tail = pq->front;
}
free(delNode);
}
获取队头队尾的元素
Datatype Queuefront(Queue* pq) {
assert(pq);
assert(!QueueEmpty(pq));
return pq->front->next->data;
}
Datatype QueueTail(Queue* pq) {
assert(pq);
assert(!QueueEmpty(pq));
return pq->Tail->data;
}
获取队列中元素总个数
int QueueSize(Queue* pq) {
assert(pq);
int count = 0;
QNode* cur = pq->front->next;
while (cur) {
count++;
cur = cur->next;
}
return count;
}
判断队列是否为空
int QueueEmpty(Queue* pq) {
assert(pq);
return NULL == pq->front->next;
}
队列销毁
从头节点开始,找到一个节点释放一个,释放之前要先改变头节点的指向
void QueueDestroy(Queue* pq) {
assert(pq);
QNode* delNode = NULL;
delNode = pq->front;
while (delNode) {
pq->front = delNode->next;
free(delNode);
delNode = pq->front;
}
pq->front = NULL;
pq->Tail = NULL;
}