一、无向图Graph数据类型:

import java.util.Scanner;


public class Graph
{ 
	private final int V;
	private int E;
	private Bag<Integer>[] adj;
	public	Graph(int v)
	{
		this.V = v;
		this.E = 0;
		adj = (Bag<Integer>[])new Bag[v];
		for (int i = 0; i < v; i++)
			adj[i] = new Bag<Integer>();
	}
	public Graph(In in)
	{
		this(in.readInt());
		int E = in.readInt();
		for (int i = 0; i < E; i++)
		{
			int v = in.readInt();
			int w = in.readInt();
			addEdge(v, w);
		}
		
			
		
		
	}
	public int V()
	{
		return V;
	}
	public int E()
	{
		return E;
	}
	public void addEdge(int v, int w)
	{
		adj[v].add(w);
		adj[w].add(v);
		E++;
	}
	public Iterable<Integer> adj(int v)
	{
		return adj[v];
	}
	//常见图操作
	public static int degree(Graph G, int v)
	{
		int count = 0;
		for (int w : G.adj(v)) count++;
		return count;
	}
	public static int maxDegree(Graph G)
	{
		int max = 0;
		for (int v= 0;  v < G.V(); v++)
		{
			if (degree(G,v) > max)
				max = degree(G, v);
		}
		return max;
	}
	public static double avgDgree(Graph G)
	{
		return G.E() * 2 / G.V();
	}
	public static int numberOfSelfLoops(Graph G)
	{
		int count = 0;
		for (int v = 0; v < G.V(); v++)
			for (int w : G.adj(v))
				if (v == w) count++;
		return count / 2;
	}
	
}
二、使用深度优先搜索查找图中路径:

public class DepthFirstPaths 
{
	private boolean[] marked;
	private int[] edgTo;
	private  final int s;
	public DepthFirstPaths(Graph G, int s)
	{
		this.s = s;
		marked = new boolean[G.V()];
		edgTo = new int[G.V()];
		dfs(G, s);
	}
	private void dfs(Graph G, int v)
	{
		marked[v] = true;
		for (int w : G.adj(v))
			if (!marked[w])
			{
				edgTo[w] = v;
				dfs(G, w);
			}
	}
	public boolean hasPathTo(int v)
	{return marked[v];}
	public Iterable<Integer> pathTo(int v)
	{
		if (!hasPathTo(v)) return null;
		Stack<Integer> path = new Stack<Integer>();
		for (int x = v; x != s; x = edgTo[x])
		{
			path.push(x);
		}
		path.push(s);
		return path;
	}
}

三、使用广度搜索查找图中路径:

public class BreadthFirstPaths 
{
	private boolean[] marked;
	private int[] edgTo;
	private  final int s;
	public BreadthFirstPaths(Graph G, int s)
	{
		this.s = s;
		marked = new boolean[G.V()];
		edgTo = new int[G.V()];
		bfs(G, s);
	}
	private void bfs(Graph G, int s)
	{
		Queue<Integer> queue = new Queue<Integer>();
		marked[s] = true;
		queue.enqueue(s);
		while (!queue.isEmpty())
		{
			int v = queue.dequeue();
			for (int w : G.adj(v))
			{
				if (!marked[w])
				{
					edgTo[w] = v;
					marked[w] = true;
					queue.enqueue(w);
				}
			}
		}
	}
	public boolean hasPathTo(int v)
	{return marked[v];}
	public Iterable<Integer> pathTo(int v)
	{
		if (!hasPathTo(v)) return null;
		Stack<Integer> path = new Stack<Integer>();
		for (int x = v; x != s; x = edgTo[x])
		{
			path.push(x);
		}
		path.push(s);
		return path;
	}
}

四、使用深度优先找出图中连通分量:

public class CC 
{
	private boolean[] marked;
	private int[] id;
	private int count;
	public CC(Graph G)
	{
		
		marked = new boolean[G.V()];
		id = new int[G.V()];
		for (int v = 0; v < G.V(); v++)
		{
			if (!marked[v])
			{
				dfs(G, v);
				count++;
			}
		}
	}
	private void dfs(Graph G, int s)
	{
		marked[s] = true;
		id[s] = count;
		for (int v : G.adj(s))
		{
			if (!marked[v])
				dfs(G, v);
			
		}
	}
	public boolean connected(int v, int w)
	{
		return id[v] == id[w];
	}
	public int count()
	{return count;}
	public int id(int v)
	{return id[v];}
}

