目录
1.栈
一种特殊的线性表,只允许在一端进行插入和删除操作,进行插入删除的一端称作栈顶,另一端称作栈底。
既然是线性表,那么可以使用链表和数组来实现栈;
若使用数组,那么数组靠后的部分做栈顶,如此一来每次插入数组每次插入数据不需要挪动元素。
若使用单链表,那么单链表靠头的部分作为栈顶比较合适,当删除数据的时候只需要从头开始删除就可以了。
如果用双向链表,那么哪一个部分做栈顶都无所谓,因为每一个节点都可以找到prev。
这里建议用数组实现,因为最简单。
2. 栈的实现
这里选择使用数组来实现,栈顶下标默认是0,每次增加一个元素就+1,此时栈顶下标也是栈元素的个数。
①入栈:首先判断传入指针是否为空,然后如果capacity和栈元素个数top相同,那么就扩容;不需要扩容那就将top下标位置的元素赋值为相应元素。
②出栈:若top为0,说明栈内没有元素,若有元素直接将top--即可。
③得到栈大小:返回top即可。
④栈是否为空:若top为0就是空返回1,否则返回0。
⑤获取栈顶数据:直接返回top-1下标的数据即可。
#define _CRT_SECURE_NO_WARNINGS
#include"stack.h"
// 初始化
void InitStack(Stack* stk)
{
assert(stk);
stk->_a = malloc(sizeof(dataType) * 4);
stk->_capacity = 4;
stk->_top = 0;// 栈顶元素下标为0
}
// 销毁
void DestroyStack(Stack* stk)
{
assert(stk);
free(stk->_a);
stk->_a = NULL;
stk->_capacity = stk->_top = 0;
}
// 入栈
void PushStack(Stack* stk, dataType x)
{
assert(stk);
if (stk->_capacity == stk->_top) // 容量和栈元素个数相等,就扩容
{
stk->_capacity *= 2;
dataType* tmp = (dataType*)realloc(stk->_a, sizeof(dataType) * stk->_capacity);
if (tmp)
{
printf("内存不足!");
exit(-1);
}
else
{
stk->_a = tmp;
}
}
// 正常插入
stk->_a[stk->_top] = x;
stk->_top++;
}
// 出栈
void PopStack(Stack* stk)
{
assert(stk);
assert(stk->_top > 0);// 有元素才能删除
stk->_top--;
}
// 栈的元素个数
int SizeStack(Stack* stk)
{
assert(stk);
return stk->_top;
}
// 栈是否为空
void isEmpty(Stack* stk)
{
assert(stk);
return stk->_top == 0 ? 1 : 0; // 没有元素就是空
}
// 获取栈顶数据
dataType getTop(Stack* stk)
{
assert(stk);
if (stk->_top > 0)
{
return stk->_a[stk->_top - 1];
}
else
{
return NULL;
}
}
3.队列
进行插入操作的一端称为队尾,进行删除操作一端称作队头。
当然队列可以用数组和链表来实现,如果使用数组,可以有两种方法实现:
①每次插入数据的时候,依次插入即可,出队的时候需要移动后面的数据将前面的数据进行覆盖从而达到出队的效果;
②或者直接指定一个下标,每次出队,将下标后移,但是这样的话,每次出队都会浪费一个空间。
所以数组实现队列是一个不好的选择。
这里使用单链表进行实现,我们可以维护两个指针,一个指向队头,一个指向队尾,当入队的时候可以使用队尾指针,当出队的时候,可以使用队头指针。
所以这里推荐使用单链表+两个指针就能实现队列。
4.队列的实现
我们使用两个结构体来完成队列,第一个结构体代表队列的节点,包含数据和next指针;
第二个结构体代表整个队列,包含头指针和尾指针;
①初始化队列:将头尾指针置空。
②销毁队列:从头指针开始当当前指针不为空就直接销毁,最后将头尾指针置空。
③队列入队:如果此时队列为空(head == NULL),将新节点作为头尾指针;如果队列不为空,只需要旧的尾结点的next指向新节点,同时新节点为tail。
④队列出队:将头指针的next保存下来,将头指针释放,更新头指针,若在更新的过程中将最后一个节点删除,即head为NULL,此时需要将tail也置为空,为了保持程序的合理运行,在程序开始之前需要判断head不为空。
⑤获取队头元素:当队头不为空,返回队头的值,队尾同理不再赘述。
⑥判空:其实就是判断头指针是否为空,为空返回1,不为空返回0。
⑦求队列长度:用一个指针遍历即可。
#define _CRT_SECURE_NO_WARNINGS
#include"queue.h"
void QueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
}
void QueDestroy(Queue* pq)
{
assert(pq);
QueueNode* curr = pq->head;
while (curr)
{
QueueNode* next = curr->next;
free(curr);
curr = next;
}
pq->head = pq->tail = NULL;
}
void QuePush(Queue* pq, dataType x)
{
assert(pq);
QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
newNode->data = x;
newNode->next = NULL;
if (pq->head == NULL) // 如果队列为空,插入一个节点
{
pq->head = pq->tail = newNode;
}
else
{
pq->tail->next = newNode; // 队列不为空,tail插入节点
pq->tail = newNode;
}
}
void QuePop(Queue* pq)
{
assert(pq);
assert(pq->head);
QueueNode* next = pq->head->next;
free(pq->head);
pq->head = next;
if (pq->head == NULL)
{
// tail head == NULL,删除的过程中,删除到最后一个的时候需要将tail置空
pq->tail = NULL;
}
}
dataType QueFront(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->head->data;
}
dataType QueBack(Queue* pq)
{
assert(pq);
assert(pq->tail);
return pq->tail->data;
}
int isEmpty(Queue* pq)
{
assert(pq);
if (pq->head == NULL)
{
return 1;
}else
{
return 0;
}
}
int QueSize(Queue* pq)
{
assert(pq);
QueueNode* curr = pq->head;
int size = 0;
while (curr)
{
curr = curr->next;
size++;
}
return size;
}
下一期内容,将栈和队列的OJ题进行详解。