数据结构中栈的介绍

合集下载

数据结构-栈与队列

数据结构-栈与队列

栈 1.6栈的应用
运算符的优先级关系表在运算过程中非常重要,它是判定进栈、出栈的重要依据。
θ1
θ2
+
-
+
>
>
-
>
>
*
>
>
/
>
>
(
<
<
)
>
>
#
<
<
*
/
(
)
#
<
<
<
>
>
<
<
<
>
>
>
>
<
>
>
>
>
<
>
>
<
<
<
=
>
>
>
>
<
<
<
=

1.6栈的应用
下面以分析表达式 4+2*3-12/(7-5)为例来说明求解过程,从而总结出表达式求值的算 法。求解中设置两个栈:操作数栈和运算符栈。从左至右扫描表达式:# 4+2*3-12/(7-5) #, 最左边是开始符,最右边是结束符。表达式求值的过程如下表所示:
1.4栈的顺序存储结构
设计进栈算法——Push 函数。首先,判断栈是否已满,如果栈已满,就运用 realloc 函 数重新开辟更大的栈空间。如果 realloc 函数返回值为空,提示溢出,则更新栈的地址以及栈 的当前空间大小。最终,新元素入栈,栈顶标识 top 加 1。

stack的知识点

stack的知识点

stack的知识点1. 栈的定义和特点栈(Stack)是一种具有特殊限制的线性数据结构,它的特点是“后进先出”(Last In First Out,LIFO)。

栈在计算机科学中有着广泛的应用,是一种非常重要的数据结构。

2. 栈的基本操作栈的基本操作包括入栈(push)和出栈(pop)两个操作。

•入栈操作:将元素添加到栈的顶部,使其成为新的栈顶元素。

•出栈操作:移除栈顶的元素,并返回被移除的元素。

除了入栈和出栈操作外,栈还支持其他操作,如获取栈顶元素(top)、判断栈是否为空(empty)、获取栈的大小(size)等。

3. 栈的实现方式栈可以使用数组或链表来实现。

•数组实现:使用数组来存储栈中的元素,通过一个指针来指示栈顶元素的位置。

入栈操作将元素添加到数组的末尾,出栈操作将指针向前移动一位。

•链表实现:使用链表来存储栈中的元素,每个节点包含一个数据元素和一个指向下一个节点的指针。

入栈操作将新元素插入链表的头部,出栈操作将头节点移除。

4. 栈的应用场景栈在计算机科学中有许多应用场景,以下是一些常见的应用场景。

•函数调用栈:在函数调用时,参数、局部变量和返回地址等信息会被压入栈中,函数返回时再从栈中弹出这些信息。

•表达式求值:栈可以用于解析和计算数学表达式,如中缀表达式的转换和后缀表达式的计算。

•括号匹配:栈可以用于检查表达式中的括号是否匹配,如圆括号、方括号和花括号等。

•浏览器的前进和后退功能:浏览器使用栈来记录用户访问的网页历史,通过栈的出栈和入栈操作实现前进和后退功能。

5. 栈的复杂度分析栈的入栈和出栈操作都只涉及到栈顶元素,所以这两个操作的时间复杂度都是O(1)。

而获取栈顶元素、判断栈是否为空和获取栈的大小等操作也都可以在O(1)时间内完成。

6. 总结栈是一种非常重要的数据结构,具有广泛的应用场景。

它的特点是“后进先出”,支持入栈和出栈等基本操作。

栈可以使用数组或链表来实现,常见的应用场景包括函数调用栈、表达式求值、括号匹配和浏览器的前进后退功能等。

栈和队列数据结构的特点

栈和队列数据结构的特点

栈和队列数据结构的特点栈和队列是常用的数据结构,它们在程序设计和算法实现中有着重要的作用。

下面将分别介绍栈和队列的特点。

一、栈(Stack)的特点:1.先进后出(FILO):栈是一种只允许在栈顶进行插入和删除操作的线性数据结构。

元素的插入和删除都只能在栈顶进行,最后插入的元素是第一个被删除的元素。

2.后进先出(LIFO):栈中最后一个进栈的元素是第一个出栈的元素。

3.只能在栈顶进行操作:栈的操作局限于栈顶,在栈顶可以执行的操作有入栈和出栈操作,其他位置的元素无法直接访问和操作。