五、G是无环图么:

public class Cycle 
{
	private boolean[] marked;
	private boolean hasCycle;
	public Cycle(Graph G)
	{
		marked = new boolean[G.V()];
		for (int s = 0; s < G.V(); s++)
		{
			if (!marked[s])
			{
				dfs(G, s, s);
			}
		}
	}
	private void dfs(Graph G, int v, int u)
	{
		marked[v] = true;
		for (int w : G.adj(v))
			if (!marked[w])
			{
				dfs(G, w, v);
			}
			else if (w != v) hasCycle = true;
	}
	public boolean hasCycle()
	{return hasCycle;}
	
}
六、图G是二分图么?

public class TwoColor
{
	private boolean[] marked;
	private boolean[] color;
	private boolean isTwoColor = true;
	public TwoColor(Graph G)
	{
		
		marked = new boolean[G.V()];
		color = new boolean[G.V()];
		for (int v = 0; v < G.V(); v++)
		{
			if (!marked[v])
			{
				dfs(G, v);
				
			}
		}
	}
	private void dfs(Graph G, int s)
	{
		marked[s] = true;
		for (int v : G.adj(s))
		{
			if (!marked[v])
			{
				color[v] = !color[s];
				dfs(G, v);
			}
			else if (color[v] == color[s]) isTwoColor = false;
		}
	}
	public boolean isTwoColor()
	{
		return isTwoColor;
	}
}

七、有向图类型:

public class Digraph
{
	private final int V;
	private int E;
	private Bag<Integer>[] adj;
	public	Digraph(int v)
	{
		this.V = v;
		this.E = 0;
		adj = (Bag<Integer>[])new Bag[v];
		for (int i = 0; i < v; i++)
			adj[i] = new Bag<Integer>();
	}
	public Digraph(In in)
	{
		this(in.readInt());
		int E = in.readInt();
		for (int i = 0; i < E; i++)
		{
			int v = in.readInt();
			int w = in.readInt();
			addEdge(v, w);
		}
		
			
		
		
	}
	public int V()
	{
		return V;
	}
	public int E()
	{
		return E;
	}
	public void addEdge(int v, int w)
	{
		adj[v].add(w);
		E++;
	}
	public Iterable<Integer> adj(int v)
	{
		return adj[v];
	}
	public Digraph reverse()
	{
		Digraph R = new Digraph(V);
		for (int v = 0; v < V; v++)
			for (int w : adj(v))
			{
				R.addEdge(w, v);
			}
			
		return R;
		
			
		
	}
	//常见图操作
	public static int degree(Graph G, int v)
	{
		int count = 0;
		for (int w : G.adj(v)) count++;
		return count;
	}
	public static int maxDegree(Graph G)
	{
		int max = 0;
		for (int v= 0;  v < G.V(); v++)
		{
			if (degree(G,v) > max)
				max = degree(G, v);
		}
		return max;
	}
	public static double avgDgree(Graph G)
	{
		return G.E() * 2 / G.V();
	}
	public static int numberOfSelfLoops(Graph G)
	{
		int count = 0;
		for (int v = 0; v < G.V(); v++)
			for (int w : G.adj(v))
				if (v == w) count++;
		return count / 2;
	}
}
八、有向图的可达性:

public class DirectedDFS 
{
	private boolean[] marked;
	public DirectedDFS(Digraph G, int s)
	{
		marked = new boolean[G.V()];
		dfs(G, s);
	}
	public DirectedDFS(Digraph G, Iterable<Integer> sources)
	{
		marked = new boolean[G.V()];
		for (int s : sources)
			if (!marked[s])
				dfs(G, s);
	}
	private void dfs(Digraph G, int s)
	{
		marked[s] = true;
		for (int v : G.adj(s))
			if (!marked[v])
				dfs(G, v);
	}
	public boolean marked(int v)
	{
		return marked[v];
	}
	public static void main(String[] args)
	{
		Digraph G = new Digraph(new In(args[0]));
		Bag<Integer> sources = new Bag<Integer>();
		for (int i = 1; i < args.length; i++)
			sources.add(Integer.parseInt(args[i]));
		DirectedDFS reachable = new DirectedDFS(G, sources);
		for (int v = 0; v < G.V(); v++)
			if (reachable.marked(v)) StdOut.print(v + " ");
		StdOut.println();		
	}
}

