数据结构之栈和队列

数据结构之栈和队列,第1张

文章目录
  • 1.1 栈的结构及概念
  • 1.2 栈的实现
  • 1.3 栈的完整代码
  • 1.4 OJ题:"有效括号"
  • 2.1 队列的结构及概念
  • 2.2 队列的实现
  • 2.3 队列完整代码
  • 3.1 队列和栈相关OJ题

1.1 栈的结构及概念
  1. 栈:一种特殊的线性表。


    只允许在固定的一端进行插入和删除元素的 *** 作。


    插入和删除的一端叫做栈顶,LIFO(Last In First Out)的原则。


    栈中的数据遵循后进先后LIFO(Last In First Out)的原则。


  2. 压栈:栈的插入 *** 作叫做压栈/入栈/进栈,入数据在栈顶。


  3. 出栈:栈的删除 *** 作叫做出栈,出数据也在栈顶。


  1. 数据结构中的栈与 *** 作系统中虚拟进程的栈是不同的,一个是数据结构的概念。


    一个是 *** 作系统的内存划分区域,用来函数调用时,建立栈帧!!!

1.2 栈的实现
  1. 栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。


    因为数组在尾上插入数据的
    代价比较小0(1),而链表尾插需要遍历找尾链接,时间复杂度为0(n)。



头文件->Stack.h

// 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
//typedef int STDataType;
//#define N 10
//typedef struct Stack
//{
//STDataType _a[N];
//int _top; // 栈顶
//}Stack;

#ifndef STACK_H_
#define STACK_H_

#include 
#include 
#include 
#include 

typedef int STDataType;

typedef struct Stack
{
    STDataType *p;
    int top;      //栈顶
    int Capacity; //容量
} ST;

void StackInit(ST *st);               //初始化栈
void StackDestory(ST *st);            //销毁栈
void StackPush(ST *st, STDataType x); //压栈
void StackPop(ST *st);                //出栈
bool StackEmpty(ST *st);              //判断栈是否为空
int StackSize(ST *st);                //栈中变量的数量
STDataType StackTop(ST *st);          //栈顶的值

#endif

第一个接口:StackInit->初始化

void StackInit(ST *st)
{
    st->p = NULL;
    st->top = 0;
    st->Capacity = 0;
}

第二个接口StackDestory:释放栈->释放开辟的空间,将容量和栈顶置为0

void StackDestory(ST *st)
{
    assert(st);
    free(st->p);
    st->p = NULL;
    st->top = 0;
    st->Capacity = 0;
}

第三个接口StackPush:扩容并且入栈

void StackPush(ST *st, STDataType x)
{
    assert(st);
    //扩容
    if (st->Capacity == st->top)
    {
        //初始化中容量为0这里要判断一下
        int newCapacity = st->Capacity == 0 ? 2 : st->Capacity * 2;
        //开辟动态空间
        st->p = (STDataType *)realloc(st->p, sizeof(STDataType) * newCapacity);
        //判断堆区是否已满
        if (st->p == NULL)
        {
            printf("realloc fail!!!\n");
            exit(EXIT_FAILURE);
        }
        //更新容量
        st->Capacity = newCapacity;
    }
    //在栈顶插入数据
    st->p[st->top] = x;
    ++st->top;
}

第四个接口StackPop:出栈

void StackPop(ST *st)
{
    assert(st);
    //当栈没有变量时,继续出栈会导致越界
    assert(st->top > 0);
    --st->top;
}

第五个接口StackEmpty:判断栈释放为空

bool StackEmpty(ST *st)
{
    assert(st);
    //如果top为0则返回true,反之false
    return st->top == 0;
}

第六个接口StackSize:栈中有效数据的数量

int StackSize(ST *st)
{
    assert(st);
    return st->top;
}

最后一个接口:StackTop->取栈顶的数据

STDataType StackTop(ST *st)
{
    assert(st);
    //数组下标从0开始,所以需要减一
    return st->p[st->top - 1];
}

1.3 栈的完整代码
Stack.h

#ifndef STACK_H_
#define STACK_H_

#include 
#include 
#include 
#include 

//在数据结构中栈是一种特殊的顺序表
//在 *** 作系统中栈是虚拟进程中的内存分布

typedef int STDataType;

typedef struct Stack
{
    STDataType *p;
    int top;      //栈顶
    int Capacity; //容量
} ST;