4.压入和弹出操作:在栈中,我们只能在栈的一端(通常是栈顶)进行数据的插入和删除操作,分别称为“压入”和“弹出”。

5.递归的应用:栈的结构特点使得它在递归算法的实现中非常有用。

递归函数调用时,每次进入一层递归都需要保存当前的状态,包括参数、局部变量等信息,在递归返回时再恢复状态。

6.存储空间的限制:栈的存储空间是有限的,当栈的元素数量超过了栈的容量时,就会发生栈溢出错误。

7.实现方式:栈可以使用数组或链表来实现。

栈的典型应用场景包括函数调用、表达式求值、括号匹配、迷宫求解等。

二、队列(Queue)的特点:1.先进先出(FIFO):队列是一种只允许在队尾插入操作,在队头删除操作的线性数据结构。

最先插入的元素是第一个被删除的元素,最后插入的元素是最后被删除的元素。

2.队头和队尾操作:队列的操作局限于队头和队尾,在队头可以执行的操作有删除,称为“出队”操作;在队尾可以执行的操作有插入,称为“入队”操作。

3.可用空间有限:队列的存储空间是有限的,当队列的元素数量超过了队列的容量时,就会无法再插入新的元素,即发生队列溢出错误。

4.实现方式:队列可以使用数组或链表来实现。

若使用链表实现的队列,可实现动态调整队列的大小。

队列的典型应用场景包括多线程任务调度、缓冲队列、消息队列等。

栈和队列都是特殊的线性数据结构,它们各自的特点使它们在不同的应用场景下得到广泛的应用。

栈的总结以及体会

栈的总结以及体会

栈的总结以及体会
栈是一种常用的数据结构,常用于程序的调用栈、表达式求值、深度优先搜索等场景。

栈的特点是先进后出,只允许在栈顶进行操作。

以下是对栈的总结和体会:
1. 实现方式:栈可以通过数组或链表来实现。

数组实现简单,但需要指定固定大小;链表实现可以动态调整大小,但需要额外的内存空间来保存指针信息。

2. 基本操作:栈的基本操作包括入栈(push)、出栈(pop)、获取栈顶元素(top)、判空(isEmpty)等。

操作的时间复杂
度均为O(1)。

3. 应用场景:栈在计算机科学中有广泛的应用。

例如,程序调用栈用于存储函数的局部变量和返回地址;表达式求值中使用栈来转换中缀表达式为后缀表达式,并利用后缀表达式进行运算;深度优先搜索中使用栈来维护待访问的节点。

4. 栈的优点:由于栈的特点,它在某些场景下能够提供高效的解决方案。

例如,在递归算法中,通过使用栈来保存递归的中间结果,可以避免递归的重复计算,提升算法的性能;在编译器的语法分析阶段,可以使用栈来验证括号的匹配情况,确保代码的正确性。

5. 栈的缺点:栈的大小一般是有限制的,当数据量超过栈的容量时,会导致栈溢出。

此外,由于栈是一个内存上的顺序结构,数据的存储是连续的,对于大型数据结构,可能会出现内存分
配不足的问题。

总而言之,栈是一种简单、高效的数据结构,广泛应用于计算机科学的各个领域。

熟练掌握栈的基本操作和相关应用场景,能够帮助我们更好地理解和解决实际问题。

栈和队列先进先出和后进先出的数据结构

栈和队列先进先出和后进先出的数据结构

栈和队列先进先出和后进先出的数据结构栈和队列是常用的数据结构,它们分别以先进先出(FIFO)和后进先出(LIFO)的方式来组织和管理数据。

在许多编程语言中,栈和队列被广泛应用于解决各种问题。

本文将从定义、特点、应用和实现这几个方面来介绍栈和队列。

一、定义栈(Stack)是一种只允许在固定一端进行插入和删除操作的线性数据结构。

这一端被称为栈顶,而另一端被称为栈底。

栈的特点是先进后出。

队列(Queue)是一种先进先出的线性数据结构,允许在一端进行插入操作,而在另一端进行删除操作。

插入操作在队列的尾部进行,删除操作则在队列的头部进行。

二、特点2.1 栈的特点(1)插入和删除操作只能在栈顶进行,保证数据的顺序。

