数据结构——栈和队列

(ps:栈是用数组实现的,队列是用链表实现的)

一、栈

1.用顺序表很好实现;用双向链表很好实现;用单向链表得头插然后头删也好操作(用链表尾的话,如果出栈需要遍历链表)

2.实现一个栈

//注意top是指向栈顶元素(top是下标)还是指向栈顶元素的下一个元素(top是size)

//判满的时候,第一种top==(capacity-1),第二种top==capacity。因为capacity和size一样

//realloc的第一个参数为空指针的时候,realloc的作用就和malloc一样(解决了初始为NULL的情况)

//栈不为空才能执行出栈pop操作

//访问栈的每一个元素需要把前一个pop掉才能访问下一个

//入栈顺序是1234,出栈顺序不一定是4321,可能边入边出

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef int STDataType;

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

// 初始化和销毁
void STInit(ST* pst)
{
	assert(pst);

	pst->a = NULL;
	// top指向栈顶数据的下一个位置
	pst->top = 0;

	// top指向栈顶数据
	//pst->top = -1;

	pst->capacity = 0;
}

void STDestroy(ST* pst)
{
	assert(pst);

	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}

// 入栈  出栈
void STPush(ST* pst, STDataType x)
{
	assert(pst);

	// 扩容
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		pst->a = tmp;
		pst->capacity = newcapacity;
	}

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

void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);

	pst->top--;
}

// 取栈顶数据
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);

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

// 判空
bool STEmpty(ST* pst)
{
	assert(pst);

	return pst->top == 0;
}

// 获取数据个数
int STSize(ST* pst)
{
	assert(pst);

	return pst->top;
}

int main()
{
	// 入栈:1 2 3 4
	// 出栈:4 3 2 1  /  2 4 3 1
	ST s;
	STInit(&s);
	STPush(&s, 1);
	STPush(&s, 2);

	printf("%d ", STTop(&s));
	STPop(&s);

	STPush(&s, 3);
	STPush(&s, 4);

	while (!STEmpty(&s))
	{
		printf("%d ", STTop(&s));
		STPop(&s);
	}

	STDestroy(&s);
}

//栈的训练题

//1.

//注意数量匹配但种类不一定匹配

//return false之前记得要清空,不然会有内存泄露的风险

//注意当只有一个"("或者")"输入时,第一个是要注意导致最后数组中还有数据,但是没进while循环,导致输出结果为true;第二个是要注意首先输入右括号,然后去访问栈顶元素匹配,但此时还没有元素进栈,导致越界访问

//思路:左括号入栈,右括号就取栈顶元素与他匹配。又因为如果匹配的话还要继续比较,程序比较难写,所以换个思路,当不匹配直接return false结束程序。

//升级版

//这个代码是我自己写的,有一个测试点没过,那个测试点是说有多余左括号,但是目前未发现错误在哪


#include <stdio.h>
#include <malloc.h>

typedef struct QList
{
    char *arr;
    int size;
} QL;
int main()
{
    QL ql,match;
    ql.arr=(char*)malloc(sizeof(char)*300);
    match.arr=(char*)malloc(sizeof(char)*300);
    match.size=0;
    ql.size=0;
    char ch[300];
    //  char dao;
    char duoyu;//用来记录最后一个多着的括号
    int cnt=1;
    int count=0;
    //首先把所有的括号存起来
    while(scanf("%s",&ch))
    {
        if(ch[0]=='.'&&!ch[1])
            break;
        for(int i=0; ch[i]; i++)
        {
            if(ch[i]=='('||ch[i]=='['||ch[i]=='{'||ch[i]==')'||ch[i]==']'||ch[i]=='}')
                ql.arr[ql.size++]=ch[i];
            else if(ch[i]=='/'&&ch[++i]=='*')
            {
                //把两个符号变为一个符号
                ql.arr[ql.size++]='<';
            }
            else if(ch[i]=='*'&&ch[++i]=='/')
            {
                ql.arr[ql.size++]='>';
            }
        }
    }
    //一定要注意只有一个括号的时候
    count=ql.size;
    if(count==1)
    {
        if(ql.arr[0]=='('||ql.arr[0]=='{'||ql.arr[0]=='[')
        {
            printf("%c-?\n",ql.arr[0]);
            return 0;
        }
        else if(ql.arr[0]==')'||ql.arr[0]==']'||ql.arr[0]=='}')
        {
            printf("?-%c\n",ql.arr[0]);
            return 0;
        }
        else if(ql.arr[0]=='<')
        {
            printf("/*-?\n");
            return 0;
        }
        else if(ql.arr[0]=='>')
        {
            printf("?-*/\n");
            return 0;
        }
    }
    //然后再用一个栈来存左括号,遇到右括号就判断
    for(int i=0; i<ql.size; i++)
    {
        if(ql.arr[i]=='('||ql.arr[i]=='{'||ql.arr[i]=='['||ql.arr[i]=='<')
        {
            match.arr[match.size++]=ql.arr[i];
        }
        else if(ql.arr[i]==')')
        {
            if(match.arr[match.size-1]=='('&&match.size>=1)
            {
                match.size--;
            }
            else
            {
                cnt=0;
                duoyu=ql.arr[i];
                break;
            }
        }
        else if(ql.arr[i]=='}')
        {
            if(match.arr[match.size-1]=='{'&&match.size>=1)
            {
                match.size--;
            }
            else
            {
                cnt=0;
                    duoyu=ql.arr[i];
                break;
            }
        }
        else if(ql.arr[i]==']')
        {
            if(match.arr[match.size-1]=='['&&match.size>=1)
            {
                match.size--;
            }
            else
            {
                cnt=0;
                duoyu=ql.arr[i];
                break;
            }
        }
        else if(ql.arr[i]=='>')
        {
            if(match.arr[match.size-1]=='<'&&match.size>=1)
            {
                match.size--;
            }
            else
            {
                cnt=0;
                duoyu=ql.arr[i];
                break;
            }
        }
    }
    //printf("%d %c\n",match.size,match.arr[match.size]);
    //这里一定要写cnt==1的判断条件,否则会输出两次
    if(match.size==0&&cnt==1)
    {
        printf("YES\n");
    }
    else
    {
        printf("NO\n");
        if(match.size!=0)//如果栈不为空,则一定还有左括号,则一定输出左括号
        {
            if(match.arr[match.size-1]=='<')
                printf("/*-?\n");
            else
                printf("%c-?\n",match.arr[match.size-1]);
        }
        else//左括号进栈就会让size++,所以当size为0的时候,栈里面一定没有左括号了
        {
            if(duoyu=='>')
                printf("?-*/\n");
            else
                printf("?-%c\n",duoyu);
        }
    }
    return 0;
}