void StackInit(ST *st);               //初始化栈
void StackDestory(ST *st);            //销毁栈
void StackPush(ST *st, STDataType x); //压栈
void StackPop(ST *st);                //出栈
bool StackEmpty(ST *st);              //判断栈是否为空
int StackSize(ST *st);                //栈中变量的数量
STDataType StackTop(ST *st);          //栈顶的值

#endif

Stack.c

#include "Stack.h"

void StackInit(ST *st)
{
    st->p = NULL;
    st->top = 0;
    st->Capacity = 0;
}

void StackDestory(ST *st)
{
    assert(st);
    free(st->p);
    st->p = NULL;
    st->top = 0;
    st->Capacity = 0;
}

void StackPush(ST *st, STDataType x)
{
    assert(st);
    //扩容
    if (st->Capacity == st->top)
    {
        int newCapacity = st->Capacity == 0 ? 2 : st->Capacity * 2;
        st->p = (STDataType *)realloc(st->p, sizeof(STDataType) * newCapacity);
        if (st->p == NULL)
        {
            printf("realloc fail!!!\n");
            exit(EXIT_FAILURE);
        }
        st->Capacity = newCapacity;
    }
    //在栈顶插入数据
    st->p[st->top] = x;
    ++st->top;
}

void StackPop(ST *st)
{
    assert(st);
    //当栈没有变量时,继续出栈会导致越界
    assert(st->top > 0);
    --st->top;
}

bool StackEmpty(ST *st)
{
    assert(st);
    //如果top为0则返回true,反之false
    return st->top == 0;
}

int StackSize(ST *st)
{
    assert(st);
    return st->top;
}

STDataType StackTop(ST *st)
{
    assert(st);
    return st->p[st->top - 1];
}


Test.c

#include "Stack.h"

int main()
{
    ST s;
    //初始化栈
    StackInit(&s);
    //压栈
    StackPush(&s, 1);
    StackPush(&s, 2);
    //出栈
    StackPop(&s);
    StackPush(&s, 3);
    StackPush(&s, 4);
    StackPush(&s, 5);
    StackPush(&s, 6);

    //出栈
    StackPop(&s);
    StackPop(&s);

    while (!StackEmpty(&s)) //判断栈是否为空
    {
        printf("%d ", StackTop(&s)); //打印栈顶的数据
        StackPop(&s);                //出栈
    }
    printf("\n");
    //释放栈
    StackDestory(&s);

    system("pause");
    return 0;
}

1.4 OJ题:“有效括号”

括号匹配问题。


(OJ链接)
思想:栈

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;		
	int capacity;	
}ST;

void StackInit(ST* ps);
void StackDestory(ST* ps);
void StackPush(ST* ps, STDataType x);
void StackPop(ST* ps);
bool StackEmpty(ST* ps);
int StackSize(ST* ps);
STDataType StackTop(ST* ps);

void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

void StackDestory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		ps->a = (STDataType*)realloc(ps->a, newCapacity* sizeof(STDataType));
		if (ps->a == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}

		ps->capacity = newCapacity;
	}

	ps->a[ps->top] = x;
	ps->top++;
}

void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	--ps->top;
}

bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}

STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}


int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}

bool isValid(char * s)
{
    ST st;
    StackInit(&st);
    
    while(*s)
    {
        if(*s == '(' || *s == '{' || *s == '[')
        {
           //左括号入栈
            StackPush(&st, *s);
            //更新s
            ++s;
        }
        else
        {
            //如果只有一个右括号,会导致访问空指针,导致崩溃
            if(StackEmpty(&st))
            return false;
            //左括号出栈与右括号匹配
            char top = StackTop(&st); //取栈顶值
            StackPop(&st);
            if(*s == ')' && top == '(' 
            || *s == '}' && top == '{' 
            || *s == ']' && top == '[')
            {
                //如果匹配则继续比较
                ++s;
            }
            else
            {
                return false;
            }
        }
    }
    //如果栈为空,则所有括号匹配<->不为空则没有完全匹配
    bool ret = StackEmpty(&st);
    StackDestory(&st);
    return ret;
}

2.1 队列的结构及概念
  1. 队列:只允许在一端进行插入数据 *** 作,在另一端进行删除数据 *** 作的特殊线性表,队列具有先进先出
    FIFO(First In First Out) ->跟排队一样
  2. 入队列:进行插入 *** 作的一端称为"队尾 "
  3. 出队列:进行删除 *** 作的一端称为"队头"

