数据结构之栈与队列习题详解解析

个人主页:点我进入主页

专栏分类:C语言初阶      C语言程序设计————KTV       C语言小游戏     C语言进阶

C语言刷题       数据结构初阶

欢迎大家点赞,评论,收藏。

一起努力,一起奔赴大厂。

目录

1.前言

2.概念题

3.编程题

3.1. 括号匹配问题。OJ链接

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

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

3.4. 设计循环队列。OJ链接

4.总结 


1.前言

        在上一篇文章中我们讲解了关于栈和队列的性质以及栈和队列的实现,代码还没保存吗?力扣刷题用不到吗?力扣解题出现错误调试时还想自己写一个栈和队列吗?还愣着干嘛,还不保存上。今天我们就主要针对上一篇文章的习题进行详细解析,我们先对概念题进行解析然后再搞编程题。

2.概念题

1.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出
栈的顺序是( )。
A 12345ABCDE
B EDCBA54321
C ABCDE12345
D 54321EDCBA

        我们将1,2,3,4,5,A,B,C,D,E依次入栈,我们知道栈是先进后出,所以我们的出栈顺序为EDCBA54321故答案为B.

2.若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()
A 1,4,3,2
B 2,3,4,1
C 3,1,4,2
D 3,4,2,1

        针对这种题型我们最好的解决方法就是模拟栈的实现,我们进1出1,进2,进3,进4出4,出3,出2,选项A可以实现正确,我们进1,进2,出2,进3,出3,进4,出4,出1,选项B可以实现正确,我们进1,进2,进3,出3,想要出1需要将2出栈,所以错误,选择C.

3.循环队列的存储空间为 Q(1:100) ,初始状态为 front=rear=100 。经过一系列正常的入队与退队操作
后, front=rear=99 ,则循环队列中的元素个数为( )
A 1
B 2
C 99
D 0或者100

        我们知道循环队列是可以让数据进行循环,我们知道有100个位置,每个位置都占,当我们的队列空时满足front=rear,当我们的队列满时满足front=rear,故选择D.

4.以下( )不是队列的基本运算?
A 从队尾插入一个新元素
B 从队列中删除第i个元素
C 判断一个队列是否为空D 读取队头元素的值

        我们知道队列是先进先出,也就是头出,尾进,所以我们呢很容易看出选项B从队列中删除第i个元素,不满足头出,尾进,故选择B.

5.现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N。其队内有效长度为?(假设
队头不存放数据)
A (rear - front + N) % N + 1
B (rear - front + N) % N
C ear - front) % (N + 1)
D (rear - front + N) % (N - 1)

        我们可以得到循环队列是有一个空位,我们知道rear和front这两个位置是不定的可能在一起,也可能front在前,也可能rear在前,故我们让这两个相减然后加上N,然后对N进行取余就可以得到有效长度。

3.编程题

3.1. 括号匹配问题。OJ链接

typedef struct Stack{char* data;int top;int capacity;
}Stack;
void InItStack(Stack *s)
{assert(s);s->capacity=4;s->top=-1;s->data=(char*)malloc(sizeof(char)*s->capacity);
}
void CheckCapacity(Stack *s)
{assert(s);if(s->top+1==s->capacity)s->capacity+=2;char *p=(char*)realloc(s->data,sizeof(char)*s->capacity);if(p==NULL){perror("realloc fail");return ;}else s->data=p;
}
void StackPush(Stack *s,char ch)
{assert(s);CheckCapacity(s);s->data[++s->top]=ch;
}
bool StackEmpty(Stack*s)
{assert(s);if(s->top==-1)return true;return false ; 
}
char StackPop(Stack*s)
{assert(s);if(!StackEmpty(s)){s->top--;return s->data[s->top+1];}return '1';
}
void StackDestory(Stack *s)
{assert(s);free(s->data);
}
bool isValid(char* arr) {Stack s;InItStack(&s);int sz=strlen(arr),i;for(i=0;i<sz;i++){if(arr[i]=='('||arr[i]=='['||arr[i]=='{'){StackPush(&s,arr[i]);}else{char ch=StackPop(&s);if((arr[i]==')'&&ch=='(')||(arr[i]==']'&&ch=='[')||(arr[i]=='}'&&ch=='{')){;}else {StackDestory(&s);return false;}}}StackDestory(&s);return StackEmpty(&s);
}
本题主要用到栈的操作,我们遇到左括号入栈,遇到右括号出栈,出栈时比较是否匹配,最后看栈是否空。

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