(2)栈是一种后进先出(LIFO)的数据结构,也就是最后插入的元素最先被删除。

(3)栈只能在栈顶进行插入和删除操作,不允许在中间或者底部进行操作。

2.2 队列的特点(1)插入操作只能在队列的尾部进行,保证数据的顺序。

(2)删除操作只能在队列的头部进行,始终删除最先插入的元素。

(3)队列是一种先进先出(FIFO)的数据结构,也就是最先插入的元素最早被删除。

三、应用3.1 栈的应用(1)函数调用和递归:栈被用于保存函数调用时的局部变量和返回地址。

(2)表达式求值:使用栈来实现中缀表达式转换为后缀表达式,然后计算结果。

(3)括号匹配:通过栈检查括号是否配对合法。

(4)浏览器的前进和后退:把浏览器的访问记录保存在栈中,方便前进和后退操作。

3.2 队列的应用(1)任务调度:使用队列管理任务,在现有任务执行完毕后按照先后顺序执行新任务。

(2)缓存管理:常用的缓存淘汰策略是先进先出,即最早进入缓存的数据最早被淘汰。

(3)消息队列:实现进程间的异步通信,提高系统的并发性和可扩展性。

(4)打印队列:打印任务按照先后顺序排队执行,保证打印的顺序。

四、实现栈和队列可以通过数组或链表来实现。

使用数组实现的栈和队列称为顺序栈和顺序队列,而使用链表实现的栈和队列称为链式栈和链式队列。

信息学奥赛知识点(十二)—栈和队列

信息学奥赛知识点(十二)—栈和队列

栈和队列是信息学竞赛中经常涉及的数据结构,它们在算法和程序设计中有着广泛的应用。

掌握栈和队列的基本原理和操作方法,对于参加信息学竞赛的同学来说是非常重要的。

本文将深入探讨栈和队列的相关知识点,帮助大家更好地理解和掌握这两种数据结构。

一、栈的定义与特点栈是一种先进后出(LIFO)的数据结构,它的特点是只允许在栈顶进行插入和删除操作。

栈可以用数组或链表来实现,常见的操作包括压栈(push)、出栈(pop)、获取栈顶元素(top)等。

栈的应用非常广泛,比如在计算机程序中,函数的调用和返回值的存储就是通过栈来实现的。

二、栈的基本操作1. 压栈(push):将元素压入栈顶2. 出栈(pop):将栈顶元素弹出3. 获取栈顶元素(top):返回栈顶元素的值,但不把它从栈中移除4. 判空:判断栈是否为空5. 获取栈的大小:返回栈中元素的个数三、栈的应用1. 括号匹配:利用栈来检查表达式中的括号是否匹配2. 表达式求值:利用栈来实现中缀表达式转换为后缀表达式,并进行求值3. 迷宫求解:利用栈来实现迷宫的路径搜索4. 回溯算法:在深度优先搜索和递归算法中,通常会用到栈来保存状态信息四、队列的定义与特点队列是一种先进先出(FIFO)的数据结构,它的特点是只允许在队尾进行插入操作,在队首进行删除操作。

队列同样可以用数组或链表来实现,常见的操作包括入队(enqueue)、出队(dequeue)、获取队首元素(front)、获取队尾元素(rear)等。

队列在计算机领域也有着广泛的应用,比如线程池、消息队列等都可以用队列来实现。

五、队列的基本操作1. 入队(enqueue):将元素插入到队列的末尾2. 出队(dequeue):从队列的头部删除一个元素3. 获取队首元素(front):返回队列的头部元素的值4. 获取队尾元素(rear):返回队列的尾部元素的值5. 判空:判断队列是否为空6. 获取队列的大小:返回队列中元素的个数六、队列的应用1. 广度优先搜索算法(BFS):在图的搜索中,通常会用队列来实现BFS算法2. 线程池:利用队列来实现任务的调度3. 消息队列:在分布式系统中,常常会用队列来进行消息的传递4. 最近最少使用(LRU)缓存算法:利用队列实现LRU缓存淘汰在信息学竞赛中,栈和队列的相关题目经常出现,并且有一定的难度。

栈的共享数据结构

栈的共享数据结构