2.2 队列的实现
  1. 队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数
    组头上出数据,需要挪动数据,效率会比较低。


头文件:Queue.h

#ifndef QUEUE_H_
#define QUEUE_H_

#include 
#include 
#include 
#include 

//链式队列->队列节点里面包含队列头和尾
typedef int QDataType;

//入队数据
typedef struct QueueNode
{
    QDataType val;          //数据域
    struct QueueNode *next; //指针域
} QNode;

//队列结构
typedef struct Queue
{
    QNode *head; //头节点->队列头
    QNode *tail; //尾结点->队列尾
} Queue;

void QueueInit(Queue *q);              //初始化
void QueueDestory(Queue *q);           //释放队列
void QueuePush(Queue *q, QDataType x); //数据入队列(从队尾进入)
void QueuePop(Queue *q);
bool QueueEmpty(Queue *q);      //判断队列是否为空
size_t QueueSize(Queue *q);     //队列长度
QDataType QueueFront(Queue *q); //队头数据
QDataType QueueBack(Queue *q);  //队尾数据

#endif

第一个接口QueueInit:初始化队列

void QueueInit(Queue *q)
{
    assert(q);
    q->head = NULL;
    q->tail = NULL}

第二个接口QueueDestory:释放队列

void QueueDestory(Queue *q)
{
    assert(q);

    QNode *cur = q->head;
    while (cur)
    {
        QNode *next = cur->next;
        free(cur);
        cur = next;
    }
    q->head = q->tail = NULL;
}

第三个接口QueuePush:入队列

void QueuePush(Queue *q, QDataType x)
{
    assert(q);

    //开辟新节点
    QNode *newnode = (QNode *)malloc(sizeof(QNode));
    //判空
    if (newnode == NULL)
    {
        printf("malloc fail!!!\n");
        exit(EXIT_FAILURE);
    }
    else
    {
        newnode->val = x;
        newnode->next = NULL;
    }

    //入队列
    //如果队头和队尾都为NULL,则它们同时指向头节点
    if (q->head == NULL && q->tail == NULL)
    {
        q->head = newnode;
        q->tail = newnode;
    }
    else
    {
        q->tail->next = newnode;
        //更新队尾
        q->tail = newnode;
    }
}

第四个接口QueuePop:出队列

void QueuePop(Queue *q)
{
    assert(q);
    //队列没有数据时,不能删除
    assert(q->head != NULL);

    //出队列(从队头出)
    if (q->head->next == NULL)
    {
        //当只有一个节点时,头被释放,tail还是指向原来的头节点,这将导致不确定性
        free(q->head);
        q->tail = q->head = NULL;
    }
    else
    {
        QNode *next = q->head->next;
        free(q->head);
        q->head = next;
    }
}

第五个接口QueueEmpty:判断队列是否为NULL

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	//return pq->head == NULL && pq->tail == NULL;
	return pq->head == NULL;
}

第六个接口QueueSize:队列的长度

size_t QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	size_t size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}

	return size;
}

第七个接口QueueFront:队头数据

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	return pq->head->data;
}

最后一个接口QueueBack:队尾数据

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->tail);

	return pq->tail->data;
}

2.3 队列完整代码
Queue.h

#ifndef QUEUE_H_
#define QUEUE_H_

#include 
#include 
#include 
#include 

//链式队列->队列节点里面包含队列头和尾
typedef int QDataType;

typedef struct QueueNode
{
    QDataType val;          //数据域
    struct QueueNode *next; //指针域
} QNode;

typedef struct Queue
{
    QNode *head; //头节点->队列头
    QNode *tail; //尾结点->队列尾
} Queue;

void QueueInit(Queue *q);              //初始化
void QueueDestory(Queue *q);           //释放队列
void QueuePush(Queue *q, QDataType x); //数据入队列(从队尾进入)
void QueuePop(Queue *q);
bool QueueEmpty(Queue *q);      //判断队列是否为空
size_t QueueSize(Queue *q);     //队列长度
QDataType QueueFront(Queue *q); //队头数据
QDataType QueueBack(Queue *q);  //队尾数据

#endif

Queue.c

#include "Queue.h"