typedef struct QNode {int data;struct QNode* next;
}QNode;
typedef struct Queue {struct QNode* head;struct QNode* tail;
}Queue;
void QueueInit(Queue* q)
{assert(q);q->head = NULL;q->tail = NULL;
}
QNode* QueueCreate(Queue* q, int x)
{QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");return NULL;}newnode->data = x;newnode->next = NULL;return newnode;
}
void QueuePush(Queue* q, int x)
{QNode* newnode = QueueCreate(q, x);if (q->head == NULL){q->head = newnode;q->tail = newnode;}else{q->tail->next = newnode;q->tail = newnode;}
}
bool QueueEmpty(Queue* q)
{return q->head == NULL;
}
int QueuePop(Queue* q)
{assert(q);if (!QueueEmpty(q)){int data = q->head->data;if (q->head == q->tail){free(q->head);q->head = q->tail = NULL;}else{QNode* cur = q->head->next;free(q->head);q->head = cur;}return data;}return 1;
}
int QueueSize(Queue* q)
{int count = 0;QNode* cur = q->head;while (cur){count++;cur = cur->next;}return count;
}
void QueueDestory(Queue* q)
{assert(q);if (!QueueEmpty(q)){QNode* cur = q->head->next;QNode* prev = q->head;while (prev){free(prev);prev = cur;if (cur){cur = cur->next;}}}
}
void QueuePrint(Queue* q)
{assert(q);QNode* cur = q->head;while (cur){printf("%d ", cur->data);cur = cur->next;}
}
int top(Queue* q)
{assert(q);if (!QueueEmpty(q)){return q->head->data;}return 1;
}typedef struct {Queue queue1;Queue queue2;
} MyStack;MyStack* myStackCreate() {MyStack*obj=(MyStack*)malloc(sizeof(MyStack));QueueInit(&obj->queue1);QueueInit(&obj->queue2);return obj;
}void myStackPush(MyStack* obj, int x) {assert(obj);if(!QueueEmpty(&obj->queue1)){QueuePush(&obj->queue1,x);}else{QueuePush(&obj->queue2,x);}
}int myStackPop(MyStack* obj) {assert(obj);if(!QueueEmpty(&obj->queue1)){QNode*cur=(obj->queue1).head;while(cur->next!=NULL){cur=cur->next;int data=QueuePop(&obj->queue1);QueuePush(&obj->queue2,data);}return  QueuePop(&obj->queue1);}else{QNode*cur=obj->queue2.head;while(cur->next!=NULL){cur=cur->next;int data=QueuePop(&obj->queue2);QueuePush(&obj->queue1,data);}return  QueuePop(&obj->queue2);    }
}int myStackTop(MyStack* obj) {if(!QueueEmpty(&obj->queue1)){return ((obj->queue1).tail)->data;}elsereturn ( (obj->queue2).tail)->data; 
}bool myStackEmpty(MyStack* obj) {assert(obj);return QueueEmpty(&obj->queue1)&&QueueEmpty(&obj->queue2);
}void myStackFree(MyStack* obj) {assert(obj);QueueDestory(&obj->queue1);QueueDestory(&obj->queue2);free(obj);
}
typedef struct QNode {int data;struct QNode* next;
}QNode;
typedef struct Queue {struct QNode* head;struct QNode* tail;
}Queue;
void QueueInit(Queue* q)
{assert(q);q->head = NULL;q->tail = NULL;
}
QNode* QueueCreate(Queue* q, int x)
{QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");return NULL;}newnode->data = x;newnode->next = NULL;return newnode;
}
void QueuePush(Queue* q, int x)
{QNode* newnode = QueueCreate(q, x);if (q->head == NULL){q->head = newnode;q->tail = newnode;}else{q->tail->next = newnode;q->tail = newnode;}
}
bool QueueEmpty(Queue* q)
{return q->head == NULL;
}
int QueuePop(Queue* q)
{assert(q);if (!QueueEmpty(q)){int data = q->head->data;if (q->head == q->tail){free(q->head);q->head = q->tail = NULL;}else{QNode* cur = q->head->next;free(q->head);q->head = cur;}return data;}return 1;
}
int QueueSize(Queue* q)
{int count = 0;QNode* cur = q->head;while (cur){count++;cur = cur->next;}return count;
}
void QueueDestory(Queue* q)
{assert(q);if (!QueueEmpty(q)){QNode* cur = q->head->next;QNode* prev = q->head;while (prev){free(prev);prev = cur;if (cur){cur = cur->next;}}}
}
void QueuePrint(Queue* q)
{assert(q);QNode* cur = q->head;while (cur){printf("%d ", cur->data);cur = cur->next;}
}
int top(Queue* q)
{assert(q);if (!QueueEmpty(q)){return q->head->data;}return 1;
}typedef struct {Queue queue1;Queue queue2;
} MyStack;MyStack* myStackCreate() {MyStack*obj=(MyStack*)malloc(sizeof(MyStack));QueueInit(&obj->queue1);QueueInit(&obj->queue2);return obj;
}void myStackPush(MyStack* obj, int x) {assert(obj);if(!QueueEmpty(&obj->queue1)){QueuePush(&obj->queue1,x);}else{QueuePush(&obj->queue2,x);}
}int myStackPop(MyStack* obj) {assert(obj);if(!QueueEmpty(&obj->queue1)){QNode*cur=(obj->queue1).head;while(cur->next!=NULL){cur=cur->next;int data=QueuePop(&obj->queue1);QueuePush(&obj->queue2,data);}return  QueuePop(&obj->queue1);}else{QNode*cur=obj->queue2.head;while(cur->next!=NULL){cur=cur->next;int data=QueuePop(&obj->queue2);QueuePush(&obj->queue1,data);}return  QueuePop(&obj->queue2);    }
}int myStackTop(MyStack* obj) {if(!QueueEmpty(&obj->queue1)){return ((obj->queue1).tail)->data;}elsereturn ( (obj->queue2).tail)->data; 
}bool myStackEmpty(MyStack* obj) {assert(obj);return QueueEmpty(&obj->queue1)&&QueueEmpty(&obj->queue2);
}void myStackFree(MyStack* obj) {assert(obj);QueueDestory(&obj->queue1);QueueDestory(&obj->queue2);free(obj);
}

        我们用队列实现栈,这主要对栈和队列性质的考察,我们入栈就是寻找空队列入队,出栈是将非空队列出队剩下一个数据,这一个数据就是要出栈的数据,栈顶就是队尾的数据。

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