栈的共享数据结构栈(Stack)是一种数据结构,用于在计算机科学中管理和组织数据。

它遵循先进后出(LIFO)的原则,即最后进入堆栈的元素最先出来。

栈可以通过数组或链表实现,但无论如何实现,栈都具有一些共享的数据结构。

在栈的实现中,通常有两个主要操作:push(入栈)和pop(出栈)。

push操作将一个元素添加到栈的顶部,而pop操作则从栈顶移除元素。

此外,栈还有一个peek操作,它返回栈顶的元素,但不对栈做任何修改。

对于栈的实现,有两种常见的共享数据结构,它们是数组和链表。

1.数组实现栈:数组实现栈时,最简单的方式是使用固定大小的数组。

我们需要一个指针来跟踪栈顶元素在数组中的位置。

当执行push操作时,只需将元素添加到当前栈顶指针的下一个位置,并更新栈顶指针。

当执行pop操作时,只需将栈顶指针向下移一位,并返回该位置的元素。

限制性固定大小的数组实现栈的一个问题是可能会溢出。

当栈已满时,尝试push新元素将导致溢出。

为了解决这个问题,通常我们会使用动态大小的数组。

当栈满时,我们会先创建一个更大的数组,并将现有元素复制到新数组,然后继续push新元素。

数组实现栈的优点是简单且常数时间(O(1))的push和pop操作。

然而,其缺点是动态分配内存时可能会导致性能下降。

2.链表实现栈:链表实现栈的常见方法是使用单链表。

我们需要一个指针来跟踪栈顶元素,同时该指针将指向单链表的头。

当执行push操作时,我们只需要创建一个新节点,并将其插入到链表的头部。

当执行pop操作时,只需将栈顶指针指向下一个节点,并返回当前节点的值。

链表实现栈的优点是可以动态添加节点而无需担心溢出问题。

除此之外,链表实现栈的内存分配比数组实现更灵活,因为它只在需要时分配新节点。

与数组实现相比,链表实现栈的缺点是可能导致内存分配更频繁,并且每个节点需要存储额外的指针。

除了数组和链表之外,栈的共享数据结构还可以使用动态数组(Vector)或双链表(Double Linked List)等其他数据结构来实现。

栈的概念理解

栈的概念理解

栈的概念理解栈是一种数据结构,它是一种特殊的线性表,只能在表的一端进行插入和删除操作,该一端被称为栈顶,另一端被称为栈底。

栈的特点是后进先出(Last In First Out, LIFO)。

在栈中,最后插入的元素最先弹出,而最先插入的元素最后弹出。

这就好像是一堆盘子,你只能在最上面放盘子和拿盘子,不能随意放在下面的盘子上。

栈的这种特性使得它非常适合解决一些具有“倒序”需求的问题。

栈的基本操作包括入栈和出栈。

入栈(Push)是指将元素放入栈顶;出栈(Pop)是指从栈顶弹出元素。

除此之外,还有一些常用的操作,比如获取栈顶元素(Top)、判断栈是否为空(Empty)、获取栈中元素的个数(Size)等。

栈的实现可以用数组或链表来完成。

使用数组实现的栈叫作顺序栈,使用链表实现的栈叫作链式栈。

对于顺序栈,我们需要定义一个数组和一个整数来表示栈。

数组用于存储栈中的元素,整数用于记录栈顶元素的下标。

一开始,栈为空,栈顶下标可以初始化为-1。

插入元素时,需要判断栈是否已满,如果已满则无法插入;如果未满,将元素放入栈顶,同时栈顶下标加1。

删除元素时,需要判断栈是否为空,如果为空则无法删除;如果不为空,将栈顶元素弹出,并将栈顶下标减1。

对于链式栈,我们需要定义一个结构体来表示栈中的节点。

节点包括一个数据域和一个指向下一个节点的指针域。

和顺序栈类似,链式栈也需要一个指针来表示栈顶元素。

插入元素时,需要创建一个新节点,并将栈顶指针指向该节点,新节点的指针域指向原来的栈顶元素。

删除元素时,需要判断栈是否为空,如果为空则无法删除;如果不为空,将栈顶节点删除,并将栈顶指针指向下一个节点。

栈的应用非常广泛。

