数据结构链表c++实现

合集下载

c语言中linklist类型

c语言中linklist类型

c语言中linklist类型LinkList类型是C语言中常用的数据结构之一,它是一种线性链表的实现方式。

在计算机科学中,链表是一种常见的数据结构,用于存储和操作一系列元素。

链表由一系列节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。

链表中的第一个节点称为头节点,最后一个节点称为尾节点。

链表可以根据需要动态地增加或删除节点,相比于数组,链表的大小可以根据实际需求进行调整。

链表的实现可以使用不同的方式,其中最常见的是单向链表。

在单向链表中,每个节点只有一个指针,指向下一个节点。

这种实现方式简单且高效,适用于大多数场景。

除了单向链表,还有双向链表和循环链表等其他实现方式。

链表的优点是可以快速在任意位置插入或删除节点,而无需移动其他节点。

这是由于链表中的节点通过指针相互连接,而不是像数组那样连续存储。

另外,链表的大小可以根据需要进行动态调整,而数组的大小是静态的。

这使得链表在处理动态数据集合时非常有用。

然而,链表也有一些缺点。

首先,访问链表中的任意节点都需要从头节点开始遍历,直到找到目标节点。

这导致了链表的访问时间复杂度为O(n),而数组的访问时间复杂度为O(1)。

其次,链表需要额外的内存空间来存储指针信息,这会占用更多的存储空间。

在C语言中,可以使用结构体来定义链表节点,例如:```typedef struct Node {int data;struct Node *next;} Node;typedef struct LinkedList {Node *head;Node *tail;} LinkedList;```上述代码定义了一个包含数据和指针的节点结构体Node,以及一个包含头节点和尾节点指针的链表结构体LinkedList。

通过这样的定义,可以方便地进行链表的操作,比如插入、删除和遍历等。

链表的插入操作可以分为三步:创建新节点、修改指针、更新链表的头尾指针。

例如,插入一个新节点到链表末尾的代码如下:```void insert(LinkedList *list, int data) {Node *newNode = (Node *)malloc(sizeof(Node));newNode->data = data;newNode->next = NULL;if (list->head == NULL) {list->head = newNode;list->tail = newNode;} else {list->tail->next = newNode;list->tail = newNode;}}```链表的删除操作也类似,可以分为三步:找到目标节点、修改指针、释放内存。

《数据结构(C语言版)》严蔚敏代码实现———链表

《数据结构(C语言版)》严蔚敏代码实现———链表

《数据结构(C语⾔版)》严蔚敏代码实现———链表⼀、前⾔哈喽,⼤家好~我是熊⼦q,我⼜来了!他来了他来了,他带着代码过来了!今天要分享的代码是链表!快快搬着⼩板凳!⼆、代码严奶奶的书中预定义了⼀些预定义常量和类型,⼤家可以 新建⼀个y.h⽂件粘贴以下内容, 然后再去复制代码哦。