//这是大佬的ac代码

#include <stdio.h>
#include <stdlib.h>
#define Maxsize 105

typedef struct StackRecord *Stack;
struct StackRecord  {
    int Top;
    char *Array;
};

Stack creat(){
    Stack s = (Stack) malloc(sizeof(struct StackRecord));
    s->Array = (char *) malloc(Maxsize * sizeof(char));
    s->Top = -1;
    return s;
}

int is_empty(Stack s){
    if(s->Top == -1) return 1;
    else return 0;
}
void push(Stack s, char x){
    s->Array[++(s->Top)] = x;
}
void pop(Stack s){
    s->Top--;

}
char top(Stack s){
    return s->Array[s->Top];
}
char a[100];
char str[200];
int main(){
    int k = 0;
    char t;
    while(scanf("%s",str)){

        if(str[0]=='.'&&!str[1]) break;
        for(int i=0; str[i]; i++){
            if(str[i]=='('||str[i]=='['||str[i]=='{'||str[i]==')'||str[i]=='}' ||str[i]==']')
                a[k++]=str[i];
            else if(str[i]=='/'&&str[i+1]=='*'){
                a[k++]='<';
                i++;
            }else if(str[i]=='*'&&str[i+1]=='/'){
                a[k++]='>';
                i++;
            }
        }
    }
    int flag=0;
    Stack s = creat();
    for(int i=0; i<k; i++){
        if(a[i]=='(' || a[i]=='[' || a[i]=='{' || a[i]=='<') {
            push(s, a[i]);
        } else if(a[i] == ')'){
            if(s->Top!=-1 && top(s)=='(') pop(s);
            else{
                t = a[i];
                flag = 1;
                break;
            }

        } else if(a[i] == ']'){
            if(s->Top != -1 && top(s)=='[') pop(s);
            else{
                t = a[i];
                flag = 1;
                break;
            }

        } else if(a[i] == '}'){
            if(s->Top != -1 && top(s)== '{') pop(s);
            else{
                t = a[i];
                flag = 1;
                break;
            }
        } else if(a[i] == '>'){
            if(s->Top != -1 && top(s)== '<') pop(s);
            else{
                t = a[i];
                flag = 1;
                break;
            }
        }
    }
    if(!flag && is_empty(s)) printf("YES\n");
    else{
        printf("NO\n");
        if(!is_empty(s)){
            if(top(s)=='<') printf("/*-?\n");
            else printf("%c-?\n", top(s));
        }else{
            if(t=='>') printf("?-*/\n");
            else printf("?-%c\n", t);
        }
    }
    return 0;
}

二、队列

//队列用于保持公平性

//应用:广度优先遍历(好友推荐)

//不能在单链表队尾出,因为找不到上一个节点

//注意分析零个节点、一个节点、两个节点......

//解决需要改变形参指针指向的数据进而影响实参的问题:

        1.二级指针/传引用

        2.加上一个头节点

        3.把头尾指针再包装到一个结构体里面,相当于要改变结构体的内容,只需要传结构体指针就可以了,结构体指针就是一级指针

//队列的实现

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType val;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);

		cur = next;
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