typedef struct Stack {int* data;int top;int capacity;
}Stack;void InItStack(Stack* s)
{assert(s);s->capacity = 4;s->top = -1;s->data = (int*)malloc(sizeof(int) * s->capacity);
}
void CheckCapacity(Stack* s)
{assert(s);if (s->top + 1 == s->capacity)s->capacity += 2;int* p = (int*)realloc(s->data, sizeof(int) * s->capacity);if (p == NULL){perror("realloc fail");return;}elses->data = p;
}
void StackPush(Stack* s, int ch)
{assert(s);CheckCapacity(s);s->data[++s->top] = ch;
}
bool StackEmpty(Stack* s)
{assert(s);if (s->top == -1)return true;return false;
}
int StackPop(Stack* s)
{assert(s);if (!StackEmpty(s)){s->top--;return s->data[s->top + 1];}return '1';
}
void StackDestory(Stack* s)
{assert(s);free(s->data);
}
int StackSize(Stack* s)
{assert(s);return s->top + 1;
}
int StackTopdata(Stack* s)
{assert(s);return s->data[s->top];
}typedef struct {Stack s1;Stack s2;
} MyQueue;MyQueue* myQueueCreate() {MyQueue*obj=(MyQueue*)malloc(sizeof(MyQueue));InItStack(&obj->s1);InItStack(&obj->s2);return obj;
}void myQueuePush(MyQueue* obj, int x) {assert(obj);if(!StackEmpty(&obj->s1)){StackPush(&obj->s1,x);}else{StackPush(&obj->s2,x);}
}int myQueuePop(MyQueue* obj) {assert(obj);int data;if(!StackEmpty(&obj->s1)){while(!StackEmpty(&obj->s1)){StackPush(&obj->s2,StackPop(&obj->s1));}data=StackPop(&obj->s2);while(!StackEmpty(&obj->s2)){StackPush(&obj->s1,StackPop(&obj->s2));}}else{while(!StackEmpty(&obj->s2)){StackPush(&obj->s1,StackPop(&obj->s2));}data=StackPop(&obj->s1);while(!StackEmpty(&obj->s1)){StackPush(&obj->s2,StackPop(&obj->s1));}}return data;
}int myQueuePeek(MyQueue* obj) {assert(obj);if(!StackEmpty(&obj->s1)){return (obj->s1).data[0];}elsereturn (obj->s2).data[0];
}bool myQueueEmpty(MyQueue* obj) {assert(obj);return StackEmpty(&obj->s1)&&StackEmpty(&obj->s2);
}void myQueueFree(MyQueue* obj) {assert(obj);free((obj->s1).data);free((obj->s2).data);free(obj);
}

        这道题也是考察栈和队列的性质 ,入队就是寻找非空的栈入栈(都为空时入第二个栈),由于队列是先进先出,所以我们将非空栈全部转到空栈,栈是先进后出,所以数据会倒置,栈顶就是第一个进入的数据,让他出栈就是出队,然后再次倒置回去,正常操作即可。


