迪克斯特拉(Dijkstra)算法

合集下载

dijkstra算法

dijkstra算法

Dijkstra算法(Dijkstra算法)由荷兰计算机科学家Dikstra于1959年提出,因此也称为Dikstra算法。

从一个顶点到其余顶点的最短路径算法解决了权利图中的最短路径问题。

Dijestela算法的主要特征是从起点开始,采用贪婪算法的策略。

每次,它都会遍历最接近且未访问过的顶点的相邻节点,直到起点为止。

Dijkstra的算法通常以两种方式表示,一种使用永久和临时标签,另一种使用OPEN和CLOSE表,两者都使用永久和临时标签。

请注意,该算法不需要图形中的负边缘权重。

1.首先,引入一个辅助数组(向量)D,其中每个元素D代表当前找到的Dijkstra运行动画过程Dijkstra运行动画过程从起点(即源点)到其他每个顶点的长度。

例如,D = 2表示从起点到顶点3的路径的相对最小长度为2。

这里的重点是相对的,这意味着D在算法执行期间近似于最终结果,但不一定相等执行期间的长度。

2. D的初始状态为:如果存在一个从to的弧(即,存在一个从to的连接边),则D是弧上的权重(即,从to的边的权重);否则,将D设置为无穷大。

显然,长度为D = Min {D | ∈V}是从起点到顶点的最短路径,即()。

3.那么,下一个最短的长度是?即找到与从源点到下一顶点的最短路径长度相对应的顶点,并且该最短路径长度仅次于从源点到顶点的最短路径长度。

假设子短路径的终点是,则可以想象路径是()或()。

它的长度是从PI到PI的弧上的权重,或者是D加上从PI到PI的弧上的权重。

4.通常,假定S是从源点获得的最短路径长度的一组顶点,则可以证明下一条最短路径(令其终点为)是arc()或仅从源点穿过中间的S顶点,最后到达顶点。

因此,具有较短长度的下一个最短路径长度必须为D = Min {D | ∈v-s},其中D是arc()上的权重,或者D(∈S)和arc(,)上的权重之和。

该算法描述如下:1)让圆弧代表圆弧上的重量。

如果弧不存在,则将弧设置为无穷大(在这种情况下为MAXCOST)。

迪杰斯特拉算法(Dijkstra)

迪杰斯特拉算法(Dijkstra)

迪杰斯特拉算法(Dijkstra)模板⼀:时间复杂度O(n2)1int dijkstra(int s,int m) //s为起点,m为终点2 {3 memset(dist,0,sizeof(dist)); //初始化,dist数组⽤来储存s到各个点的距离4 memset(v,0,sizeof(v)); //初始化v数组,是否已标记5for(int i=1;i<=n;++i) //++i返回的是引⽤,稍微快⼀点6 {7 dist[i]=e[s][i]; //初始化,e[s][i]数组表⽰是点s到点i的权值,数组e⽤来存有向图或⽆向图的权值,⽤INF初始化8 }9for(int i=1;i<=n;++i)10 {11 k=INF,u=0;12for(int j=1;j<=n;++j) //找出当前dist最⼩的点13 {14if(!v[j]&&dist[j]<k)15 {16 k=dist[j];17 u=j;18 }19 }20 v[u]=1; //标记21for(int j=1;j<=n;++j) //更新与点u相连的点的权值和22 {23if(!v[j]&&e[u][j]<INF&&dist[u]+e[u][j]<dist[j]) //⽐较从点s到点u载到点j的权值和点s直接到点j的权值24 {25 dist[j]=dist[u]+e[u][j];26 }27 }28 }29return dist[m];30 }模板⼆:时间复杂度 O(mlogn)m为边数,n为顶点数。

对于稀疏图的效果显著,对稠密图慎⽤。

