目录
习题1 用队列实现栈
1.把有数据队列的内容,传到没数据的队列中,传一个之后,再原队列中删一个
2.等q1中剩一个元素的时候,让这个元素出队
这样就可以达到栈的效果,1,2,3,4,5,让5先出栈,之后再按照同样的方法把q1看作空(压栈的时候要往有元素的队里压),开始倒数据即可
队列的实现
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode *head;
QNode* tail;
size_t size;
}Queue;
void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);
void QueuePush(Queue *pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
del = NULL;
}
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)
{
QNode* newnode = (QNode*)malloc(sizeof(QNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
if (pq->tail == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = pq->tail->next;
}
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}//如果只剩一个节点,单独处理,防止tail变为野指针
else
{
QNode* cur = pq->head;
pq->head = pq->head->next;
free(cur);
cur = NULL;
}
pq->size--;
}
QDataType QueueFront(Queue* pq)//取头数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
QDataType QueueBack(Queue* pq)//取尾数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head==NULL && pq->tail==NULL;
}
int QueueSize(Queue* pq)
{
return pq->size;
}
通过队列实现栈
结构体的建立
typedef struct {
Queue q1; //建立俩个
Queue q2;
} MyStack;
//也可用Queue*,但是这样的话要用malloc给q1和q2开辟空间
初始化
MyStack* myStackCreate() {
MyStack*obj=(MyStack*)malloc(sizeof(MyStack));
QueueInit(&obj->q1);
QueueInit(&obj->q2);
return obj;
}//直接传参即可
压栈
void myStackPush(MyStack* obj, int x) {
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}//如果q1不是空,把元素压到q1
else
{
QueuePush(&obj->q2,x);
}
}
出栈
int myStackPop(MyStack* obj) {
Queue *empty=&obj->q1;//假设q1为空
Queue *noempty=&obj->q2;//假设q2不是空
if(!QueueEmpty(&obj->q1))
{
noempty=&obj->q1;
empty=&obj->q2;
while(QueueSize(noempty)>1) //将有元素的队列的数据,倒到只剩最后一个元素
{
QueuePush(empty,QueueFront(noempty));
QueuePop(noempty);
}
int top=QueueFront(noempty);//该元素就是栈顶
QueuePop(noempty);
return top;//返回栈顶
}
返回栈顶
int myStackTop(MyStack* obj) {
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}//直接调用返回队尾的函数即可
判断是否为空
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
销毁空间
void myStackFree(MyStack* obj) { QueueDestory(&obj->q1); QueueDestory(&obj->q2); free(obj); }
这里不能直接free(obj),因为q1和q2是链表,链表的内存不连续,如果释放掉boj,则q1和q2没有释放,会造成内存泄漏
用队列实现栈
若队列中是1 2 3 4 5,则按照栈的规则,此时出栈顺序应该是 5 4 3 2 1,我们可以用俩个队列来实现栈
1.把PushST里面的数据倒到PopST
2.出栈的时候,我们只需要出PopST里面的元素就可以,当有元素要填充时,填充到PushST就行
栈的实现
typedef int STDdtaTtype;
typedef struct Stcak
{
STDdtaTtype* data;
int top;
int capacity;
}ST;
void StackInit(ST* ps);
void StackDestory(ST* ps);
void StackPush(ST* ps, STDdtaTtype x);
void StackPop(ST* ps);
bool StackEmpty(ST* ps);
int StackSize(ST* ps);
STDdtaTtype StackTop(ST* ps);
void StackInit(ST* ps)
{
assert(ps);
ps->data = NULL;
ps->capacity = ps->top = 0;
}
void StackDestory(ST* ps)
{
assert(ps);
free(ps->data);
ps->capacity = ps->top = 0;
ps->data = NULL;
}
void StackPush(ST* ps, STDdtaTtype x)//注意要看top的位置是0还是-1
{
assert(ps);
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
if (ps->capacity == ps->top)//判断是否满了
{
STDdtaTtype* tmp = realloc(ps->data, sizeof(ps) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->data = tmp;
ps->capacity = newcapacity;
}
ps->data[ps->top] = x;
ps->top++;
}
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
--ps->top;
}
STDdtaTtype StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->data[ps->top - 1];
}
bool StackEmpty(ST* ps)//判断是否为空栈
{
assert(ps);
return ps->top == 0;
}
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
结构体创建
typedef struct {
ST PushST;
ST PopST;
} MyQueue;
结构体的初始化
MyQueue* myQueueCreate() {
MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
StackInit(&obj->PushST);
StackInit(&obj->PopST);
return obj;
}
元素入队
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->PushST,x);
}
将Push元素倒到Pop中
void PushSTTOPopsT(MyQueue* obj)
{
if(StackEmpty(&obj->PopST))//如果Pop栈内为空,并且Push不为空就给元素
{
while(!StackEmpty(&obj->PushST))//当Push不为空
{
StackPush(&obj->PopST,StackTop(&obj->PushST));
StackPop(&obj->PushST);
}
}
}
返回头元素然后移除
int myQueuePop(MyQueue* obj) {
PushSTTOPopsT(obj);
int front=StackTop(&obj->PopST);
StackPop(&obj->PopST);
return front;
}
返回头元素
int myQueuePeek(MyQueue* obj) {
PushSTTOPopsT(obj);
return StackTop(&obj->PopST);
}
判断是否为空
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->PopST)&&StackEmpty(&obj->PushST);
}
空间的销毁
void myQueueFree(MyQueue* obj) {
StackDestory(&obj->PushST);
StackDestory(&obj->PopST);
free(obj);
}
设计循环队列
这里采用数组实现(链表有缺点后面会讲到),front指向首元素,back指向最后元素的下一个空间,空间大小为4,多开辟一个空间,即总共开辟五个空间,使得back+1的位置是front
结构体的定义
typedef struct {
int *a;
int front;
int back;
int N; //用来统计空间的总个数
} MyCircularQueue;
初始化
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a=(int *)malloc(sizeof(int)*(k+1)); //多开辟一个空间
obj->back=obj->front=0;
obj->N=k+1;
return obj;
}
判断是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->back==obj->front;
}//当back==front时,就是空
判断是否满了
元素满了之后back指向红框,back+1,back指向数组外,此时让back%N,back就会回到首元素位置
bool myCircularQueueIsFull(MyCircularQueue* obj) { return (obj->back+1)%obj->N==obj->front; }//当back的下一个位置是front时候是满
在队列内插入元素
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) { if (myCircularQueueIsFull(obj)) return false; //如果满了则退出 obj->a[obj->back]=value; obj->back=(obj->back+1)%obj->N; //插入之后,back要走到下一个位置 return true; }
删除队列内元素
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
obj->front++;
obj->front=obj->front%obj->N; //跟back一样,要防止越界
return true;
}
返回队首元素
int myCircularQueueFront(MyCircularQueue* obj) {
if( myCircularQueueIsEmpty(obj))
return -1;
else
return obj->a[obj->front];
} //如果不为空,返回队首元素
获得队尾元素
int myCircularQueueRear(MyCircularQueue* obj) { if(myCircularQueueIsEmpty(obj)) return -1; else return obj->a[(obj->back-1+obj->N)%obj->N]; }
back的上一个位置就是队尾元素,队back-1即可,但是存在一个问题
当back位于该位置时,back-1就是-1,此时我们可以(back-1+N)%N,使back回到最后一个位置,不用链表实现该题是因为,当获取back所在元素时,链表需要遍历,而数组可以直接访问
小练习题:
5.现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N-1。其队内有效长度为?(假设 队头不存放数据)
A (rear - front + N) % N + 1
B (rear - front + N) % N
C ear - front) % (N + 1)
D (rear - front + N) % (N - 1)
选B,
空间释放
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a); //先释放数组空间,再释放给结构体创建的空间
free(obj);
}