3.4. 设计循环队列。OJ链接


typedef struct {int* data;int capacity;int head;int tail;
} MyCircularQueue;bool myCircularQueueIsEmpty(MyCircularQueue* obj) {if(obj->tail==obj->head){return true;}return false;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {assert(obj);if((obj->tail+1)%(obj->capacity+1)==obj->head)return true;elsereturn false;
}void myCircularQueueFree(MyCircularQueue* obj) {assert(obj);free(obj->data);free(obj);
}MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue*queue=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));queue->data=(int*)malloc(sizeof(int)*(k+1));queue->capacity=k;queue->head=queue->tail=0;return queue;
}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {assert(obj);if(!myCircularQueueIsFull(obj)){obj->data[obj->tail]=value;obj->tail=(obj->tail+1)%(obj->capacity+1);return true;}return false;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {assert(obj);if(!myCircularQueueIsEmpty(obj)){obj->head=(obj->head+1)%(obj->capacity+1);return true;}return false;
}int myCircularQueueFront(MyCircularQueue* obj) {assert(obj);if(!myCircularQueueIsEmpty(obj))return obj->data[obj->head];return -1;
}int myCircularQueueRear(MyCircularQueue* obj) {assert(obj);if(!myCircularQueueIsEmpty(obj)){return obj->data[(obj->tail+obj->capacity)%(obj->capacity+1)];}return -1;
}

        循环队列我们利用数组或链表都可以,当我们使用数组时只需要进行推理即可以得到数据的下标,链表在判断队列满和空比较困难,所以我们选择数组,尤其是找队列的头和尾,判断对满就是(obj->tail+1)%(obj->capacity+1)==obj->head,队列空就是obj->tail=obj->head;

4.总结 

        今天的内容主要是对栈和队列的性质和操作进行深入学习,还有一个特别重要的就是我们在写题的时候我们的栈,队列,链表的外接函数我们可以进行复制过来,特别注意,我们可以称之为cv操作,今天的内容就到这里了,希望大家可以学到很多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/169966.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python_selenium自动化测试框架

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

springboot程序启动成功后执行的方法

//实现该接口&#xff0c;run方法既程序启动成功后将要执行的方法 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) //package org.springframework.boot;FunctionalInterface public interface CommandLineRunner {…

nginx的n种用法(nginx安装+正向代理+反向代理+透明代理+负载均衡+静态服务器)

nginx的安装 一、安装依赖 # 一键安装四个依赖 yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel二、安装nginx yum install nginx三、检查是否安装成功 nginx -v四、启动/停止nginx /etc/init.d/nginx start /etc/init.d/nginx stop五、编辑配置文件…

重生之我是一名程序员 41 ——字符串函数(2)

哈喽啊大家晚上好&#xff01;今天呢我们延续昨天的内容&#xff0c;给大家带来第二个字符串函数——strcat函数。 首先呢&#xff0c;还是先带大家认识一下它。strcat函数是C语言中用于将两个字符串连接起来的函数&#xff0c;其函数原型为&#xff1a; char *strcat(char *…

JAVA毕业设计112—基于Java+Springboot+Vue的宠物领养社区小程序(源码+数据库)

基于JavaSpringbootVue的宠物领养社区小程序(源码数据库)112 一、系统介绍 本系统前后端分离带小程序 小程序&#xff08;用户端&#xff09;&#xff0c;后台管理系统&#xff08;管理员&#xff09; 小程序&#xff1a; 登录、注册、宠物领养、发布寻宠、发布领养、宠物社…

算法设计与实现--分治篇

什么是分治算法 分治算法是一种常见的问题解决方法&#xff0c;它将一个复杂的问题划分为多个相同或相似的子问题&#xff0c;然后递归地解决这些子问题&#xff0c;最后将子问题的解合并得到原问题的解。 分治算法通常包含三个步骤&#xff1a; 分解&#xff08;Divide&…

MongoDB的部署

MongoDB部署 基于Linux平台 前置要求 在Centos7 64位系统中安装64位的MongoDB的安装包&#xff0c;通过访问MongoDB官网https://www.mongodb.com/download-center/community进入MongoDB下载页面。Version&#xff1a;指定MongoDB版本&#xff0c;MongoDB的版本分为稳定版和开发…

5.3每日一题(不确定正负号的级数敛散性:和一个正项级数比较判定)

比较判别法和比较判别法的极限形式是对正项级数而言的&#xff0c;若一个级数和p级数比较&#xff0c;结果>0&#xff0c;则同敛散&#xff1b;若结果<0&#xff0c;则结果乘以-1 结果又同敛散了&#xff1b;所以只要比值不等于0&#xff0c;则同敛散&#xff1b; 所以当…

JVM虚拟机:G1垃圾回收器的日志分析

本文重点 本文我们将学习G1垃圾回收器的日志 使用 执行命令 java -Xms20M -Xmx20M -XX:PrintGCDetails -XX:UseG1GC 类名 分析 前面我们学习了G1垃圾回收器&#xff0c;它的回收有三种可能&#xff1a; YGC FGC MixedGC GC pause表示STW,Evacuation表示复制对象&#xff0c;…

c语言-字符函数和字符串函数详解

文章目录 1. 字符分类函数2. 字符转换函数3. strlen的使用和模拟实现4. strcpy的使用和模拟实现5. strncpy函数的使用6. strcat的使用和模拟实现7. strncat函数的使用8. strcmp的使用和模拟实现9. strncmp函数的使用10. strstr的使用和模拟实现11. strtok函数的使用12. strerro…

【尚跑】2023宝鸡马拉松安全完赛,顺利PB达成

1、赛事背景 千年宝地&#xff0c;一马当先&#xff01;10月15日7时30分&#xff0c;吉利银河2023宝鸡马拉松在宝鸡市行政中心广场鸣枪开跑。 不可忽视的是&#xff0c;这次赛事的卓越之处不仅在于规模和参与人数&#xff0c;还在于其精心的策划和细致入微的组织。为了确保每位…

Kotlin学习——kt里面的函数,高阶函数 函数式编程 扩展函数和属性

Kotlin 是一门现代但已成熟的编程语言&#xff0c;旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作&#xff0c;并提供了多种方式在多个平台间复用代码&#xff0c;以实现高效编程。 https://play.kotlinlang.org/byExample/01_introduction/02_Functio…

论文笔记:详解NEUPSL DSI

《Using Domain Knowledge to Guide Dialog Structure Induction via Neural Probabilistic 》 名词解释 Dialog Structure Induction&#xff08;DSI&#xff09;是推断给定目标导向对话的潜在对话结构&#xff08;即一组对话状态及其时间转换&#xff09;的任务。它是现代对…

基于Scapy修改ClientHello的SNI(三)

需求:修改HTTPS的ClientHello中的SNI字段 目标:修改成功,wireshark显示正常 语言:Python 三方库:Scapy 下面是一个标准的ClientHello报文,是从一个完整的HTTPS流中保存出来的,原始报文中的SNI是www.baidu.com 在上一篇文章中 记录基于scapy构造ClientHello报文的尝试…

【Docker】Docker与Kubernetes:区别与优势对比

前言 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。   kubernetes&#xff0c;简称K8s&a…

C#调用ffmpeg从视频提取图片

微信公众号“CSharp编程大全”的文章《C#从视频提取图片&#xff1f;》介绍了基于Microsoft.DirectX.AudioVideoPlayback.Video类实现从视频提取图片的方式&#xff0c;本来是想学习并测试该类的用法&#xff0c;但实际测试过程中却没有测通。百度从视频提取图片&#xff0c;网…

【Leetcode合集】1457. 二叉树中的伪回文路径

1457. 二叉树中的伪回文路径 1457. 二叉树中的伪回文路径 代码仓库地址&#xff1a; https://github.com/slience-me/Leetcode 个人博客 &#xff1a;https://slienceme.xyz 给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的&am…

rtsp点播异常出现‘circluar_buffer_size‘ option was set but it is xx

先说现象: 我使用potplay播放器来点播rtsp码流的时候可以点播成功&#xff0c;同事使用vlc和FFplay来点播rtsp码流的时候异常。 排查思路: 1.开始怀疑是oss账号问题&#xff0c;因为ts切片数据是保存在oss中的&#xff0c;我使用的是自己的oss账号&#xff0c;同事使用的是公司…

四边形不等式优化DP

目录 四边形不等式内容[HNOI2008]玩具装箱解析代码实现 参考资料 四边形不等式内容 TODO [HNOI2008]玩具装箱 解析 满足四边形不等式&#xff0c;决策具有单调性. 对于两个位置 i , j i, j i,j, 对应的最优决策点一定有 o p t [ i ] < o p t [ j ] opt[i] < opt[j]…

代码随想录 406. 根据身高重建队列

题目 假设有打乱顺序的一群人站成一个队列&#xff0c;数组 people 表示队列中一些人的属性&#xff08;不一定按顺序&#xff09;。每个 people[i] [hi, ki] 表示第 i 个人的身高为 hi &#xff0c;前面 正好 有 ki 个身高大于或等于 hi 的人。 请你重新构造并返回输入数组 p…