void QueueInit(Queue *q)
{
    assert(q);
    q->head = q->tail = NULL;
}

void QueueDestory(Queue *q)
{
    assert(q);

    QNode *cur = q->head;
    while (cur)
    {
        QNode *next = cur->next;
        free(cur);
        cur = next;
    }
    q->head = q->tail = NULL;
}

void QueuePush(Queue *q, QDataType x)
{
    assert(q);

    //开辟新节点
    QNode *newnode = (QNode *)malloc(sizeof(QNode));
    //判空
    if (newnode == NULL)
    {
        printf("malloc fail!!!\n");
        exit(EXIT_FAILURE);
    }
    else
    {
        newnode->val = x;
        newnode->next = NULL;
    }

    //入队列
    //如果队头和队尾都为NULL,则它们同时指向头节点
    if (q->head == NULL && q->tail == NULL)
    {
        q->head = newnode;
        q->tail = newnode;
    }
    else
    {
        q->tail->next = newnode;
        //更新队尾
        q->tail = newnode;
    }
}

void QueuePop(Queue *q)
{
    assert(q);
    assert(q->head != NULL);

    //出队列(从队头出)
    if (q->head->next == NULL)
    {
        //当只有一个节点时,头被释放,tail还是指向原来的头节点,这将导致不确定性
        free(q->head);
        q->tail = q->head = NULL;
    }
    else
    {
        QNode *next = q->head->next;
        free(q->head);
        q->head = next;
    }
}

bool QueueEmpty(Queue *q)
{
    assert(q);
    return q->head == NULL;
}

size_t QueueSize(Queue *q)
{
    assert(q);
    assert(q->head);

    QNode *cur = q->head;
    size_t Size = 0;
    while (cur)
    {
        ++Size;
        cur = cur->next;
    }
    return Size;
}

QDataType QueueFront(Queue *q)
{
    assert(q);
    assert(q->head);

    return q->head->val;
}

QDataType QueueBack(Queue *q)
{
    assert(q);
    assert(q->tail);

    return q->tail->val;
}

Test.c

#include "Queue.h"

//测试
int main()
{
    Queue q;
    //初始化队列
    QueueInit(&q);
    //入队列
    QueuePush(&q, 1);
    QueuePush(&q, 2);
    QueuePush(&q, 3);
    QueuePush(&q, 4);
    QueuePush(&q, 5);
    QueuePush(&q, 6);
    //显示队列数据
    while (!QueueEmpty(&q))
    {
        printf("%d ", QueueFront(&q));
        //出队列
        QueuePop(&q);
    }
    printf("\n");

    Queue p;
    //初始化队列
    QueueInit(&p);
    //入队列
    QueuePush(&p, 1);
    QueuePush(&p, 2);
    QueuePush(&p, 3);
    QueuePush(&p, 4);
    QueuePush(&p, 5);
    QueuePush(&p, 6);

    //出队列
    QueuePop(&p);
    QueuePop(&p);
    //显示队列数据
    while (!QueueEmpty(&p))
    {
        printf("%d ", QueueFront(&p));
        //出队列
        QueuePop(&p);
    }

    printf("\n");
    system("pause");
    return 0;
}

3.1 队列和栈相关OJ题
  1. 用队列实现栈。


    (OJ链接)
    思想:使用二个队列来进行实现

用C实现,造轮子我省略了…

MyStack* myStackCreate() 
{
    //动态开辟空间
    MyStack* st = (MyStack*)malloc(sizeof(MyStack));
    //初始化结构体
    QueueInit(&st->q1);
    QueueInit(&st->q2);
    return st;
}

void myStackPush(MyStack* obj, int x) 
{
    Queue* nonEmpty = &obj->q1; //非空队列
    Queue* Empty = &obj->q2; //空队列
    if(!QueueEmpty(nonEmpty))
    {
        QueuePush(nonEmpty, x);
    }
    else
    {
        QueuePush(Empty, x);
    }
}

int myStackPop(MyStack* obj) 
{
    Queue* nonEmpty = &obj->q1; //非空队列
    Queue* Empty = &obj->q2; //空队列
    //判断非空和空队列
    if(!QueueEmpty(Empty))
    {
        nonEmpty = &obj->q2;
        Empty = &obj->q1;
    }
    //把非空队列的N-1个数据压入到空队列中
    while(QueueSize(nonEmpty) > 1)
    {
        QueuePush(Empty, QueueFront(nonEmpty));
        //非空队列出数据
        QueuePop(nonEmpty);
    }

    //返回非空队列的队头
    int top = QueueFront(nonEmpty);
    QueuePop(nonEmpty);
    return top;
}