// 队尾插入
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	newnode->next = NULL;
	newnode->val = x;
    //此时队列为空
	if (pq->ptail == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

// 队头删除
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->size != 0);

	/*QNode* next = pq->phead->next;
	free(pq->phead);
	pq->phead = next;

	if (pq->phead == NULL)
		pq->ptail = NULL;*/

	// 一个节点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else // 多个节点
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}

	pq->size--;
}

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

	return pq->phead->val;
}

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

	return pq->ptail->val;
}

int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

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

	return pq->size == 0;
}
int main()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	printf("%d ", QueueFront(&q));
	QueuePop(&q);

	QueuePush(&q, 3);
	QueuePush(&q, 4);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");

	return 0;
}

//队列的训练题:

//1.

//思路:保持一个存数据一个为空。入数据入到不为空的队列。出数据的时候通过空的队列导一下,把前size-1个数据导过去,剩下的那个提取出来

//注意:局部变量出了作用域会销毁,但是malloc的空间出了作用域还会存在

//像这个myStackCreate函数,st出了作用域会销毁,返回的地址中不知道存储了什么

//也不能把他定义成全局或静态,因为测试用例可能会调用好几次create函数,但是定义成全局或者静态的之后底层就只有那一个空间,程序就不对了

//不能对全局指针和静态指针多次malloc

//如何销毁呢?

//完整代码

 //自己写的时候出现的问题

//要改变同一块地址上的内容时要用指针

//2.有限空间,保证先进先出,重复使用

//只这样判断会发现空和满的判断条件一样

//法二:

//解决tail位于0时再减一的问题

//完整代码

//是否空、是否满、插入、删除、找队头、找队尾、销毁

//注意取模


 

//3.用栈来实现队列

//思路:把数据都存放在pushst中,popst专门用来出数据,当popst中空了之后,就把pushst中的数据移动到popst中

//自己写出现的问题:指针未定义

这个pushst和popst都不能是指针类型,因为后续要给这个结构体malloc,如果是指针的话,tmp malloc出来的空间里面放的都是指针的大小,指针的大小是固定的字节数(4/8),这个地方只有用栈对象才可以访问栈里面的元素

//自己写的代码过了

//要用malloc时,要malloc的那个结构体里面的成员不能是指针

//因为你这个是给一整个结构体开空间,之前遇到的里面是指针的情况是那个指针就是一个节点,给那个节点开空间就是对的

//这里一定要注意好几层结构体嵌套起来

typedef int STDataType;
typedef struct Stack
{
    STDataType* a;
    int top;
    int capacity;
}ST;
void STInit(ST* pst)
{
    assert(pst);//别忘了断言
    pst->a=NULL;
    pst->top=pst->capacity=0;
}
void STDestroy(ST* pst)
{
    //也得断言!
    assert(pst);
    free(pst->a);
    pst->top=pst->capacity=0;
}
void STPush(ST* pst,STDataType x)
{
    assert(pst);
    if(pst->top==pst->capacity)
    {
        int newcapacity=pst->capacity==0?4:2*pst->capacity;
        //realloc注意两个参数
        STDataType* tmp=(STDataType*)realloc(pst->a,newcapacity*sizeof(STDataType));
        //只要开辟空间必须判断是否成功
        if(tmp==NULL)
        {
            exit(-2);
        }
        //别忘了重新赋值
        pst->a=tmp;
        pst->capacity=newcapacity;
    }
    pst->a[pst->top]=x;
    pst->top++;
}
void STPop(ST* pst)
{
    //照例断言
    //没有数据就不用pop了
    assert(pst);
    assert(pst->a);
    pst->top--;
}
STDataType STTop(ST* pst)
{
    assert(pst);
    assert(pst->a);
    return pst->a[pst->top-1];
}
bool STEmpty(ST* pst)
{
    assert(pst);
    return pst->top==0;
}
int STSize(ST* pst)
{
    return pst->top;
}

typedef struct {
    ST pushst;
    ST popst;
} MyQueue;

//创建一个节点
MyQueue* myQueueCreate() {
    MyQueue* tmp=(MyQueue*)malloc(sizeof(MyQueue));
    STInit(&tmp->pushst);
    STInit(&tmp->popst);
    return tmp;
}

void myQueuePush(MyQueue* obj, int x) {
    STPush(&obj->pushst,x);
}

//返回队列开头元素
int myQueuePeek(MyQueue* obj) {
    if(STEmpty(&obj->popst))
    {
        while(!STEmpty(&obj->pushst))
        {
            int top=STTop(&obj->pushst);
            STPush(&obj->popst,top);
            STPop(&obj->pushst);
        }
    }
    return STTop(&obj->popst);
}
int myQueuePop(MyQueue* obj) {
    int front=myQueuePeek(obj);
    STPop(&obj->popst);
    return front;
}


bool myQueueEmpty(MyQueue* obj) {
    return STEmpty(&obj->pushst)&&STEmpty(&obj->popst);
}

void myQueueFree(MyQueue* obj) {
    STDestroy(&obj->pushst);
    STDestroy(&obj->popst);
    free(obj);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值