y.h⽂件内容:/*** 严奶奶书中的预定义常量和类型**///函数结果状态代码#define TRUE 1 //成功#define FALSE 0 //失败#define OK 1 //成功#define ERROR 0 //错误#define INFEASIBLE -1 //不可实⾏#define OVERFLOW -2 //溢出//Status 是函数的类型,其值是函数结果状态代码typedef int Status;链表LinkList.cpp:#include "y.h"#include <iostream>#include <cstdlib>#include <cstdio>using namespace std;typedef int ElemType;/*** 严奶奶单链表的实现* by 熊⼦q 2021.2.1**/typedef struct LNode{ElemType data;struct LNode *next;}LNode,*LinkList;//获取元素Status GetElem(LinkList L, int i, ElemType &e){//L为带头结点的单链表的头指针//当第i个元素存在时,其值赋给e并返回OK,否则返回ERRORLNode *p = L->next; //p指向第⼀个结点int j = 1; //j为计数器while(p && j<i){ //寻找第i个位置p = p->next;++j;}if(!p || j>i) return ERROR; //第i个元素不存在e = p->data; //否则获取第i个元素return OK;}//插⼊元素,时间复杂度O(n)Status Insert(LinkList &L, int i, ElemType e){//在带头结点的单链表L中第i个位置之前插⼊元素eLNode *p = L;int j = 0;while(p && j<i-1){p = p->next;++j;}if(!p || j>i-1) return ERROR; //i⼩于1或者⼤于表长加1LNode *q = (LNode*)malloc(sizeof(LNode));q->data = e; //插⼊数据q->next = p->next;p->next = q;return OK;}//删除元素,时间复杂度O(n)Status ListDelete(LinkList &L, int i, ElemType e){//在带头结点的单链表L中,删除第i个元素,并由e返回其值LNode *p = L->next;int j = 1;while(p && j<i-1){p = p->next;++j;} //寻找i的前驱元素if(!(p->next) || j>i-1) return ERROR; //删除位置不合理,i元素不存在或 LNode *q = p->next; //删除第i个位置元素,并释放该结点 p->next = q->next;e = q->data;free(q);return OK;}//创建链表void CreateList(LinkList &L, int n){//逆序输⼊n个元素的值,建⽴带头结点的单链表LL = (LinkList)malloc(sizeof(LNode));L->next = NULL; //建⽴⼀个头结点printf("请输⼊数据:\n");for(int i=n;i>0;--i){LNode *p = (LNode*)malloc(sizeof(LNode));scanf("%d",&(p->data));p->next = L->next; L->next = p;}}//合并两个有序链表void MergeList(LinkList &La, LinkList &Lb, LinkList &Lc){//已知单链表La和Lb的元素按值⾮递减排列//归并La和Lb得到新的单链表Lc,Lc的元素也按值⾮递减排列LNode *pa = La->next;LNode *pb = Lb->next;LNode *pc = La; //⽤La的头结点作为Lc的头结点Lc = pc;while(pa && pb){//取⼆者中较⼤值添加到Lc中if(pa->data > pb->data){//先添加该节点为pc的后继元素,然后pc和pa指针都后移pc->next = pa; pc = pc->next; pa = pa->next;}else{pc->next = pb; pc = pc->next; pb = pb->next;}}pc->next = pa? pa: pb; //插⼊剩余段free(Lb); //释放Lb的头结点}//输出单链表void Display(LinkList &L){LNode *p = L->next;printf("单链表的内容为:");while(p){printf("%d",p->data);if(p->next) printf("->");else printf("\n");p = p->next;}}int main(){LinkList l;CreateList(l, 5);Display(l);// printf("在第%d位插⼊%d",1,123);// Insert(l, 1, 123);// Display(l);int tmp;GetElem(l, 2, tmp);printf("%d",tmp);return 0;}三、运⾏截图四、附录如果你想看其他的代码,下⾯有链接哦:。

数据结构c语言实现

数据结构c语言实现

数据结构c语言实现数据结构是计算机科学中重要的一个领域,它研究不同的数据组织方式,以及在这些数据上进行各种操作的算法。

常见的数据结构包括数组、栈、队列、链表、树、图等。

在C语言中,数据结构是通过使用结构体来实现的。

结构体是由一组数据成员组合而成的自定义数据类型,可以包含不同数据类型的数据成员。

以下是如何在C语言中实现不同的数据结构。

数组数组是数据结构中最基本的数据结构之一。

C语言中的数组定义方式如下:```int array[5];```这个代码定义了一个名为array的数组,其中有5个元素,每个元素的类型是整数。

要访问数组中的元素,可以通过下标访问:这个代码设置了数组中第一个元素的值为1。

栈栈是一种后进先出(LIFO)的数据结构。

使用C语言中的数组可以实现栈。