1const int INF = 0x3f3f3f3f;2const int maxn = 150;3struct Edge4 {5int from, to, dist;6 Edge(int u, int v, int d) :from(u), to(v), dist(d) {}7 };8struct HeapNode9 {10int d, u;11 HeapNode(int D,int U):d(D),u(U){}12bool operator < (const HeapNode& rhs) const {13return d > rhs.d;14 }15 };16struct Dijkstra17 {18int n, m;19 vector<Edge> edges;20 vector<int> G[maxn]; //maxn要⼤于顶点数21bool done[maxn];22int d[maxn];23int p[maxn];2425void init(int n)26 {27this->n = n;28for (int i = 0; i < n; ++i)29 G[i].clear();30 edges.clear();31 }3233void addEdge(int from, int to, int dist)34 {35 edges.push_back(Edge(from, to, dist));36 m = edges.size();37 G[from].push_back(m - 1);38 }40void dijkstra(int s, int kk) //s为起点 kk为终点41 {42 priority_queue<HeapNode> Q;43for (int i = 0; i < n; ++i)44 d[i] = INF;45 d[s] = 0;46 memset(done, 0, sizeof(done));47 Q.push(HeapNode( 0, s ));48while (!Q.empty())49 {50 HeapNode x = Q.top();51 Q.pop();52int u = x.u;53if (u == kk) //到终点就退出54 {55 printf("%d\n", x.d);56break;57 }58if (done[u])59 {60continue;61 }62 done[u] = true;63for (int i = 0; i < G[u].size(); ++i)64 {65 Edge& e = edges[G[u][i]];66if (d[u] + e.dist < d[e.to])67 {68 d[e.to] = d[u] + e.dist;69 p[e.to] = G[u][i];70 Q.push(HeapNode(d[e.to], e.to ));71 }72 }73 }74 }7576 };放⼀道模板题AC代码1 #include <iostream>2 #include <stdio.h>3 #include <cstring>4 #include <algorithm>5 #include <vector>6 #include <queue>7using namespace std;8const int INF = 0x3f3f3f3f;9const int maxn = 150;10struct Edge11 {12int from, to, dist;13 Edge(int u, int v, int d) :from(u), to(v), dist(d) {}14 };15struct HeapNode16 {17int d, u;18 HeapNode(int D,int U):d(D),u(U){}19bool operator < (const HeapNode& rhs) const {20return d > rhs.d;21 }22 };23struct Dijkstra24 {25int n, m;26 vector<Edge> edges;27 vector<int> G[maxn]; //maxn要⼤于顶点数28bool done[maxn];29int d[maxn];30int p[maxn];3132void init(int n)33 {34this->n = n;35for (int i = 0; i < n; ++i)36 G[i].clear();37 edges.clear();38 }3940void addEdge(int from, int to, int dist)42 edges.push_back(Edge(from, to, dist));43 m = edges.size();44 G[from].push_back(m - 1);45 }4647void dijkstra(int s, int kk) //s为起点 kk为终点48 {49 priority_queue<HeapNode> Q;50for (int i = 0; i < n; ++i)51 d[i] = INF;52 d[s] = 0;53 memset(done, 0, sizeof(done));54 Q.push(HeapNode( 0, s ));55while (!Q.empty())56 {57 HeapNode x = Q.top();58 Q.pop();59int u = x.u;60if (u == kk)61 {62 printf("%d\n", x.d);63break;64 }65if (done[u])66 {67continue;68 }69 done[u] = true;70for (int i = 0; i < G[u].size(); ++i)71 {72 Edge& e = edges[G[u][i]];73if (d[u] + e.dist < d[e.to])74 {75 d[e.to] = d[u] + e.dist;76 p[e.to] = G[u][i];77 Q.push(HeapNode(d[e.to], e.to ));78 }79 }80 }81 }8283 };84int n, m, x, y, k;85 Dijkstra d;86int main()87 {88while (cin>>n>>m)89 {90 d.init(n);91if (n == 0 && m == 0)92break;93while (m--)94 {95 cin >> x >> y >> k;96 d.addEdge(x - 1, y - 1, k);97 d.addEdge(y - 1, x - 1, k);98 }99 d.dijkstra(0, n - 1);100 }101return0;102 }View Code持续更新……。

迪杰斯特拉算法介绍

迪杰斯特拉算法介绍

迪杰斯特拉算法介绍迪杰斯特拉(Dijkstra)算法是典型最短路径算法,⽤于计算⼀个节点到其他节点的最短路径。

它的主要特点是以起始点为中⼼向外层层扩展(⼴度优先搜索思想),直到扩展到终点为⽌。

基本思想通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。

此外,引进两个集合S和U。

S的作⽤是记录已求出最短路径的顶点(以及相应的最短路径长度),⽽U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。

初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是"起点s到该顶点的路径"。

然后,从U中找出路径最短的顶点,并将其加⼊到S中;接着,更新U中的顶点和顶点对应的路径。

然后,再从U中找出路径最短的顶点,并将其加⼊到S中;接着,更新U中的顶点和顶点对应的路径。

... 重复该操作,直到遍历完所有顶点。

操作步骤(1) 初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为"起点s到该顶点的距离"[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。

(2) 从U中选出"距离最短的顶点k",并将顶点k加⼊到S中;同时,从U中移除顶点k。

(3) 更新U中各个顶点到起点s的距离。

之所以更新U中顶点的距离,是由于上⼀步中确定了k是求出最短路径的顶点,从⽽可以利⽤k来更新其它顶点的距离;例如,(s,v)的距离可能⼤于(s,k)+(k,v)的距离。

(4) 重复步骤(2)和(3),直到遍历完所有顶点。

单纯的看上⾯的理论可能⽐较难以理解,下⾯通过实例来对该算法进⾏说明。

迪杰斯特拉算法图解以上图G4为例,来对迪杰斯特拉进⾏算法演⽰(以第4个顶点D为起点)。

初始状态:S是已计算出最短路径的顶点集合,U是未计算除最短路径的顶点的集合!第1步:将顶点D加⼊到S中。

此时,S={D(0)}, U={A(∞),B(∞),C(3),E(4),F(∞),G(∞)}。

dijkstra最短路径 应用案例

dijkstra最短路径 应用案例

Dijkstra算法是一种用于解决图的单源最短路径问题的算法,由荷兰计算机科学家埃德斯格·迪克斯特拉提出。

该算法被广泛应用于网络路由算法、城市交通规划、通信网络等领域。

本文将从几个具体的案例出发,介绍Dijkstra最短路径算法的应用。

一、网络路由算法在现代计算机网络中,Dijkstra算法被应用于路由器之间的数据传输。

路由器之间通过Dijkstra算法计算出最短路径,以确保数据包能以最短的路径传输,从而提高网络的传输效率和稳定性。

假设有一个由多个路由器组成的网络,每个路由器之间存在多条连接线路,而每条线路都有一个权重值,代表数据传输的成本。

当一个路由器需要发送数据时,Dijkstra算法可以帮助它找到到达目的地最短且成本最小的路径。

这样,网络中的数据传输就能以最高效的方式进行,从而提升了整个网络的性能。

二、城市交通规划Dijkstra算法也被广泛应用于城市交通规划领域。

在城市交通规划中,人们通常需要找到最短路径以及最快到达目的地的方法,而Dijkstra算法正是能够满足这一需求的算法之一。

假设某城市有多条道路,每条道路都有不同的行驶时间。

当一个人需要从城市的某个地点出发到达另一个地点时,可以利用Dijkstra算法计算出最短行驶时间的路径。

这样,城市交通规划部门就可以根据这些信息对城市的交通流量进行合理分配和调度,提高城市交通的效率。

三、通信网络另一个Dijkstra算法的应用案例是在通信网络中。

通信网络通常是由多个节点和连接这些节点的线路组成的。

而节点之间的通信是通过传送数据包来实现的。

在这种情况下,Dijkstra算法可以帮助确定数据包传输的最短路径,以提高通信网络的效率和稳定性。

在一个由多个节点组成的通信网络中,当一个节点需要向另一个节点发送数据时,Dijkstra算法可以帮助确定最短路径,从而确保数据包能够以最短的路径传输到目的地。

这样一来,通信网络就能够更加稳定地进行数据传输,提高了通信网络的效率。

dijkstra算法代码实现

dijkstra算法代码实现

Dijkstra算法是一种用于解决单源最短路径问题的经典算法,由荷兰计算机科学家艾兹赫尔·迪克斯特拉在1956年提出。

该算法主要用于计算一个顶点到其余各个顶点的最短路径。

Dijkstra算法的基本思想是:假设图G中顶点集合为V,边集合为E,从源点s开始,初始时只有s的已知最短路径,用集合S记录已找到最短路径的顶点。

利用S中顶点的最短路径来更新其余顶点的最短路径,直到找到从s到其余所有顶点的最短路径。

Dijkstra算法具体实现过程如下:1. 创建两个集合,一个用来保存已找到最短路径的顶点集合S,另一个用来保存未找到最短路径的顶点集合V-S。

2. 初始化距离数组dist[],将源点到各个顶点的距离初始化为无穷大,源点到自身的距离初始化为0。

3. 从源点s开始,将s加入S集合,更新源点到其余各个顶点的距离,如果存在边(u,v),使得dist[v] > dist[u] + w(u,v),则更新dist[v] = dist[u] + w(u,v),其中w(u,v)表示边(u,v)的权值。

4. 重复第3步,直到将所有顶点加入S集合为止,此时dist数组即为源点到各个顶点的最短路径。

根据以上实现思路,我们可以使用代码来实现Dijkstra算法。

以下是Python语言的Dijkstra算法实现示例:```pythondef dijkstra(graph, src):dist = [float('inf')] * len(graph)dist[src] = 0visited = [False] * len(graph)for _ in range(len(graph)):u = min_distance(dist, visited)visited[u] = Truefor v in range(len(graph)):if graph[u][v] > 0 and not visited[v] and dist[v] > dist[u] + graph[u][v]:dist[v] = dist[u] + graph[u][v]print_solution(dist)def min_distance(dist, visited):min_dist = float('inf')min_index = -1for v in range(len(dist)):if dist[v] < min_dist and not visited[v]:min_dist = dist[v]min_index = vreturn min_indexdef print_solution(dist):print("顶点\t最短距离")for i in range(len(dist)):print(f"{i}\t{dist[i]}")```在上面的示例代码中,我们首先定义了一个dijkstra函数,该函数接受图的邻接矩阵表示和源点的索引作为参数。

堆优化的Dijkstra算法

堆优化的Dijkstra算法

堆优化的Dijkstra算法 堆优化的Dijkstra算法 迪杰斯特拉算法(Dijkstra)是由荷兰计算机于1959 年提出的,因此⼜叫狄克斯特拉算法。

是从⼀个顶点到其余各顶点的算法,解决的是有权图中最短路径问题。

迪杰斯特拉算法主要特点是从起始点开始,采⽤的,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为⽌。

这⾥的Dijkstra算法是经过堆优化的算法,⽤于解决稀疏图问题。

基本思想是通过邻接表储存每个点所能到达的点。

⽤⼀个dist[]数组来储存初始点到达每个节点的最短距离,并初始化为⽆穷⼤。

⾸先将初始点推⼊队列中,然后通过这个点来遍历每⼀个⼦节点并将每个使得初始节点到当前点的距离加上这个点到每个⼦节点的距离⼩于当前dist[]的值替换掉,并推⼊队列中,由于这⾥是⼩根堆,所取到的队⾸的值⼀定是当初始节点距离的点并开始下⼀轮遍历,以此类推直⾄找到⽬标节点的最短距离。

代码:#include<iostream>#include<cstring>#include<algorithm>#include<queue>using namespace std;typedef pair<int,int> PII;const int N=200010;int ne[N],h[N],e[N],idx;//邻接表储存⽅式int n,m;int w[N],dist[N];//w储存每条边的权值,dist储存初始节点到每⼀个节点的最短距离bool st[N];//储存每个点是否找到了最短距离priority_queue<PII,vector<PII>,greater<PII>> heap;//⼩根堆的定义void insert(int a,int b,int c){e[idx]=b;w[idx]=c;//储存权值ne[idx]=h[a];h[a]=idx++;}int dijkstra(){memset(dist,0x3f,sizeof dist);//将距离初始初始化为正⽆穷heap.push({0,1});//将初始节点推⼊队列中while(!heap.empty()){auto t=heap.top();heap.pop();int cnt=t.second,distance=t.first;//t.first代表当前节点到初始节点的最短距离,second代表当前是哪⼀个节点if(st[cnt]) continue;st[cnt]=true;for(int i=h[cnt];i!=-1;i=ne[i])//遍历这个链表{int j=e[i];if(dist[j]>distance+w[i])//如果当前节点到⼦节点的距离加上这条边的权值⼩于之前头结点到⼦节点的距离{dist[j]=distance+w[i];heap.push({dist[j],j});}}}if(dist[n]!=0x3f3f3f3f) return dist[n];return -1;}int main(){scanf("%d%d",&n,&m);memset(h,-1,sizeof h);while(m--){int a,b,c;scanf("%d%d%d",&a,&b,&c);insert(a,b,c);}printf("%d",dijkstra());return0;}。

迪杰斯特拉(dijkstra)算法

迪杰斯特拉(dijkstra)算法

初 始 时
dist path
1 0 C1
2 4 C1,C2
3 8 C1,C3
4 maxint
5 maxint
6 maxint
第一次:选择m=2,则s=[c1,c2],计算比较dist[2]+GA[2,j]与dist[j]的大小(3<=j<=6)dist path源自1 02 43 7
4 8
5 10
6 maxint
求从C1到各顶点的最短路径
9 4 2
C3
C6
4 2 6
C5
C4
4 3
8
C2
4
C1
Procedure dijkstra(GA,dist,path,i); {表示求Vi到图G中其余顶点的最短路
径,GA为图G的邻接矩阵,dist和path为变量型参数,其中path的基类型为集合} begin for j:=1 to n do begin {初始化} if j<>i then s[j]:=0 else s[j]:=1; dist[j]:=GA[i,j]; if dist[j]<maxint then path[j]:=[i]+[j] else path[j]:=[ ]; end; for k:=1 to n-2 do begin w:=maxint; m:=i; for j:=1 to n do {求出第k个终点Vm} if (s[j]=0) and (dist[j]<w) then begin m:=j;w:=dist[j];end; if m<>i then s[m]:=1 else exit; {若条件成立,则把Vm加入到s中,否则 退出循环,因为剩余的终点,其最短路径长度均为maxint,无需再计算下去}

Dijkstra算法(狄克斯特拉算法)

Dijkstra算法(狄克斯特拉算法)

Dijkstra 算法Dijkstra 算法(狄克斯特拉算法) 算法(狄克斯特拉算法)目录[隐藏]• • • • • o •1 2 3 4 5Dijkstra 算法概述 算法描述 虚拟码 时间复杂度 Dijkstra 算法案例分析 5.1 案例一:基于 Dijkstra 算法在物流配送中的应用[1] 6 参考文献[编辑]Dijkstra 算法概述Dijkstra 算法 算法是由荷兰计算机科学家狄克斯特拉(Dijkstra)于 1959 年提出的,因此 又叫狄克斯特拉算法。

是从一个顶点到其余各顶点的最短路径算法, 解决的是有向图中最短 路径问题。

其基本原理是:每次新扩展一个距离最短的点,更新与其相邻的点的距离。

其基本原理是:每次新扩展一个距离最短的点,更新与其相邻的点的距离。

当所有边 权都为正时,由于不会存在一个距离更短的没扩展过的点, 权都为正时,由于不会存在一个距离更短的没扩展过的点,所以这个点的距离永远不会再 被改变,因而保证了算法的正确性。

不过根据这个原理, 被改变,因而保证了算法的正确性。

不过根据这个原理,用 Dijkstra 求最短路的图不能有 负权边,因为扩展到负权边的时候会产生更短的距离,有可能就破坏了已经更新的点距离 负权边,因为扩展到负权边的时候会产生更短的距离,有可能就破坏了已经更新的点距离 不会改变的性质。

不会改变的性质。

举例来说,如果图中的顶点表示城市,而边上的权重表示著城市间开车行经的距离。

Dijkstra 算法可以用来找到两个城市之间的最短路径。

Dijkstra 算法的输入包含了一个有权重的有向图 G,以及 G 中的一个来源顶点 S。

我 们以 V 表示 G 中所有顶点的集合。

每一个图中的边,都是两个顶点所形成的有序元素对。

(u,v)表示从顶点 u 到 v 有路径相连。

我们以 E 所有边的集合,而边的权重则由权重函数 w: E → [0, ∞]定义。

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

两个指定顶点之间的最短路径
问题如下:给出了一个连接若干个城镇的铁路网络,在这个网络的两个指定城镇间,找一条最短铁路线。

以各城镇为图G 的顶点,两城镇间的直通铁路为图G 相应两顶点间的边,得图G 。

对G 的每一边e ,赋以一个实数)(e w —直通铁路的长度,称为e 的权,得到赋权图G 。

G 的子图的权是指子图的各边的权和。

问题就是求赋权图G 中指定的两个顶点00,v u 间的具最小权的轨。

这条轨叫做00,v u 间的最短路,
它的权叫做00,v u 间的距离,亦记作),(00v u d 。

求最短路已有成熟的算法:迪克斯特拉(Dijkstra )算法,其基本思想是按距0u 从近到远为顺序,依次求得0u 到G 的各顶点的最短路和距离,直至0v (或直至G 的所有顶点),算法结束。

为避免重复并保留每一步的计算信息,采用了标号算法。

下面是该算法。

(i) 令0)(0=u l ,对0u v ≠,令∞=)(v l ,}{00u S =,0=i 。

(ii) 对每个i S v ∈(i i S V S \=),用
)}()(),({min uv w u l v l i
S u +∈ 代替)(v l 。

计算)}({min v l i
S v ∈,把达到这个最小值的一个顶点记为1+i u ,令}{11++=i i i u S S 。

(iii). 若1||-=V i ,停止;若1||-<V i ,用1+i 代替i ,转(ii)。

算法结束时,从0u 到各顶点v 的距离由v 的最后一次的标号)(v l 给出。

在v 进入i S 之前的标号)(v l 叫T 标号,v 进入i S 时
的标号)(v l 叫P 标号。

算法就是不断修改各项点的T 标号,直至获得P 标号。

若在算法运行过程中,将每一顶点获得P 标号所由来的边在图上标明,则算法结束时,0u 至各项点的
最短路也在图上标示出来了。

例9 某公司在六个城市621,,,c c c 中有分公司,从i c 到j c 的直接航程票价记在下述矩阵的),(j i 位置上。

(∞表示无直接航路),请帮助该公司设计一张城市1c 到其它城市间的票价最
便宜的路线图。

⎥⎥⎥⎥⎥⎥⎥⎦⎤⎢⎢⎢⎢⎢⎢⎢⎣⎡∞∞∞∞∞∞
05525251055010202525100102040
2010015252015050102540500 用矩阵n n a ⨯(n 为顶点个数)存放各边权的邻接矩阵,行向量pb 、1index 、2index 、d 分别用来存放P 标号信息、标号顶点顺序、标号顶点索引、最短通路的值。

其中分量
⎩⎨⎧=顶点未标号
当第顶点已标号当第i i i pb 01)(; )(2i index 存放始点到第i 点最短通路中第i 顶点前一顶点的序号;
)(i d 存放由始点到第i 点最短通路的值。

求第一个城市到其它城市的最短路径的Matlab 程序如下:
clear;
clc;
M=10000;
a(1,:)=[0,50,M,40,25,10];
a(2,:)=[zeros(1,2),15,20,M,25];
a(3,:)=[zeros(1,3),10,20,M];
a(4,:)=[zeros(1,4),10,25];
a(5,:)=[zeros(1,5),55];
a(6,:)=zeros(1,6);
a=a+a';
pb(1:length(a))=0;pb(1)=1;index1=1;index2=o nes(1,length(a));
d(1:length(a))=M;d(1)=0;temp=1;
while sum(pb)<length(a)
tb=find(pb==0);
d(tb)=min(d(tb),d(temp)+a(temp,tb));
tmpb=find(d(tb)==min(d(tb)));
temp=tb(tmpb(1));
pb(temp)=1;
index1=[index1,temp];
index=index1(find(d(index1)==d(temp)-a(temp ,index1)));
if length(index)>=2 index=index(1);
end
index2(temp)=index; end
d, index1, index2。

相关文档
最新文档