九、寻找有向环:

public class DirectedCycle 
{
	private boolean[] marked;
	private boolean[] onStack;
	private Stack<Integer> cycle;
	private int[] edgeTo;
	public DirectedCycle(Digraph G)
	{
		marked = new boolean[G.V()];
		onStack = new boolean[G.V()];
		edgeTo = new int[G.V()];
		for (int v = 0; v < G.V(); v++)
			if (!marked[v])
				dfs(G, v);
	}
	private void dfs(Digraph G, int v)
	{
		marked[v] = true;
		onStack[v] = true;
		for (int w : G.adj(v))
			if (hasCycle()) return;
			else if(!marked[w])
			{
				edgeTo[w] = v;
				dfs(G, w);
			}
			else if (onStack[w])
			{
				cycle = new Stack<Integer>();
				for (int x = v; x != w; x = edgeTo[x])
					cycle.push(x);
				cycle.push(w);
				cycle.push(v);
			}
		onStack[v] = false;
	}
	public boolean hasCycle(Digraph G)
	{
		return cycle != null;
	}
	public Iterable<Integer> cycle()
	{
		return cycle;
	}
}
十、有向图基于深度优先搜索的顶点排序:

public class DepthFirstOrder 
{
	private boolean[] marked;
	private Queue<Integer> pre;
	private Queue<Integer> post;
	private Stack<Integer> reversepost;
	public DepthFirstOrder(Digraph G)
	{
		pre = new Queue<Integer>();
		post = new Queue<Integer>();
		reversepost = new Stack<Integer>();
		marked = new boolean[G.V()];
		for (int v = 0; v < G.V(); v++)
			if (!marked[v]) dfs(G, v);
	}
	private void dfs(Digraph G, int v)
	{
		marked[v] = true;
		pre.enqueue(v);
		for (int w : G.adj(v))
			if (!marked[w]) dfs(G, w);
		post.enqueue(v);
		reversepost.push(v);
	}
	public Iterable<Integer> pre()
	{
		return pre;
	}
	public Iterable<Integer> post()
	{
		return post;
	}
	public Iterable<Integer> reversepost()
	{
		return reversepost;
	}
}
十一、拓扑排序:

public class Topological
{
	private Iterable<Integer> order;
	public Topological(Digraph G)
	{
		DirectedCycle cyclefinder = new DirectedCycle(G);
		if (!cyclefinder.hasCycle(G))
		{
			DepthFirstOrder dfs = new DepthFirstOrder(G);
			order = dfs.reversepost();
		}
	}
	public Iterable<Integer> order()
	{
		return order;
	}
	public boolean isDAG()
	{
		return order != null;
	}
}

十二、计算强连通分量的Kosaraju算法:

public class KosarajuSCC
{
	private boolean marked[];
	private int[] id;
	private int count;
	public KosarajuSCC(Digraph G)
	{
		id = new int[G.V()];
		marked = new boolean[G.V()];
		DepthFirstOrder order = new DepthFirstOrder(G.reverse());
		for (int s : order.reversepost())
		{
			if (!marked[s])
				{dfs(G, s); count++;}
		}
	
	}
	private void dfs(Digraph G, int s)
	{
		marked[s] = true;
		id[s] = count;
		for (int v : G.adj(s))
		{
			if (!marked[v])
				dfs(G, v);
			
		}
	}
	public boolean Stronglyconnected(int v, int w)
	{
		return id[v] == id[w];
	}
	public int count()
	{return count;}
	public int id(int v)
	{return id[v];}
}

十三、顶点对的可达性:

public class TransitiveClosure
{
	private DirectedDFS[] all;
	TransitiveClosure(Digraph G)
	{
		all = new DirectedDFS[G.V()];
		for (int v = 0; v < G.V(); v++)
		{
			all[v] = new DirectedDFS(G, v);
		}
	}
	boolean reachable(int v, int w)
	{
		return all[v].marked(w);
	}
}
十四、加权无向图数据类型:
public class Edge implements Comparable<Edge>
{
	private final int v;
	private final int w;
	private final double weight;
	public Edge(int v, int w, double weight)
	{
		this.v = v;
		this.w = w;
		this.weight = weight;
		
	}
	public double weight()
	{
		return weight;
	}
	public int either()
	{
		return v;
	}
	public int other(int vertex)
	{
		if (vertex == v) return w;
		else  return v;
		
	}
	public int compareTo(Edge that)
	{
		if (that.weight() > this.weight()) return -1;
		else if (that.weight() < this.weight()) return 1;
		else return 0;
	}
	public String toString()
	{
		return String.format("%d-%d %.2f", v, w, weight);
	}
}


public class EdgeWeightedGraph
{
	private final int V;
	private int E;
	Bag<Edge>[] adj;
	public EdgeWeightedGraph(int v)
	{
		this.V = v;
		this.E = 0;
		adj = (Bag<Edge>[])new Bag[V];
		for (int i = 0; i < V; i++)
		{
			adj[i] = new Bag<Edge>();
		}
	}
	public EdgeWeightedGraph(In in)
	{
		this(in.readInt());
        int E = in.readInt();
        if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative");
        for (int i = 0; i < E; i++) {
            int v = in.readInt();
            int w = in.readInt();
            double weight = in.readDouble();
            Edge e = new Edge(v, w, weight);
            addEdge(e);
        }
	}
   public int V()
   {
	 return V;
   }
   
   public int E()
   {
	   return E;
   }
   public void addEdge(Edge e)
   {
	   int v = e.either();
	   int w = e.other(v);
	   adj[v].add(e);
	   adj[w].add(e);
	   E++;
   }
   public Iterable<Edge> edges()
   {
	   Bag<Edge> b = new Bag<Edge>();
	   for (int v = 0; v < V(); v++)
	   {
		   for (Edge e : adj[v])
		   {
			   if (e.other(v) > v) b.add(e);
		   }
	   }
	   return b;
   }
}

十五、Prim最小生成树算法的延时实现:

public class LazyPrimMST 
{
	private boolean marked[];
	private Queue<Edge> mst;
	private MinPQ<Edge> pq;
	private double weight;
	
	public LazyPrimMST(EdgeWeightedGraph G)
	{
		weight = 0;
		marked = new boolean[G.V()];
		mst = new Queue<Edge>();
		pq = new MinPQ<Edge>();
		visit(G, 0);
		while (!pq.isEmpty())
		{
			Edge e = pq.delMin();
			int v = e.either();
			int w = e.other(v);
			if (marked[v] && marked[w]) continue;
			mst.enqueue(e);
			weight += e.weight();
			if (!marked[v]) visit(G, v);
			if (!marked[w]) visit(G, w);
		}
	}
	private void visit(EdgeWeightedGraph G, int v)
	{
		marked[v] = true;
		for (Edge e : G.adj[v])
			if (!marked[e.other(v)])
				pq.insert(e);
	}
	public Iterable<Edge> edges()
	{return mst;}
	public double weight()
	{
		return weight;
	}
}

十六、Prim最小生成树的即时算法:

public class PrimMST 
{
	private boolean[] marked;
	private Edge[] edgeTo;
	private double[] distTo;
	private IndexMinPQ<Edge> pq;
	public PrimMST(EdgeWeightedGraph G)
	{
		marked = new boolean[G.V()];
		edgeTo = new Edge[G.V()];
		distTo = new double[G.V()];
		for (int v = 0; v < G.V(); v++)
			distTo[v] = Double.POSITIVE_INFINITY;
		pq = new IndexMinPQ<Edge>(G.V());
		distTo[0] = 0.0;
		pq.insert(0, 0.0);
		while(!pq.isEmpty())
		{
			visit(G, pq.delMin());
		}
	}
	private void visit(EdgeWeightedGraph G, int v)
	{
		marked[v] = true;
		for (Edge e : G.adj[v])
		{
			int w = e.other(v);
			if(marked[w]) continue;
			if (e.weight() < distTo[w])
			{
				edgeTo[w] = e;
				distTo[w] = e.weight();
				if (pq.contains(w)) pq.change(w, distTo[w]);
				else pq.insert(w, distTo[w]);
			}
		}
	}
	public Iterable<Edge> edges() {
        Queue<Edge> mst = new Queue<Edge>();
        for (int v = 0; v < edgeTo.length; v++) {
            Edge e = edgeTo[v];
            if (e != null) {
                mst.enqueue(e);
            }
        }
        return mst;
    }
	 public double weight() {
	        double weight = 0.0;
	        for (Edge e : edges())
	            weight += e.weight();
	        return weight;
	    }

}
十七、Krustal最小生成树算法:

public class KrustalMST 
{
	private Queue<Edge> mst;
	public KrustalMST(EdgeWeightedGraph G)
	{
		mst = new Queue<Edge>();
		MinPQ<Edge> pq = new MinPQ<Edge>();
		for (Edge e : G.edges()) pq.insert(e);
		UF uf = new UF(G.V());
		while (!pq.isEmpty() && mst.size() < G.V() - 1)
		{
			Edge e = pq.delMin();
			int v = e.either();
			int w = e.other(v);
			if (uf.connected(v, w)) continue;
			uf.union(v, w);
			mst.enqueue(e);
		}
		
	}
	public Iterable<Edge> edges()
	{
		return mst;
	}
	public double weight()
	{
		double weight = 0;
		for (Edge e : edges())
		{
			weight += e.weight();
		}
		return weight;
	}
}

十八、加权有向图的数据类型:

public class DirectedEdge
{
	private int v;
	private int w;
	private double weight;
	public DirectedEdge(int v, int w, double weight)
	{
		this.v = v;
		this.w = w;
		this.weight = weight;
	}
	public int from()
	{
		return v;
	}
	public int to()
	{
		return w;
	}
	public double weight()
	{
		return weight;
	}
	public String toString()
	{
		return String.format("%d-%d %.2f", v, w, weight);
	}
}

public class EdgeWeightedDigraph 
{
	private final int V;
	private int E;
	Bag<DirectedEdge>[] adj;
	public EdgeWeightedDigraph(int v)
	{
		this.V = v;
		this.E = 0;
		adj = (Bag<DirectedEdge>[])new Bag[V];
		for (int i = 0; i < V; i++)
		{
			adj[i] = new Bag<DirectedEdge>();
		}
	}
	public EdgeWeightedDigraph(In in)
	{
		this(in.readInt());
        int E = in.readInt();
        if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative");
        for (int i = 0; i < E; i++) {
            int v = in.readInt();
            int w = in.readInt();
            double weight = in.readDouble();
            DirectedEdge e = new DirectedEdge(v, w, weight);
            addEdge(e);
        }
	}
   public int V()
   {
	 return V;
   }
   
   public int E()
   {
	   return E;
   }
   public void addEdge(DirectedEdge e)
   {
	   int v = e.from();
	  // int w = e.other(v);
	   adj[v].add(e);
	  // adj[w].add(e);
	   E++;
   }
   public Iterable<DirectedEdge> edges()
   {
	   Bag<DirectedEdge> b = new Bag<DirectedEdge>();
	   for (int v = 0; v < V(); v++)
	   {
		   for (DirectedEdge e : adj[v])
		   {
			   b.add(e);
		   }
	   }
	   return b;
   }
}


十九、Dijkstra最短路径算法(加权有向图/无向图 ,不支持负权重)