在计算机科学中,栈是一种重要的数据结构,它被用于实现函数调用、表达式求值、编译器的语法分析、操作系统的进程管理等。

在编程中,我们可以使用栈来解决一些具有“倒序”性质的问题,比如字符串反转、括号匹配、计算逆波兰表达式等。

此外,栈还被用于图的深度优先搜索(DFS)算法中的节点遍历顺序。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数据结构中栈的介绍1.栈的概念栈(Stack)是一种特殊的表,这种表只在表的一端进行插入和删除操作。

允许插入和删除数据元素的这一端称为栈顶;而另一固定的一端称为栈底。

不含任何元素的栈称为空栈。

栈的修改是按后进先出的原则进行的。

栈又称为后进先出(Last In First Out)表,简称为LIFO表。

如图1所示:假设一个栈S中的元素为a n,a n-1,..,a1,则称a1为栈底元素,a n为栈顶元素。

图1 图 22.栈的存储与操作由于栈是一个特殊的表,可以用一维数组来实现栈。

同时设立指针t(称为栈顶指针)来指示栈顶元素的当前位置。

我们用一个数组s[1..m]来表示一个栈时,将栈底固定在数组的底部,即s[1]为最早入栈的元素,并让栈向数组上方(下标增大的方向)扩展。

当t=0时,表示这个栈为一个空栈。

当t=m时,表示这个栈已满。

可以用下列方式定义栈:constm=栈表目数的上限;typestack=array[1..m] of stype; {栈的数据类型}vars:stack;t:integer; {栈顶指针}进栈、出栈操作的过程和函数(假设栈元素的数据类型为整型):(1)进栈过程(push)①若t≥m时,则给出溢出信息,作出错处理(进栈前首先检查栈是否已满,满则溢出;不满则作②);②置t=t+1(栈指针加1,指向进栈地址);③S(t)=x,结束(x为新进栈的元素);procedure push(var s:stack; x:integer;var t:integer);beginif t=m then writeln('overflow')elsebegint:=t+1;s[t]:=x;endend;(2)退栈函数(pop)①若t≤0,则给出下溢信息,作出错处理(退栈前先检查是否已为空栈,空则下溢;不空则作②);②x=s(t),(退栈后的元素赋给x);③t=t-1,结束(栈指针减1,指向栈顶)。

function pop(var s:stack;var t:integer):integer;beginif t=0 then writeln('underflow')elsebeginpop:=s[t];t:=t-1;endend;(3)读栈顶元素函数(top)function top(var s:stack; t:integer):integer;beginif t=0 then writeln('underflow')elsetop:=s[t];end;3栈的应用举例【例10-4】.设栈S的初始状态为空,元素a,b,c,d,e,f,g依次入栈,以下出栈序列不可能出现的是()。

A.a,b,c,e,d,f,gB.b,c,a,f,e,g,dC.a,e,d,c,b,f,gD.d,c,f,e,b,a,gE.g,e,f,d,c,b,a题解:此题可以采用模拟的方法,依次判断各个选项是否能出现。

如A,每个元素依次进栈然后出栈,即得到此序列。

再来看B,a,b进栈,然后b出栈,c进栈后出栈,a出栈,d,e,f进栈,f,e出栈,g进栈后出栈,d出栈,可以满足。

依此类推,发现只有E不能满足,答案为E。

【例10-5】.如下图,有一个无穷大的的栈S,在栈的右边排列着1,2,3,4,5共五个车厢。

其中每个车厢可以向左行走,也可以进入栈S让后面的车厢通过。

现已知第一个到达出口的是3号车厢,请写出所有可能的到达出口的车厢排列总数。

出口←←S↓题解:首先必是1,2,3进栈,然后3出栈,此时栈中有元素1,2,未进栈元素有4,5。

我们可以分情况讨论,由于2一定在1之前出栈,我们可以讨论4,5的出栈顺序,如下:(1)4先出栈:此时相当于4,5不经过栈直接到出口。

相当于1,2,4,5四个数字的一个/4=6(种)。

排列,2排在1前,4排在5前,共有种数44(2)5先出栈:此时4和5的出栈顺序必连续,有以下三种排列:5 4 2 1;2 5 4 1;2 1 5 4。

综上所述,总的排列数是9种。

