优先队列实现dijkstra算法C++代码
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了优先队列实现dijkstra算法C++代码相关的知识,希望对你有一定的参考价值。
禁止使用STL
只要代码写得好,再加多少分都行
嗯。。。我当然知道STL模板好了。。。但是现在就是不能用,所以这个优先队列就是要自己写。。。但是我不会写。。。C代码也可以啊。。。反正C和C++我都看得懂
P.S. O(n^2)的数据大了就会超时啊。。。我要的是O(nlogn)的。。。所以要用优先队列
5楼那个是从百度百科复制的吧,那个是O(n^2)的算法,看清楚再copy。。。
#define MaxNum 765432100
using namespace std;
ifstream fin("Dijkstra.in");
ofstream fout("Dijkstra.out");
int Map[501][501];
bool is_arrived[501];
int Dist[501],From[501],Stack[501];
int p,q,k,Path,Source,Vertex,Temp,SetCard;
int FindMin()
int p,Temp=0,Minm=MaxNum;
for(p=1;p<=Vertex;p++)
if ((Dist[p]<Minm)&&(!is_arrived[p]))
Minm=Dist[p];
Temp=p;
return Temp;
int main()
memset(is_arrived,0,sizeof(is_arrived));
fin >> Source >> Vertex;
for(p=1;p<=Vertex;p++)
for(q=1;q<=Vertex;q++)
fin >> Map[p][q];
if (Map[p][q]==0) Map[p][q]=MaxNum;
for(p=1;p<=Vertex;p++)
Dist[p]=Map[Source][p];
if (Dist[p]!=MaxNum)
From[p]=Source;
else
From[p]=p;
is_arrived[Source]=true;
SetCard=1;
do
Temp=FindMin();
if (Temp!=0)
SetCard=SetCard+1;
is_arrived[Temp]=true;
for(p=1;p<=Vertex;p++)
if ((Dist[p]>Dist[Temp]+Map[Temp][p])&&(!is_arrived[p]))
Dist[p]=Dist[Temp]+Map[Temp][p];
From[p]=Temp;
else
break;
while (SetCard!=Vertex);
for(p=1;p<=Vertex;p++)
if(p!=Source)
fout << "========================\n";
fout << "Source:" << Source << "\nTarget:" << p << '\n';
if (Dist[p]==MaxNum)
fout << "Distance:" << "Infinity\n";
fout << "Path:No Way!";
else
fout << "Distance:" << Dist[p] << '\n';
k=1;
Path=p;
while (From[Path]!=Path)
Stack[k]=Path;
Path=From[Path];
k=k+1;
fout << "Path:" << Source;
for(q=k-1;q>=1;q--)
fout << "-->" << Stack[q];
fout << "\n========================\n\n";
fin.close();
fout.close();
return 0;
Sample Input
2
7
00 20 50 30 00 00 00
20 00 25 00 00 70 00
50 25 00 40 25 50 00
30 00 40 00 55 00 00
00 00 25 55 00 10 00
00 70 50 00 10 00 00
00 00 00 00 00 00 00
Sample Output
========================
Source:2
Target:1
Distance:20
Path:2-->1
========================
========================
Source:2
Target:3
Distance:25
Path:2-->3
========================
========================
Source:2
Target:4
Distance:50
Path:2-->1-->4
========================
========================
Source:2
Target:5
Distance:50
Path:2-->3-->5
========================
========================
Source:2
Target:6
Distance:60
Path:2-->3-->5-->6
========================
========================
Source:2
Target:7
Distance:Infinity
Path:No Way!
========================
示例程序及相关子程序:
void Dijkstra(int n,int[] Distance,int[] iPath)
int MinDis,u;
int i,j;
//从邻接矩阵复制第n个顶点可以走出的路线,就是复制第n行到Distance[]
for(i=0;i<VerNum;i++)
Distance=Arc[n,i];
Visited=0;
//第n个顶点被访问,因为第n个顶点是开始点
Visited[n]=1;
//找到该顶点能到其他顶点的路线、并且不是开始的顶点n、以前也没走过。
//相当于寻找u点,这个点不是开始点n
for(i=0;i<VerNum;i++)
u=0;
MinDis=No;
for(j=0;j<VerNum;j++)
if(Visited[j] == 0&&(Distance[j]<MinDis))
MinDis=Distance[j];
u=j;
//如范例P1871图G6,Distance=[No,No,10,No,30,100],第一次找就是V2,所以u=2
//找完了,MinDis等于不连接,则返回。这种情况类似V5。
if(MinDis==No) return ;
//确立第u个顶点将被使用,相当于Arc[v,u]+Arc[u,w]中的第u顶点。
Visited[u]=1;
//寻找第u个顶点到其他所有顶点的最小路,实际就是找Arc[u,j]、j取值在[0,VerNum]。
//如果有Arc[i,u]+Arc[u,j]<Arc[i,j],则Arc[i,j]=Arc[i,u]+Arc[u,j]<Arc[i,j]
//实际中,因为Distance[]是要的结果,对于起始点确定的情况下,就是:
//如果(Distance[u] + Arc[u,j]) <= Distance[j] 则:
//Distance[j] = Distance[u] + Arc[u, j];
//而iPath[]保存了u点的编号;
//同理:对新找出的路线,要设置Visited[j]=0,以后再找其他路,这个路可能别利用到。例如V3
for(j=0;j<VerNum;j++)
if(Visited[j]==0&&Arc[u,j]<No&&u!= j)
if ((Distance[u] + Arc[u,j]) <= Distance[j])
Distance[j] = Distance[u] + Arc[u, j];
Visited[j]=0;
iPath[j] = u;
//辅助函数
void Prim()
int i,m,n=0;
for(i=0;i<VerNum;i++)
Visited=0;
T=new TreeNode();
T.Text =V;
Visited[n]++;
listBox1.Items.Add (V[n]);
while(Visit()>0)
if((m=MinAdjNode(n))!=-1)
T[n].Nodes.Add(T[m]);
n=m;
Visited[n]++;
else
n=MinNode(0);
if(n>0) T[Min2].Nodes.Add(T[Min1]);
Visited[n]++;
listBox1.Items.Add (V[n]);
treeView1.Nodes.Add(T[0]);
void TopoSort()
int i,n;
listBox1.Items.Clear();
Stack S=new Stack();
for(i=0;i<VerNum;i++)
Visited=0;
for(i=VerNum-1;i>=0;i--)
if(InDegree(i)==0)
S.Push(i);
Visited++;
while(S.Count!=0)
n=(int )S.Pop();
listBox1.Items.Add (V[n]);
ClearLink(n);
for(i=VerNum-1;i>=0;i--)
if(Visited==0&&InDegree(i)==0)
S.Push(i);
Visited++;
void AOETrave(int n,TreeNode TR,int w)
int i,w0;
if(OutDegree(n)==0) return;
for(i=0;i<VerNum;i++)
if((w0=Arc[n,i])!=0)
listBox1.Items.Add (V+"\t"+(w+w0).ToString()+"\t"+i.ToString()+"\t"+n.ToString());
TreeNode T1=new TreeNode();
T1.Text =V+" [W="+(w+w0).ToString()+"]";
TR.Nodes.Add(T1);
AOETrave(i,T1,w+w0);
void AOE()
int i,w=0,m=1;
TreeNode T1=new TreeNode();
for(i=0;i<VerNum;i++)
Visited=0;
T1.Text =V[0];
listBox1.Items.Add ("双亲表示法显示这个生成树:");
listBox1.Items.Add ("V\tW\tID\tPID");
for(i=0;i<VerNum;i++)
if((w=Arc[0,i])!=0)
listBox1.Items.Add (V+"\t"+w.ToString()+"\t"+i.ToString()+"\t0");
TreeNode T2=new TreeNode();
T2.Text=V+" [W="+w.ToString()+"]";
AOETrave(i,T2,w);
T1.Nodes.Add (T2);
listBox1.Items.Add("\t\t树"+m.ToString());
m++;
treeView1.Nodes.Clear();
treeView1.Nodes.Add (T1);
int IsZero()
int i;
for(i=0;i<VerNum;i++)
if(LineIsZero(i)>=0) return i;
return -1;
int LineIsZero(int n)
int i;
for(i=0;i<VerNum;i++)
if (Arc[n,i]!=0) return i;
return -1;
void DepthTraverse()
int i,m;
for(i=0;i<VerNum;i++)
Visited=0;
T=new TreeNode();
T.Text =V;
R=0;
while((m=IsZero())>=0)
if(Visited[m]==0)
listBox1.Items.Add (V[m]);
R[m]=1;
Visited[m]++;
DTrave(m);
for(i=0;i<VerNum;i++)
if(R==1)
treeView1.Nodes.Add (T);
void DTrave(int n)
int i;
if (LineIsZero(n)<0) return;
for(i=VerNum-1;i>=0;i--)
if(Arc[n,i]!=0)
Arc[n,i]=0;
Arc[i,n]=0;
if(Visited==0)
listBox1.Items.Add (V);
T[n].Nodes.Add (T);
R=0;
Visited++;
DTrave(i);
void BreadthTraverse()
int i,m;
for(i=0;i<VerNum;i++)
Visited=0;
T=new TreeNode();
T.Text =V;
R=0;
while((m=IsZero())>=0)
if(Visited[m]==0)
listBox1.Items.Add (V[m]);
R[m]=1;
Visited[m]++;
BTrave(m);
for(i=0;i<VerNum;i++)
if(R==1)
treeView1.Nodes.Add (T);
void BTrave(int n)
int i;
Queue Q=new Queue();
Q.Enqueue(n);
while(Q.Count!=0)
for(i=0;i<VerNum;i++)
if(Arc[n,i]!=0)
Arc[n,i]=0;
Arc[i,n]=0;
if(Visited==0)
listBox1.Items.Add(V);
T[n].Nodes.Add (T);
R=0;
Visited++;
Q.Enqueue(i);
n=(int )Q.Dequeue();
int MinNode(int vn)
int i,j,n,m,Min=No;
n=-1;m=-1;
for (i=vn;i<VerNum;i++)
for(j=0;j<VerNum;j++)
if(Arc[i,j]!=No&&Arc[i,j]<Min&&Visited==0&&Visited[j]==1)
Min=Arc[i,j];n=i;m=j;
Min1=n;Min2=m;
return n;
int MinAdjNode(int n)
int i,Min,m;
Min=No;m=-1;
for(i=0;i<VerNum;i++)
if(Arc[n,i]!=No&&Visited==0&&Min>Arc[n,i]&&Visited[n]==1)
Min=Arc[n,i];m=i;
return m;
int Visit()
int i,s=0;
for(i=0;i<VerNum;i++)
if(Visited==0) s++;
return s;
[编辑本段]dijkstra算法的Pascal实现:
program dijkstra;
var
a:array[1..100,1..100]of integer;
flag:array[1..100]of boolean;
w,x,n,i,j,min,minn:integer;
begin
readln(n);
for i:=1 to n do
begin
for j:=1 to n do read(a[i,j]);
readln;
end;
fillchar(flag,sizeof(flag),false);
flag[1]:=true;
minn:=1;
for x:=2 to n do
begin
min:=32767;
for i:=2 to n do
if (a[1,i]<min)and(flag=false) then
begin
min:=a[1,i];
minn:=i;
end;
flag[minn]:=true;
for j:=1 to n do
if (j<>minn) and (a[1,minn]+a[minn,j]<a[1,j]) and(flag[j]=false) then
a[1,j]:=a[1,minn]+a[minn,j];
end;
for i:=1 to n do
write(a[1,i],' ');
end.
4
0 100 30 999
100 0 99 10
30 99 0 99
999 10 99 0
程序输出:0 100 30 110
dijkstra算法的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(i)=1;index1=1;index2=ones(1,length(a));
d(1:length(a))=M;d(i)=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(i));
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
matlab编程
function [s,d]=minroute(i,m,w,opt)
% 图与网络论中求最短路径的dijkstra算法M函数
% 格式[s,d]=minroute(i,m,w,opt)
if nargin<4,opt=0;end
dd=[];tt=[];ss=[];ss(1,1)=i;v=1:m;v(i)=[];
dd=[0;i];kk=2;[mdd,ndd]=size(dd);
while~isempty(v)
[tmpd,j]=min(w(i,v));tmpj=v(j);
for k=2:ndd
[tmp2,jj]=min(dd(1,k)+w(dd(2,k),v));
tmp2=v(jj);tt(k-1,:)=[tmp1,tmp2,jj];
end;
tmp=[tmpd,tmpj,j;tt];[tmp3,tmp4]=min(tmp(;,1));
if tmp3==tmpd
ss(1:2,kk)=[i;tmp(tmp4,2)];
else,tmp5=find(ss(:,tmp4)~=0);tmp6=length(tmp5);
if dd(2,tmp4)=ss(tmp6,tmp4)
ss(1:tmp6+1,kk)=[ss(tmp5,tmp4);tmp(tmp4,2)];
else,ss(1:3,kk)=[i;dd(2,tmp4);tmp(tmp4,2)];
end;end
dd=[dd,[tmp3,tmp(tmp4,2)]];v(tmp(tmp4,3))=[];
[mdd,ndd]=size(dd);kk=kk+1;
end;
if opt==1
[tmp,t]=sort(dd(2,:));s=ss(:t);d=dd(1,t);
else,s=ss;d=dd(1,:);
end 参考技术A 模板是HDOJ 2544
我写的是记录每个点在堆中的位置IncreaseKey,也可以Relax后直接往里插,用个bool数组记录一下
#include<cstdio>
#include<cstdlib>
int n,m;
int map[101][101],d[101];
class Heap
public:
int handle[101];
void Build(int n)
for(int i=1;i<=n;i++) a[i]=handle[i]=i;
size=n;
void Percup(int p)
int temp=a[p];
for(;d[temp]<d[a[p>>1]];p>>=1)
a[p]=a[p>>1];
handle[a[p]]=p;
a[p]=temp;
handle[a[p]]=p;
int DeleteMin()
int val=a[1];
a[1]=a[size--];
Percdown(1);
handle[val]=0;
return val;
bool Empty() return size==0;
private:
int a[101],size;
void Percdown(int p)
int temp=a[p],child;
for(;(p<<1)<=size;p=child)
child=p<<1;
if(child+1<=size && d[a[child+1]]<d[a[child]]) child++;
if(d[temp]<d[a[child]]) break;
a[p]=a[child];
handle[a[p]]=p;
a[p]=temp;
handle[a[p]]=p;
h;
void init()
int i,j,c;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) map[i][j]=0x7fffffff;
d[i]=0x7fffffff;
d[1]=0;
while(m--)
scanf("%d%d%d",&i,&j,&c);
map[i][j]=map[j][i]=c;
void Relax(int num)
for(int i=1;i<=n;i++)
if(h.handle[i] && map[num][i]!=0x7fffffff && d[i]>d[num]+map[num][i])
d[i]=d[num]+map[num][i];
h.Percup(h.handle[i]);
void dijk()
h.Build(n);
while(!h.Empty()) Relax(h.DeleteMin());
int main()
while(scanf("%d%d",&n,&m) && n+m)
init();
dijk();
printf("%d\n",d[n]);
system("pause");
return 0;
本回答被提问者采纳 参考技术B #include<algorithm>
using namespace std;
#define MAXN 100
#define HEAP_SIZE 100
typedef int Graph[MAXN][MAXN];
//对于Heap中的每个元素,都对应一个<data,cost>
//表示这个元素的标号和实际的值,由于堆Heapify之后
//data的位置会移动,因此index[s]表示data值为s的元素实际所处的位置
template <class COST_TYPE>
class Heap
public:
int data[HEAP_SIZE],index[HEAP_SIZE],size;
COST_TYPE cost[HEAP_SIZE];
void shift_up(int i)
int j;
while(i>0)
j=(i-1)/2;
if(cost[data[i]]<cost[data[j]])
swap(index[data[i]],index[data[j]]);
swap(data[i],data[j]);
i=j;
else break;
void shift_down(int i)
int j,k;
while(2*i+1<size)
j=2*i+1;
k=j+1;
if(k<size&&cost[data[k]]<cost[data[j]]&&cost[data[k]]<cost[data[i]])
swap(index[data[k]],index[data[i]]);
swap(data[k],data[i]);
i=k;
else if(cost[data[j]]<cost[data[i]])
swap(index[data[j]],index[data[i]]);
swap(data[j],data[i]);
i=j;
else break;
void init()
size=0;
memset(index,-1,sizeof(index));
memset(cost,-1,sizeof(cost));
bool empty()
return(size==0);
int pop()
int res=data[0];
data[0]=data[size-1];
index[data[0]]=0;
size--;
shift_down(0);
return res;
int top()
return data[0];
void push(int x,COST_TYPE c)
if(index[x]==-1)
cost[x]=c;
data[size]=x;
index[x]=size;
size++;
shift_up(index[x]);
else
if(c<cost[x])
cost[x]=c;
shift_up(index[x]);
shift_down(index[x]);
;
//下面是利用该优先队列实现dijkstra算法的一个例子
int Dijkstra(Graph G,int n,int s,int t)
Heap<int> heap;
heap.init();
heap.push(s,0);
while(!heap.empty())
int u=heap.pop();
if(u==t)
return heap.cost[t];
for(int i=0;i<n;i++)
if(G[u][i]>=0)
heap.push(i,heap.cost[u]+G[u][i]);
return -1;
参考技术C 那你直接照着源码写不就OK了?
--------------------------------------------------
template<class _Ty,
class _Container = vector<_Ty>,
class _Pr = less<typename _Container::value_type> >
class priority_queue
// priority queue implemented with a _Container
public:
typedef _Container container_type;
typedef typename _Container::value_type value_type;
typedef typename _Container::size_type size_type;
typedef typename _Container::reference reference;
typedef typename _Container::const_reference const_reference;
priority_queue()
: c(), comp()
// construct with empty container, default comparator
explicit priority_queue(const _Pr& _Pred)
: c(), comp(_Pred)
// construct with empty container, specified comparator
priority_queue(const _Pr& _Pred, const _Container& _Cont)
: c(_Cont), comp(_Pred)
// construct by copying specified container, comparator
make_heap(c.begin(), c.end(), comp);
template<class _Iter>
priority_queue(_Iter _First, _Iter _Last)
: c(_First, _Last), comp()
// construct by copying [_First, _Last), default comparator
make_heap(c.begin(), c.end(), comp);
template<class _Iter>
priority_queue(_Iter _First, _Iter _Last, const _Pr& _Pred)
: c(_First, _Last), comp(_Pred)
// construct by copying [_First, _Last), specified comparator
make_heap(c.begin(), c.end(), comp);
template<class _Iter>
priority_queue(_Iter _First, _Iter _Last, const _Pr& _Pred,
const _Container& _Cont)
: c(_Cont), comp(_Pred)
// construct by copying [_First, _Last), container, and comparator
c.insert(c.end(), _First, _Last);
make_heap(c.begin(), c.end(), comp);
bool empty() const
// test if queue is empty
return (c.empty());
size_type size() const
// return length of queue
return (c.size());
const_reference top() const
// return highest-priority element
return (c.front());
reference top()
// return mutable highest-priority element (retained)
return (c.front());
void push(const value_type& _Pred)
// insert value in priority order
c.push_back(_Pred);
push_heap(c.begin(), c.end(), comp);
void pop()
// erase highest-priority element
pop_heap(c.begin(), c.end(), comp);
c.pop_back();
protected:
_Container c; // the underlying container
_Pr comp; // the comparator functor
; 参考技术D 为什么要优先队列呢?你是用来做什么的?直接一个数组,然后找最大或最小的数或char,实现时间复杂度0(n^2)不行? 楼主的问题问的非常好,我也想看看比n^2还小的dijkstra算法哈,我只知道dijkstra算法,最好数组n^2,最差最小最大堆n^2logn,有高手做出来了,我也很想瞧瞧哈
补充一点,如果是稀疏矩阵,用最大最小堆做,虽然是n^2logn,但n相对就变小了,同样减少了时间复杂度,
Dijkstra 算法运行时的区别:优先队列与双向链表
【中文标题】Dijkstra 算法运行时的区别:优先队列与双向链表【英文标题】:Difference between the runtime of Dijkstra's Algorithm: Priority Queue vs. Doubly Linked List 【发布时间】:2016-03-20 12:02:44 【问题描述】:关于运行时复杂性,以下两者之间有什么区别以及为什么?:
(1) DIJKSTRA 的算法使用常规的优先队列(Heap)
(2) DIJKSTRA 使用双向链表的算法
(除非没有区别)
【问题讨论】:
en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Running_time 不同意“离题”关闭;显然,这是热门话题,正如 *** 定义的大量相关标签所证明的那样,这些标签与问题相关。 【参考方案1】:Dijkstra 算法的最通用版本假定您可以访问某种支持以下操作的优先级队列结构:
make-heap(s, n):在初始距离∞处构建一个包含n个节点的堆,起始节点s除外,它的距离为0。 dequeue-min():移除并返回优先级最低的元素。 decrease-key(obj, key):给定优先级队列中现有的对象obj,将其优先级降低到key给定的级别。Dijkstra 算法需要一次调用 make-heap,O(n) 调用 dequeue-min,O(m) 调用 reduce-key,其中 n 是节点数,m 是边数。总体运行时间实际上可以给出为 O(Tmh + nTdeq + mTdk),其中 Tmh、Tdeq 和 Tdk 分别是进行 make-heap、dequeue 和 reduction-key 的平均(摊销)成本。
现在,假设您的优先级队列是一个双向链表。实际上,有几种方法可以将双向链表用作优先级队列:可以使节点按距离排序,或者可以使它们保持未排序的顺序。让我们逐一考虑。
在已排序的双向链表中,生成堆的成本为 O(n):只需插入起始节点,然后插入距离无穷远的 n - 1 个其他节点。执行 dequeue-min 的成本是 O(1):只需删除第一个元素。然而,做一个减少键的成本是 O(n),因为如果你需要改变一个节点的优先级,你可能不得不移动它,而且你找不到移动它的位置(在最坏的情况下)对节点进行线性扫描。这意味着运行时间将为 O(n + n + nm) = O(mn)。
在一个未排序的双向链表中,做一个 make-heap 的成本仍然是 O(n),因为你需要创建 n 个不同的节点。 dequeue-min 的成本现在是 O(n),因为您必须对列表中的所有节点进行线性扫描才能找到最小值。但是,减少键的成本现在是 O(1),因为您可以就地更新节点的键。这意味着运行时间是 O(n + n2 + m) = O(n2 + m) = O(n2) ,因为边的数量永远不会超过 O(n2)。这是比以前的改进。
对于二叉堆,如果使用标准的线性时间堆化算法,则生成堆的成本为 O(n)。执行出队的成本是 O(log n),执行减少键的成本也是 O(log n)(只需将元素冒泡,直到它位于正确的位置)。这意味着使用二叉堆的 Dijkstra 算法的运行时间是 O(n + n log n + m log n) = O(m log n),因为如果图是连通的,我们将得到 m ≥ n。
在渐近意义上,您可以使用斐波那契堆做得更好。这是一个专门为使 Dijkstra 算法快速而发明的优先级队列。它可以在 O(n) 时间内完成一个 make-heap,在 O(log n) 时间内完成一个 dequeue-min,在(摊销的)O(1) 时间内完成一个减少键。这使得 Dijkstra 算法的运行时间为 O(n + n log n + m) = O(m + n log n),尽管在实践中常数因子使斐波那契堆比二进制堆慢。
所以你有它!不同的优先级队列确实有所作为。有趣的是,“Dijkstra 算法”更像是一个算法家族而不是单个算法,因为数据结构的选择对于算法的快速运行至关重要。
【讨论】:
作为脚注,我认为重要的是要补充一点,二进制堆实现并不总是更快,因为 m 可能等于 n^2 在这种情况下复杂度是 O(n^2 log n)跨度>以上是关于优先队列实现dijkstra算法C++代码的主要内容,如果未能解决你的问题,请参考以下文章