public class DijstraSP 
{
	private DirectedEdge[] edgeTo;
	private double[] distTo;
	private IndexMinPQ<Double> pq;
	public DijstraSP(EdgeWeightedDigraph G, int s)
	{
		edgeTo = new DirectedEdge[G.V()];
		distTo = new double[G.V()];
		pq = new IndexMinPQ<Double>(G.V());
		for (int v = 0; v < G.V(); v++)
			distTo[v] = Double.POSITIVE_INFINITY;
		distTo[s] = 0.0;
		pq.insert(s, 0.0);
		while (!pq.isEmpty())
		{
			relax(G, pq.delMin());
		}
	}
	private void relax(EdgeWeightedDigraph G, int v)
	{
		for (DirectedEdge e : G.adj[v])
		{
			int w = e.to();
			if (distTo[w] > distTo[v] + e.weight())
			{
				distTo[w] = distTo[v] + e.weight();
				edgeTo[w] = e;
				if (pq.contains(w)) pq.change(w, distTo[w]);
				else pq.insert(w, distTo[w]);
			}
		}
	}
	public double distTo(int v)
	{
		return distTo[v];
	}
	public boolean hasPathTo(int v)
	{
		return distTo[v] != Double.POSITIVE_INFINITY;
	}
	public Iterable<DirectedEdge> pathTo(int v)
	{
		if (!hasPathTo(v)) return null;
		Stack<DirectedEdge> path = new Stack<DirectedEdge>();
		for (DirectedEdge e = edgeTo[v] ; e != null; e = edgeTo[e.from()])
			path.push(e);
		return path;
	}
}
二十、加权有向无环图的最短路径算法:

public class AcyclicSP
{
	private DirectedEdge[] edgeTo;
	private double[] distTo;
	
	public AcycliSP(EdgeWeightedDigraph G, int s)
	{
		edgeTo = new DirectedEdge[G.V()];
		distTo = new double[G.V()];
		for (int v = 0; v < G.V(); v++)
			distTo[v] = Double.POSITIVE_INFINITY;
		distTo[s] = 0.0;
		Topological top = new Topological(G);
		for (int v : top.order())
			relax(G, v);
	}
	private void relax(EdgeWeightedDigraph G, int v)
	{
		for (DirectedEdge e : G.adj[v])
		{
			int w = e.to();
			if (distTo[w] > distTo[v] + e.weight())
			{
				distTo[w] = distTo[v] + e.weight();
				edgeTo[w] = e;
				
			}
		}
	}
	public double distTo(int v)
	{
		return distTo[v];
	}
	public boolean hasPathTo(int v)
	{
		return distTo[v] != Double.POSITIVE_INFINITY;
	}
	public Iterable<DirectedEdge> pathTo(int v)
	{
		if (!hasPathTo(v)) return null;
		Stack<DirectedEdge> path = new Stack<DirectedEdge>();
		for (DirectedEdge e = edgeTo[v] ; e != null; e = edgeTo[e.from()])
			path.push(e);
		return path;
	}
}
二十一、一般加权有向图(负权重、环)的最短路径问题BellmanFord算法:

public class BellFordmanSP 
{
	private DirectedEdge[] edgeTo;
	private double[] distTo;
	private int cost;
	private Queue<Integer> queue;
	private boolean[] onQ;
	private Iterable<DirectedEdge> cycle;
	public BellFordmanSP(EdgeWeightedDigraph G, int s)
	{
		edgeTo = new DirectedEdge[G.V()];
		distTo = new double[G.V()];
		onQ = new boolean[G.V()];
		queue = new Queue<Integer>();
		for (int v = 0; v < G.V(); v++)
			distTo[v] = Double.POSITIVE_INFINITY;
		distTo[s] = 0.0;
		queue.enqueue(s);
		onQ[s] = true;
		while (!queue.isEmpty() && !hasNegativeCycle())
		{
			int v = queue.dequeue();
			onQ[v] = false;
			relax(G, v);
		}
	}
	private void relax(EdgeWeightedDigraph G, int v)
	{
		for (DirectedEdge e : G.adj[v])
		{
			int w = e.to();
			if (distTo[w] > distTo[v] + e.weight())
			{
				distTo[w] = distTo[v] + e.weight();
				edgeTo[w] = e;
				if (!onQ[w]) queue.enqueue(w);
				onQ[w] = true;
			}
		}
		if (cost++ % G.V() == 0)
			findNegativeCycle();
	}
	private void findNegativeCycle()
	{
		int V = edgeTo.length;
		EdgeWeightedDigraph spt;
		spt = new EdgeWeightedDigraph(V);
		for (int v = 0; v < V; v++)
			if (edgeTo[v] != null)
				spt.addEdge(edgeTo[v]);
		EdgeWeightedCycleFinder cf = new EdgeWeightedCycleFinder(spt);
		cycle = cf.cycle();
		
	}
}
















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值