【例1】计算后缀表达式题目描述数学上使用的是中缀表达式,在中缀表达式中需要使用括号来改变运算的优先级。

事实上我们可以用后缀表达式或前缀表达式,这两种表达式里就可以完全避免使用括号。

后缀表达式:运算符放在两个运算对象之后。

所有计算按运算符出现的顺序,由左而右进行。

例如:3*(5-2)+7 对应的后缀表达式为3.5.2.- *7.+@现有一后缀表达式,让你求表达式的值,已知运算符仅限于"+","-","*","/"四种运算。

输入@表示表达式结束,’.’为操作数的结束符。

如:后缀表达式3.5.2.- *7.+@的值为16。

输入一个后缀表达式,无需判错,“/”作为整除运算。

输出后缀表达式的值,一个整数。

参考程序:program ex10_6;constn=30;typestack=array[1..n] of integer;vars:stack;a:string;t,i,j,k,q:integer;procedure push(var s:stack; x:integer;var top:integer);beginif top=n then writeln('overflow')elsebegintop:=top+1;s[top]:=x;endend;function pop(var s:stack;var top:integer):integer;beginif top=0 then writeln('underflow')elsebeginpop:=s[top];top:=top-1;endend;begini:=1; t:=0;readln(a);while a[i]<>'@' dobegincase a[i] of'0'..'9' :begink:=0;repeatk:=10*k+ord(a[i])-ord('0');i:=i+1;until a[i]='.' ;push(s,k,t); {数字进栈}end;'+' : push(s,pop(s,t)+pop(s,t),t); {取栈首的两个数值相加}'-' :beginj:=pop(s,t);push(s,pop(s,t)-j,t);end;'*' : push(s,pop(s,t)*pop(s,t),t); { 取栈首的两个数值相乘}'/' :beginj:=pop(s,t);push(s,pop(s,t) div j,t);end;end;i:=i+1;end;q:=pop(s,t); {最后栈中的元素即为答案}writeln(q);end.【例2】背包问题假设有n件质量为w1,w2,...,w n的物品和一个最多能装载总质量为T的背包,能否从这n件物品中选择若干件物品装入背包,使得被选物品的总质量恰好等于背包所能装载的最大质量,即w i1+w i2+...+w ik=T。

若能,则背包问题有解,否则无解。

算法思想首先将n件物品排成一列,依次选取;若装入某件物品后,背包内物品的总质量不超过背包最大装载质量时,则装入(进栈);否则放弃这件物品的选择,选择下一件物品试探,直至装入的物品总和正好是背包的最大转载质量为止。

这时我们称背包装满。

若装入若干物品的背包没有满,而且又无其他物品可以选入背包,说明已装入背包的物品中有不合格者,需从背包中取出最后装入的物品(退栈),然后在未装入的物品中挑选,重复此过程,直至装满背包(有解),或无物品可选(无解)为止。

具体实现设用数组w[1..N],stack[1..N]分别存放物品重量和已经装入背包(栈)的物品序号,tot表示背包的剩余最大装载量。

每进栈一个物品,就从tot中减去该物品的质量,设i为待选物品序号,若tot-w[i]>=0,则该物品可选;若tot-w[i] < 0,则该物品不可选,且若I=n,则需退栈,若此时栈空,则说明无解。

参考程序:program ex10_7;var t,n,i:integer;w,stack:array[1..100]of integer;function knap(tot:integer):boolean;vari,top:integer;begintop:=0;i:=1;while (tot>0)and (i<=n)dobeginif (tot-w[i]>=0)and(i<=n) then begintop:=top+1;stack[top]:=i;tot:=tot-w[i];end;if tot=0 then exit(true){正好装满则返回true}else beginif (i=n)and(top>0)then{I=n时退栈}begini:=stack[top];dec(top);tot:=tot+w[i];if (i=n)and(top>0) thenbegin{如退栈后I=n,即退栈的元素是最后一个,则需再次退栈,因为此时已无法选择下一个物品}i:=stack[top];dec(top);tot:=tot+w[i];end;end;inc(i);end;end;exit(false);end;beginreadln(t,n);for i:=1 to n do read(w[i]);if knap(t) then writeln('Yes')else writeln('No');end.。

相关文档
最新文档