以下是一个简单的栈实现:```#define MAXSIZE 100int stack[MAXSIZE];int top = -1;void push(int data){if(top<MAXSIZE-1){ //判断栈是否满了stack[++top] = data; //插入数据}}int isEmpty(){return top==-1; //栈是否为空}队列链表链表是一个由节点组成的数据结构,每个节点包含一个数据成员和一个指向下一个节点的指针。

在C语言中,链表可以使用结构体和指针来实现。

以下是一个单向链表的实现:```struct node{int data;struct node *next;};struct node *head = NULL;void insert(int data){struct node *new_node = (struct node*) malloc(sizeof(struct node)); //分配内存new_node->data = data; //初始化数据new_node->next = head; //新节点指向当前头节点head = new_node; //更新头节点}void delete(int data){struct node *current_node = head; //从头节点开始查找struct node *previous_node = NULL;while(current_node!=NULL&&current_node->data!=data){ //查找节点previous_node = current_node;current_node = current_node->next;}if(current_node!=NULL){ //找到了节点if(previous_node!=NULL){ //非头节点previous_node->next = current_node->next; }else{ //头节点head = current_node->next;}free(current_node); //释放内存}}树。

哈希链表的c语言实现

哈希链表的c语言实现

哈希链表的c语言实现哈希链表的C语言实现哈希链表是一种常用的数据结构,用于存储和操作大量的数据。

它结合了哈希表和链表的特点,具有快速查找和高效插入删除的优势。

本文将介绍如何使用C语言实现哈希链表,并详细讲解其原理和操作。

一、哈希链表的原理哈希链表是通过哈希函数将数据的键映射到一个唯一的索引位置,然后使用链表来解决哈希冲突。

哈希函数可以是简单的取模运算,也可以是复杂的算法,关键在于保证映射的唯一性和均匀性。

二、哈希链表的结构在C语言中,我们可以使用结构体来定义哈希链表的节点和链表本身。

节点包含一个键值对,即存储的数据和对应的键,以及一个指向下一个节点的指针。

链表则包含一个指向第一个节点的指针。

```c// 定义哈希链表节点typedef struct Node {int key;int value;struct Node* next;} Node;// 定义哈希链表typedef struct HashTable {int size;Node** table;} HashT able;```三、哈希链表的操作1. 初始化哈希链表在初始化哈希链表时,需要指定链表的大小,并分配相应大小的内存空间。

同时,需要将每个节点的指针初始化为空。

2. 插入节点插入节点时,首先通过哈希函数计算出节点的索引位置,然后将节点插入到对应索引位置的链表中。

如果该位置已经存在节点,则将新节点插入到链表的头部。

3. 查找节点查找节点时,也需要通过哈希函数计算出节点的索引位置,然后遍历链表,找到对应的节点。

如果找到了节点,则返回节点的值;否则,返回空。

删除节点时,首先通过哈希函数计算出节点的索引位置,然后遍历链表,找到对应的节点并删除。

需要注意的是,删除节点时需要维护链表的连续性。

四、示例代码下面是一个简单的示例代码,演示了如何使用C语言实现哈希链表的初始化、插入、查找和删除操作。

```c#include <stdio.h>#include <stdlib.h>// 初始化哈希链表HashTable* initHashTable(int size) {HashTable* ht = (HashTable*)malloc(sizeof(HashTable));ht->size = size;ht->table = (Node**)malloc(sizeof(Node*) * size);for (int i = 0; i < size; i++) {ht->table[i] = NULL;}return ht;}void insertNode(HashTable* ht, int key, int value) { int index = key % ht->size;Node* newNode = (Node*)malloc(sizeof(Node)); newNode->key = key;newNode->value = value;newNode->next = ht->table[index];ht->table[index] = newNode;}// 查找节点int findNode(HashTable* ht, int key) {int index = key % ht->size;Node* cur = ht->table[index];while (cur) {if (cur->key == key) {return cur->value;}cur = cur->next;}return -1;}void deleteNode(HashTable* ht, int key) { int index = key % ht->size;Node* cur = ht->table[index];Node* pre = NULL;while (cur) {if (cur->key == key) {if (pre) {pre->next = cur->next;} else {ht->table[index] = cur->next; }free(cur);return;}pre = cur;cur = cur->next;}}int main() {HashTable* ht = initHashTable(10);insertNode(ht, 1, 10);insertNode(ht, 2, 20);insertNode(ht, 11, 30);printf("%d\n", findNode(ht, 1));printf("%d\n", findNode(ht, 2));printf("%d\n", findNode(ht, 11));deleteNode(ht, 2);printf("%d\n", findNode(ht, 2));free(ht->table);free(ht);return 0;}```五、总结本文介绍了哈希链表的C语言实现,并详细讲解了其原理和操作。

c语言单链表头插法实现链表逆置

c语言单链表头插法实现链表逆置

c语言单链表头插法实现链表逆置链表是一种常用的数据结构,它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。

在C语言中,我们可以使用单链表来实现各种操作,如插入、删除和查找等。

本文将介绍如何使用头插法实现链表的逆置。

首先,我们需要定义一个链表节点的结构体,包含数据和指向下一个节点的指针。

代码如下:```ctypedef struct Node {int data;struct Node* next;} Node;```接下来,我们需要实现链表的创建和逆置函数。

首先,创建一个空链表,并将头节点指针指向NULL。

代码如下:```cNode* createList() {Node* head = NULL;return head;}```然后,我们可以实现链表的插入函数,使用头插法将新节点插入到链表的头部。

代码如下:```cNode* insertNode(Node* head, int data) {Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = data;newNode->next = head;head = newNode;return head;}```接下来,我们可以实现链表的逆置函数,通过遍历链表,将每个节点插入到头部,从而实现链表的逆置。

代码如下:```cNode* reverseList(Node* head) {Node* newHead = NULL;Node* temp = NULL;while (head != NULL) {temp = head->next;head->next = newHead;newHead = head;head = temp;}return newHead;}```最后,我们可以编写主函数,测试链表的逆置功能。

代码如下:```cint main() {Node* head = createList();head = insertNode(head, 1);head = insertNode(head, 2);head = insertNode(head, 3);head = insertNode(head, 4);head = insertNode(head, 5);printf("原链表:");Node* temp = head;while (temp != NULL) {printf("%d ", temp->data);temp = temp->next;}printf("\n");head = reverseList(head);printf("逆置后的链表:");temp = head;while (temp != NULL) {printf("%d ", temp->data);temp = temp->next;}printf("\n");return 0;}```运行以上代码,输出结果如下:```原链表:5 4 3 2 1逆置后的链表:1 2 3 4 5```通过以上代码,我们成功地使用C语言的单链表头插法实现了链表的逆置。

数据结构实验报告实现单链表各种基本运算的算法

数据结构实验报告实现单链表各种基本运算的算法

实验截图(1)void InitList(LinkNode *&L)//初始化线性表{L=(LinkNode *)malloc(sizeof(LinkNode)); //创建头结点L->next=NULL;//单链表置为空表}void DestroyList(LinkNode *&L)//销毁线性表{LinkNode *pre=L,*p=pre->next;实验截图(2)bool GetElem(LinkNode *L,int i,ElemType &e) //求线性表中第i个元素值{ int j=0;if (i<=0) return false;//i错误返回假LinkNode *p=L;//p指向头结点,j置为0(即头结点的序号为0) while (j<i && p!=NULL)//找第i个结点p{ j++;p=p->next;}if (p==NULL)//存在值为e的结点,返回其逻辑序号ireturn(i);}实验截图(3)bool ListInsert(LinkNode *&L,int i,ElemType e) //插入第i个元素{ int j=0;if (i<=0) return false;//i错误返回假LinkNode *p=L,*s;//p指向头结点,j置为0(即头结点的序号为0) while (j<i-1 && p!=NULL)//查找第i-1个结点p{ j++;p=p->next;}}实验截图(4)编写exp2-2.cpp程序包含有关代码//文件名:exp2-2.cpp#include "linklist.cpp"int main(){LinkNode *h;ElemType e;printf("单链表的基本运算如下:\n");printf(" (1)初始化单链表h\n");InitList(h);printf(" (2)依次采用尾插法插入a,b,c,d,e元素\n");return 1;}实验截图(5)运行得到结果实验截图(6)。

数据结构c语言版创建单链表的代码

数据结构c语言版创建单链表的代码

数据结构c语言版创建单链表的代码单链表作为常用的线性结构之一,常常用于解决以链式方式存储数据的问题。

创建单链表需要掌握一些基础的数据结构知识以及对C语言的熟练运用。

接下来,本文将分步骤地阐述数据结构C语言版创建单链表的代码。

第一步,定义单链表结构体并定义节点类型。

在C语言中,我们可以通过结构体的方式定义单链表,其中结构体中包含两个成员变量,分别为存储数据的data和指向下一个节点的指针next。

对于节点类型,我们可以使用typedef对节点类型进行定义,例如:```struct ListNode {int data;struct ListNode *next;};typedef struct ListNode ListNode;```在以上代码中,我们首先定义了一个结构体ListNode作为单链表的元素类型,其中包含存储数据的data和指向下一个元素的指针next。

接着我们使用typedef将结构体ListNode定义为仿函数ListNode,从而使其更加方便使用。

第二步,初始化单链表。

在创建单链表之前,我们需要先将单链表的头指针初始化为NULL,表示当前链表为空。

具体代码如下:```ListNode *createLinkedList() {ListNode *head = NULL;return head;}```以上代码中,函数createLinkedList用于创建并初始化单链表,其中head表示单链表头指针,我们将其初始化为NULL。

第三步,向单链表中添加元素。

在单链表中添加元素需要借助于指针的指向关系。

具体来说,我们需要先创建新的节点,将其数据添加到节点中,然后将新节点的next指针指向之前的头节点,最后将头指针指向新节点。

具体过程如下:```ListNode *addListNode(ListNode **head, int val) {ListNode *newNode = (ListNode *)malloc(sizeof(ListNode)); newNode->data = val;newNode->next = *head;*head = newNode;return *head;}```在以上代码中,函数addListNode接收一个指向头指针的指针head,以及需要添加的元素值val。

数据结构C语言版 线性表的单链表存储结构表示和实现

数据结构C语言版 线性表的单链表存储结构表示和实现

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

h>#include 〈stdlib.h>/*数据结构C语言版线性表的单链表存储结构表示和实现P28—31编译环境:Dev-C++ 4。

9。

9。

2日期:2011年2月10日*/typedef int ElemType;// 线性表的单链表存储结构typedef struct LNode{ElemType data; //数据域struct LNode *next;//指针域}LNode, *LinkList;// typedef struct LNode *LinkList;// 另一种定义LinkList的方法// 构造一个空的线性表Lint InitList(LinkList *L){/*产生头结点L,并使L指向此头结点,头节点的数据域为空,不放数据的。

void *malloc(size_t)这里对返回值进行强制类型转换了,返回值是指向空类型的指针类型.*/(*L)= (LinkList)malloc(sizeof(struct LNode) );if( !(*L))exit(0);// 存储分配失败(*L)-〉next = NULL;// 指针域为空return 1;}// 销毁线性表L,将包括头结点在内的所有元素释放其存储空间。

int DestroyList(LinkList *L){LinkList q;// 由于单链表的每一个元素是单独分配的,所以要一个一个的进行释放while(*L ){q = (*L)—〉next;free(*L );//释放*L = q;}return 1;}/*将L重置为空表,即将链表中除头结点外的所有元素释放其存储空间,但是将头结点指针域置空,这和销毁有区别哦。

不改变L,所以不需要用指针。

*/int ClearList( LinkList L ){LinkList p,q;p = L—〉next;// p指向第一个结点while( p ) // 没到表尾则继续循环{q = p—>next;free( p );//释放空间p = q;}L—>next = NULL; // 头结点指针域为空,链表成了一个空表return 1;}// 若L为空表(根据头结点L—〉next来判断,为空则是空表),则返回1,// 否则返回0.int ListEmpty(LinkList L){if(L—>next ) // 非空return 0;elsereturn 1;}// 返回L中数据元素个数。

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

实现了一些单链表操作以及几个算法,都已注明编译环境:VC6.0#ifndef COMMON //头文件common.h中的内容#define COMMON#include <stdio.h>#include <stdlib.h>#include <math.h>#include <conio.h>#include <string.h>#define OVERFLOW 0#define ERROR 0#define OK 1#define FALSE 0#define TRUE 0#define INFEASIBLE 0#define LIST_INIT_SIZE 100#define LISTINCREMENT 10typedef int Status;typedef int ElemType;/**链表存储结构**/typedef struct LNode{ElemType data;struct LNode *next;}LNode,*LinkList; //线性链表类型/******链表相关函数******/Status ListInit(LinkList &A){//初始化链表,分配头结点A=(LinkList)malloc(sizeof(LNode));A->next=NULL;A->data=0;return OK;}int ListLocate(LinkList A,ElemType x){//确定元素x在链表A中的位置,并返回所在结点位置LNode * p=A->next;int location=1;while(p!=NULL && p->data!=x){location++;p=p->next;}if(NULL==p)return ERROR;elsereturn location;}Status ListInsert(LinkList &A,ElemType x,int i){//向带头结点链表A中的i(从1开始)位置插入元素x if(i<=0)return INFEASIBLE;int k=1;LNode *p,*q=NULL;p = A;while(k<i&&p->next!=NULL){p = p->next;k++;}q = (LNode*)malloc(sizeof(LNode));q->data = x;q->next = p->next;p->next = q;return OK;}Status ListDelete(LinkList &A, int i){//删除带头结点的链表A的位置i(从1开始)处的结点if(i<=0)return INFEASIBLE;int k=1;LNode *p = A,*q = A->next;while(q!=NULL&&k<i){p=p->next;q=q->next;k++;}if(q==NULL){printf("位置%d处无结点,无法删除\n",i);return ERROR;}else{p->next=q->next;free(q);printf("删除成功\n");return OK;}}int ListLength(LinkList A){//求得带头结点单链表的长度LNode *p;int count=0;p=A->next;while(p!=NULL){p=p->next;count++;}return count;}void ListPrint(LinkList A){//打印链表所有节点LNode *p;p = A->next;while(p!=NULL){printf("%d\n",p->data);p = p->next;}}int ListCompare(LNode *p,LNode *q){//用于比较两个结点数据域大小的函数if(p->data>q->data)return 0;elsereturn 1;}Status ListSort(LinkList &A){//带头结点的单链表排序算法//利用一个简单的冒泡排序来实现LNode *q = A;LNode *max,*prior,*p,*k;while(q->next!=NULL){max=q->next;prior=max;k=max;p=max->next;while(p!=NULL){if(ListCompare(max,p)){max=p;prior=k;}k=p;p=p->next;}if(max==q->next){q=q->next;continue;}else{prior->next=max->next;max->next=q->next;q->next=max;q=q->next;}}return OK;}Status DeleteMinkMaxk(LinkList &A,int mink,int maxk){//删除递增有序单链表介于值mink与maxk之间的所有结点,并释放被删结点//时间复杂度<=o(n)LNode *p,*prior;p=A->next;prior=A;while(p!=NULL){if(p->data>mink&&p->data<maxk){prior->next=p->next;free(p);p=prior->next;}else{if(p->data<mink)break;prior=prior->next;p=prior->next;}}return OK;}Status DeleteRepeat(LinkList &A){LNode *prior=A->next,*p=A->next->next;while(p!=NULL){if(prior->data==p->data){prior->next=p->next;free(p);p=prior->next;}else{prior=prior->next;p=p->next;}}return OK;}#endif#include "common.h" //主程序main.cpp void main(){LinkList A;printf("******------******\n");//初始化一个链表并赋予处置然后打印出来ListInit(A);for(int i=1;i<10;i++)ListInsert(A,i,i);printf("链表数据:\n");ListPrint(A);printf("******------******\n");//实现单链表长度算法printf("链表长度:%d\n",ListLength(A));printf("******------******\n");//实现单链表结点定位操作printf("定位6在链表中的位置:");int locate = ListLocate(A,6);if(locate==ERROR)printf("未找到\n");elseprintf("%d\n",locate);printf("******------******\n");//单链表实现删除指定结点ListDelete(A,11);ListPrint(A);printf("******------******\n");//单链表排序算法的实现printf("将链表按照递增顺序重新排列\n");ListSort(A);ListPrint(A);printf("******------******\n");//给出mink和maxk,删除递增有序单链表中所有介于二者之间的结点,并释放被删结点printf("删除递增有序链表中介于3和8之间的结点\n");DeleteMinkMaxk(A,3,8);ListPrint(A);printf("******------******\n");//删除递增有序单链表中的重复元素for(i=1;i<10;i++)ListInsert(A,i,i);ListSort(A);ListPrint(A);printf("去掉重复元素后\n");DeleteRepeat(A);ListPrint(A);}数据结构题集(c语言版)清华大学出版社习题的大部分解法可以去我的博客查阅:/u/1776874690。

相关文档
最新文档