int myStackTop(MyStack* obj) 
{
    Queue* nonEmpty = &obj->q1; //非空队列
    Queue* Empty = &obj->q2; //空队列
    if(!QueueEmpty(nonEmpty))
    {
        //返回队尾
        return QueueBack(nonEmpty);
    }
    else
    {
        return QueueBack(Empty);
    }

}

bool myStackEmpty(MyStack* obj) 
{
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) 
{
    //这里不能直接释放obj,会导致内存泄漏
    QueueDestory(&obj->q1);
    QueueDestory(&obj->q2);
    free(obj);
}
  1. 用栈实现队列。


    (OJ链接)
    思想:二个栈来进行实现

这里也是用C实现,也省略了造轮子…

//二个栈实现队列
typedef struct 
{
    ST st1;
    ST st2;
} MyQueue;


MyQueue* myQueueCreate() 
{
    MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue));
    //初始化结构体
    StackInit(&q->st1);
    StackInit(&q->st2);
    return q;
}

void myQueuePush(MyQueue* obj, int x) 
{
    //入栈
    StackPush(&obj->st1, x);
}

int myQueuePop(MyQueue* obj) 
{
    //将第一个栈的数据导入第二个栈中
    if(StackEmpty(&obj->st2))
    {
       while(StackSize(&obj->st1))
       {
           StackPush(&obj->st2, StackTop(&obj->st1));
           StackPop(&obj->st1); //导一个出一个
       }
    }
    
    //取第二个栈顶的数据
    int top = StackTop(&obj->st2); 
    StackPop(&obj->st2); //出栈
    return top;
}

int myQueuePeek(MyQueue* obj) 
{
    if(StackEmpty(&obj->st2))
    {
       while(StackSize(&obj->st1))
       {
           StackPush(&obj->st2, StackTop(&obj->st1));
           StackPop(&obj->st1); //导一个出一个
       }
    }
    return StackTop(&obj->st2);
}

bool myQueueEmpty(MyQueue* obj) 
{
    return StackEmpty(&obj->st1) && StackEmpty(&obj->st2);
}

void myQueueFree(MyQueue* obj) 
{
    StackDestory(&obj->st1);
    StackDestory(&obj->st2);
    free(obj);
}

  1. 设计循环队列。


    (OJ链接)

//数组实现
typedef struct 
{
    int* p;
    int head; //队头
    int tail; //队尾
    int k; //有效数据
} MyCircularQueue;

bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k) 
{
    //动态开辟结构体
    MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    assert(q);
    //开辟四个数组空间,多出一个用来判断满和空的情况
    q->p = (int*)malloc(sizeof(int) * (k + 1));
    q->head = 0;
    q->tail = 0;
    q->k = k;
    return q;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    assert(obj);
    //如果tail后面是head说明队列已经满了,不能入队
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    else
    {
        obj->p[obj->tail] = value;
        //最后一个元素不存储数据
        if(obj->tail == obj->k)
        {
            obj->tail = 0;
        }
        else
        {
            ++obj->tail;
        }
    }
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    assert(obj);
    //如果队头(head)等于队尾(tail)说明队列为空,不能删
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    else
    {
        //最后一个元素不存储数据
        if(obj->head == obj->k)
        {
            obj->head = 0;
        }
        else
        {
            ++obj->head;
        }
    }
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) 
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->p[obj->head];
    }
}

int myCircularQueueRear(MyCircularQueue* obj) 
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        //如果tail为0,则返回第k个数据,说明已经走完一遍
        if(obj->tail == 0)
            return obj->p[obj->k];
        else
            return obj->p[obj->tail - 1];

    }
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    assert(obj);
    return obj->head == obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    assert(obj);
    //如果队尾为k,并且队头为0说明,队列已满
    if(obj->tail == obj->k && obj->head == 0)
    {
        return true;
    }
    else
    {
        return obj->tail + 1 == obj->head;
    }
}

void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->p);
    free(obj);
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/564774.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-06
下一篇 2022-04-06

发表评论

登录后才能评论

评论列表(0条)

保存