数据结构–图(Graph)详解(四)

文章目录

  • 数据结构--图(Graph)详解(四)
    • 一、图中几个NB的算法
      • 1.普里姆算法(Prim算法)求最小生成树
      • 2.克鲁斯卡尔算法(Kruskal算法)求最小生成树
      • 3.拓扑排序算法
      • 4.迪杰斯特拉(Dijkstra算法)算法
      • 5.弗洛伊德算法

一、图中几个NB的算法

1.普里姆算法(Prim算法)求最小生成树

普里姆算法(Prim算法)求最小生成树:https://blog.csdn.net/wolfGuiDao/article/details/107588112

该算法从顶点的角度为出发点,时间复杂度为O(n2),更适合与解决边的绸密度更高的连通网。

2.克鲁斯卡尔算法(Kruskal算法)求最小生成树

  • 克鲁斯卡尔算法,从边的角度求网的最小生成树,时间复杂度为O(eloge)。和普里姆算法恰恰相反,更适合于求边稀疏的网的最小生成树。
  • 对于任意一个连通网的最小生成树来说,在要求总的权值最小的情况下,最直接的想法就是将连通网中的所有边按照权值大小进行升序排序,从小到大依次选择。
  • 由于最小生成树本身是一棵生成树,所以需要时刻满足以下两点:
  • 生成树中任意顶点之间有且仅有一条通路,也就是说,生成树中不能存在回路;
  • 对于具有 n 个顶点的连通网,其生成树中只能有 n-1 条边,这 n-1 条边连通着 n 个顶点。
  • 连接 n 个顶点在不产生回路的情况下,只需要 n-1 条边。
  • 所以克鲁斯卡尔算法的具体思路是:将所有边按照权值的大小进行升序排序,然后从小到大一一判断
  • 条件为:如果这个边不会与之前选择的所有边组成回路,就可以作为最小生成树的一部分;反之,舍去
  • 直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。筛选出来的边和所有的顶点构成此连通网的最小生成树。
  • 判断是否会产生回路的方法为:在初始状态下给每个顶点赋予不同的标记,对于遍历过程的每条边,其都有两个顶点,判断这两个顶点的标记是否一致,如果一致,说明它们本身就处在一棵树中,如果继续连接就会产生回路;如果不一致,说明它们之间还没有任何关系,可以连接(也可以使用并查集来判断)

假设遍历到一条由顶点 A 和 B 构成的边,而顶点 A 和顶点 B 标记不同,此时不仅需要将顶点 A 的标记更新为顶点 B 的标记,还需要更改所有和顶点 A 标记相同的顶点的标记,全部改为顶点 B 的标记。

  • 例如,使用克鲁斯卡尔算法找图 1 的最小生成树的过程为:
  • 首先,在初始状态下,对各顶点赋予不同的标记(用颜色区别),如下图所示:

  • 对所有边按照权值的大小进行排序,按照从小到大的顺序进行判断,首先是(1,3)
  • 由于顶点 1 和顶点 3 标记不同,所以可以构成生成树的一部分,遍历所有顶点,将与顶点 3 标记相同的全部更改为顶点 1 的标记,如(2)所示:
  • 其次是(4,6)边,两顶点标记不同,所以可以构成生成树的一部分,更新所有顶点的标记为:

  • 其次是(2,5)边,两顶点标记不同,可以构成生成树的一部分,更新所有顶点的标记为:

  • 然后最小的是(3,6)边,两者标记不同,可以连接,遍历所有顶点,将与顶点 6 标记相同的所有顶点的标记更改为顶点 1 的标记:

  • 继续选择权值最小的边,此时会发现,权值为 5 的边有 3 个,其中(1,4)和(3,4)各自两顶点的标记一样,如果连接会产生回路,所以舍去
  • 而(2,3)标记不一样,可以选择,将所有与顶点 2 标记相同的顶点的标记全部改为同顶点 3 相同的标记:
  • 当选取的边的数量相比与顶点的数量小 1 时,说明最小生成树已经生成。所以最终采用克鲁斯卡尔算法得到的最小生成树为(6)所示
#include "stdio.h"
#include "stdlib.h"
#define MAX_VERtEX_NUM 20
#define VertexType inttypedef struct edge{VertexType initial;VertexType end;VertexType weight;
}edge[MAX_VERtEX_NUM];//定义辅助数组
typedef struct {VertexType value;//顶点数据int sign;//每个顶点所属的集合
}assist[MAX_VERtEX_NUM];
assist assists;//qsort排序函数中使用,使edges结构体中的边按照权值大小升序排序
int cmp(const void *a,const void*b){return  ((struct edge*)a)->weight-((struct edge*)b)->weight;
}//初始化连通网
void CreateUDN(edge *edges,int *vexnum,int *arcnum){printf("输入连通网的边数:\n");scanf("%d %d",&(*vexnum),&(*arcnum));printf("输入连通网的顶点:\n");for (int i=0; i<(*vexnum); i++) {scanf("%d",&(assists[i].value));assists[i].sign=i;}printf("输入各边的起始点和终点及权重:\n");for (int i=0 ; i<(*arcnum); i++) {scanf("%d,%d,%d",&(*edges)[i].initial,&(*edges)[i].end,&(*edges)[i].weight);}
}//在assists数组中找到顶点point对应的位置下标
int Locatevex(int vexnum,int point){for (int i=0; i<vexnum; i++) {if (assists[i].value==point) {return i;}}return -1;
}int main(){int arcnum,vexnum;edge edges;CreateUDN(&edges,&vexnum,&arcnum);//对连通网中的所有边进行升序排序,结果仍保存在edges数组中qsort(edges, arcnum, sizeof(edges[0]), cmp);//创建一个空的结构体数组,用于存放最小生成树edge minTree;//设置一个用于记录最小生成树中边的数量的常量int num=0;//遍历所有的边for (int i=0; i<arcnum; i++) {//找到边的起始顶点和结束顶点在数组assists中的位置int initial=Locatevex(vexnum, edges[i].initial);int end=Locatevex(vexnum, edges[i].end);//如果顶点位置存在且顶点的标记不同,说明不在一个集合中,不会产生回路if (initial!=-1&& end!=-1&&assists[initial].sign!=assists[end].sign) {//记录该边,作为最小生成树的组成部分minTree[num]=edges[i];//计数+1num++;//将新加入生成树的顶点标记全不更改为一样的for (int k=0; k<vexnum; k++) {if (assists[k].sign==assists[end].sign) {assists[k].sign=assists[initial].sign;}}//如果选择的边的数量和顶点数相差1,证明最小生成树已经形成,退出循环if (num==vexnum-1) {break;}}}//输出语句for (int i=0; i<vexnum-1; i++) {printf("%d,%d\n",minTree[i].initial,minTree[i].end);}return 0;
}

3.拓扑排序算法

  • 拓扑排序指的是将有向无环图(又称“DAG”图)中的顶点按照图中指定的先后顺序进行排序
  • 在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序
  • 每个顶点出现且只出现一次;
  • 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。

  • 例如,图 1 中的两个图都是有向无环图,都可以使用拓扑排序对图中的顶点进行排序

  • 两个图形的区别是:左图中的 V2 和 V3 之间没有明确的前后顺序;而右图中任意两个顶点之间都有前后顺序。

  • 所以,左图中顶点之间的关系被称为“偏序”关系;右图中顶点之间的关系被称为”全序“关系。

在有向无环图中,弧的方向代表着顶点之间的先后次序,例如从 V1 指向 V2 的弧表示在进行排序时 V1 在前, V2 在后。

全序是偏序的一种特殊情况。对于任意一个有向无环图来说,通过拓扑排序得到的序列首先一定是偏序,如果任意两个顶点都具有前后顺序,那么此序列是全序。

  • 拓扑排序的方法
    对有向无环图进行拓扑排序,只需要遵循两个原则:
  • 在图中选择一个没有前驱的顶点 V(即入度为0);
  • 从图中删除顶点 V 和所有以该顶点为尾的弧(删除该顶点和所有以它为起点的有向边)。

例如,在对图 1 中的左图进行拓扑排序时的步骤如图 2 所示

有向无环图如果顶点本身具有某种实际意义,例如用有向无环图表示大学期间所学习的全部课程,每个顶点都表示一门课程,有向边表示课程学习的先后次序

例如要先学《程序设计基础》和《离散数学》,然后才能学习《数据结构》。所以用来表示某种活动间的优先关系的有向图简称为“AOV网”。

  • 进行拓扑排序时,首先找到没有前驱的顶点 V1,如图 2(1)所示;
  • 删除顶点 V1 及以 V1 作为起点的弧后,继续查找没有前驱的顶点,此时, V2 和 V3 都符合条件,可以随机选择一个,例如图 2(2) 所示,选择 V2
  • 然后继续重复以上的操作,直至最后找不到没有前驱的顶点

所以,针对图 2 来说,拓扑排序最后得到的序列有两种:

V1 -> V2 -> V3 -> V4
V1 -> V3 -> V2 -> V4
  • 如果顶点之间只是具有偏序关系,那么拓扑排序的结果肯定不唯一;如果顶点之间是全序关系,那么拓扑排序得到的序列唯一
  • 拓扑排序的C语言实现
  • 大致思路为:首先通过邻接表将 AOV 网进行存储,由于拓扑排序的整个过程中,都是以顶点的入度为依据进行排序,所以需要根据建立的邻接表统计出各顶点的入度
  • 在得到各顶点的入度后,首先找到入度为 0 的顶点作为拓扑排序的起始点
  • 然后查找以该顶点为起始点的所有顶点,如果入度为 1,说明如果删除前一个顶点后,该顶点的入度为 0,为拓扑排序的下一个对象。
#include <stdio.h>
#include <stdlib.h>
#define  MAX_VERTEX_NUM 20//最大顶点个数
#define  VertexType int//顶点数据的类型typedef enum{false,true} bool;typedef struct ArcNode{int adjvex;//邻接点在数组中的位置下标struct ArcNode * nextarc;//指向下一个邻接点的指针
}ArcNode;typedef struct VNode{VertexType data;//顶点的数据域ArcNode * firstarc;//指向邻接点的指针
}VNode,AdjList[MAX_VERTEX_NUM];//存储各链表头结点的数组typedef struct {AdjList vertices;//图中顶点及各邻接点数组int vexnum,arcnum;//记录图中顶点数和边或弧数
}ALGraph;//找到顶点对应在邻接表数组中的位置下标
int LocateVex(ALGraph G,VertexType u){for (int i=0; i<G.vexnum; i++) {if (G.vertices[i].data==u) {return i;}}return -1;
}//创建AOV网,构建邻接表
void CreateAOV(ALGraph **G){*G=(ALGraph*)malloc(sizeof(ALGraph));scanf("%d,%d",&((*G)->vexnum),&((*G)->arcnum));for (int i=0; i<(*G)->vexnum; i++) {scanf("%d",&((*G)->vertices[i].data));(*G)->vertices[i].firstarc=NULL;}VertexType initial,end;for (int i=0; i<(*G)->arcnum; i++) {scanf("%d,%d",&initial,&end);ArcNode *p=(ArcNode*)malloc(sizeof(ArcNode));p->adjvex=LocateVex(*(*G), end);p->nextarc=NULL;int locate=LocateVex(*(*G), initial);p->nextarc=(*G)->vertices[locate].firstarc;(*G)->vertices[locate].firstarc=p;}
}//结构体定义栈结构
typedef struct stack{VertexType data;struct stack * next;
}stack;//初始化栈结构
void initStack(stack* *S){(*S)=(stack*)malloc(sizeof(stack));(*S)->next=NULL;
}//判断链表是否为空
bool StackEmpty(stack S){if (S.next==NULL) {return true;}return false;
}//进栈,以头插法将新结点插入到链表中
void push(stack *S,VertexType u){stack *p=(stack*)malloc(sizeof(stack));p->data=u;p->next=NULL;p->next=S->next;S->next=p;
}//弹栈函数,删除链表首元结点的同时,释放该空间,并将该结点中的数据域通过地址传值给变量i;
void pop(stack *S,VertexType *i){stack *p=S->next;*i=p->data;S->next=S->next->next;free(p);
}//统计各顶点的入度
void FindInDegree(ALGraph G,int indegree[]){//初始化数组,默认初始值全部为0for (int i=0; i<G.vexnum; i++) {indegree[i]=0;}//遍历邻接表,根据各链表中结点的数据域存储的各顶点位置下标,在indegree数组相应位置+1for (int i=0; i<G.vexnum; i++) {ArcNode *p=G.vertices[i].firstarc;while (p) {indegree[p->adjvex]++;p=p->nextarc;}}
}void TopologicalSort(ALGraph G){int indegree[G.vexnum];//创建记录各顶点入度的数组FindInDegree(G,indegree);//统计各顶点的入度//建立栈结构,程序中使用的是链表stack *S;initStack(&S);//查找度为0的顶点,作为起始点for (int i=0; i<G.vexnum; i++) {if (!indegree[i]) {push(S, i);}}int count=0;//当栈为空,说明排序完成while (!StackEmpty(*S)) {int index;//弹栈,并记录栈中保存的顶点所在邻接表数组中的位置pop(S,&index);printf("%d",G.vertices[index].data);++count;//依次查找跟该顶点相链接的顶点,如果初始入度为1,当删除前一个顶点后,该顶点入度为0for (ArcNode *p=G.vertices[index].firstarc; p; p=p->nextarc) {VertexType k=p->adjvex;if (!(--indegree[k])) {//顶点入度为0,入栈push(S, k);}}}//如果count值小于顶点数量,表明该有向图有环if (count<G.vexnum) {printf("该图有回路");return;}
}int main(){ALGraph *G;CreateAOV(&G);//创建AOV网TopologicalSort(*G);//进行拓扑排序return  0;
}

4.迪杰斯特拉(Dijkstra算法)算法

由荷兰计算机科学家艾兹赫尔·戴克斯特拉在1956年提出。戴克斯特拉算法使用了广度优先搜索解决赋权有向图的单源最短路径问题








#include <iostream>
#include <bits/stdc++.h>
using namespace std;#define MAX 100
#define MAXCOST 0x7fffffff  //int型最大值void prim(int graph[][MAX],int n,int start,int end)
{int lowcost[MAX];int mst[MAX];int sum=0;for(int i=1;i<=n;i++)//将与各点与起始点的距离存入lowcost中{lowcost[i]=graph[start][i];mst[i]=1;}mst[start]=0;  //起始点被标记for(int i=1;i<=n;i++){if(mst[end]==0)//终点被标记结束{cout<<sum;break;}int min=MAXCOST;int minid=0;for(int j=1;j<=n;j++)//遍历lowcost数组,找最小值{if(lowcost[j]<min && mst[j]!=0){min=lowcost[j]; //最小值minid=j; //最小值下标}}//cout<<"V"<<mst[minid]<<"-V"<<minid<<"="<<min<<endl;sum=min;//cout<<sum<<endl;mst[minid]=0;  //最小值下标点被标记for(int j=1;j<=n;j++)//找最小值点与各点的距离{if(graph[minid][j]==MAXCOST)//如果此点与最小值点没有联系(距离为最大值)则lowcost不变,跳过{continue;}else if(graph[minid][j]+sum<lowcost[j] && mst[j]!=0)//此点与最小点有联系,并且+sum<lowcost 并且此点没有被标记,则赋值给lowcost{lowcost[j]=graph[minid][j]+sum;}}}
}int main()
{int n,m;int start,end;int graph[MAX][MAX];cin>>n>>m>>start>>end;//初始化图Gfor(int i=1;i<=n;i++){for(int j=1;j<=n;j++){graph[i][j]=MAXCOST;}}//构建图Gfor(int k=1;k<=m;k++){int i,j,cost;cin>>i>>j>>cost;graph[i][j]=cost;graph[j][i]=cost;}prim(graph,n,start,end);return 0;
}

5.弗洛伊德算法


暑假,小哼准备去一些城市旅游。有些城市之间有公路,有些城市之间则没有,如下图。为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程。

上图中有4个城市8条公路,公路上的数字表示这条公路的长短。

请注意这些公路是单向的。我们现在需要求任意两个城市之间的最短路程,也就是求任意两个点之间的最短路径。这个问题这也被称为“多源最短路径”问题

  • 现在需要一个数据结构来存储图的信息,我们仍然可以用一个4*4的矩阵(二维数组e)来存储。
  • 比如1号城市到2号城市的路程为2,则设e[1][2]的值为2。
  • 2号城市无法到达4号城市,则设置e[2][4]的值为∞。
  • 另外此处约定一个城市自己是到自己的也是0,例如e[1][1]为0,具体如下。

  • 现在回到问题:如何求任意两点之间最短路径呢?通过之前的学习我们知道通过深度或广度优先搜索可以求出两点之间的最短路径

  • 所以进行n2遍深度或广度优先搜索,即对每两个点都进行一次深度或广度优先搜索,便可以求得任意两点之间的最短路径。可是还有没有别的方法呢?

  • 我们来想一想,根据我们以往的经验,如果要让任意两点(例如从顶点a点到顶点b)之间的路程变短,只能引入第三个点(顶点k),并通过这个顶点k中转即a->k->b,才可能缩短原来从顶点a点到顶点b的路程

  • 那么这个中转的顶点k是1~n中的哪个点呢?甚至有时候不只通过一个点,而是经过两个点或者更多点中转会更短,即a->k1->k2b->或者a->k1->k2…->k->i…->b。

  • 比如上图中从4号城市到3号城市(4->3)的路程e[4][3]原本是12。如果只通过1号城市中转(4->1->3),路程将缩短为11(e[4][1]+e[1][3]=5+6=11)。

  • 其实1号城市到3号城市也可以通过2号城市中转,使得1号到3号城市的路程缩短为5(e[1][2]+e[2][3]=2+3=5)。

  • 所以如果同时经过1号和2号两个城市中转的话,从4号城市到3号城市的路程会进一步缩短为10。

  • 通过这个的例子,我们发现每个顶点都有可能使得另外两个顶点之间的路程变短。好,下面我们将这个问题一般化。

  • 当任意两点之间不允许经过第三个点时,这些城市之间最短路程就是初始路程,如下。

  • 假如现在只允许经过1号顶点,求任意两点之间的最短路程,应该如何求呢?
  • 只需判断e[i][1] + e[1][j]是否比e[i][j]要小即可。
  • e[i][j]表示的是从i号顶点到j号顶点之间的路程。e[i][1] + e[1][j]表示的是从i号顶点先到1号顶点,再从1号顶点到j号顶点的路程之和其中i是1~n循环,j也是1~n循环,代码实现如下。
for (i = 1; i <= n; i++)
{for (j = 1; j <= n; j++){if (e[i][j] > e[i][1] + e[1][j])e[i][j] = e[i][1] + e[1][j];}
}
  • 在只允许经过1号顶点的情况下,任意两点之间的最短路程更新为:

  • 通过上图我们发现:在只通过1号顶点中转的情况下,3号顶点到2号顶点(e[3][2])、4号顶点到2号顶点(e[4][2])以及4号顶点到3号顶点(e[4][3])的路程都变短了。

  • 接下来继续求在只允许经过1和2号两个顶点的情况下任意两点之间的最短路程。

  • 如何做呢?我们需要在只允许经过1号顶点时任意两点的最短路程的结果下,再判断如果经过2号顶点是否可以使得i号顶点到j号顶点之间的路程变得更短

  • 即判断e[i][2]+e[2][j]是否比e[i][j]要小,代码实现为如下。

//经过1号顶点
for(i=1;i<=n;i++)  for(j=1;j<=n;j++)  if (e[i][j] > e[i][1]+e[1][j])  e[i][j]=e[i][1]+e[1][j];  //经过2号顶点
for(i=1;i<=n;i++)  for(j=1;j<=n;j++)  if (e[i][j] > e[i][2]+e[2][j])  e[i][j]=e[i][2]+e[2][j];
  • 在只允许经过1和2号顶点的情况下,任意两点之间的最短路程更新为:

  • 通过上图得知,在相比只允许通过1号顶点进行中转的情况下,这里允许通过1和2号顶点进行中转,使得e[1][3]和e[4][3]的路程变得更短了。

  • 同理,继续在只允许经过1、2和3号顶点进行中转的情况下,求任意两点之间的最短路程。任意两点之间的最短路程更新为:

  • 最后允许通过所有顶点作为中转,任意两点之间最终的最短路程为:

#include <stdio.h>int main()
{int e[10][10],k,i,j,n,m,t1,t2,t3;int inf=99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值//读入n和m,n表示顶点个数,m表示边的条数scanf("%d %d",&n,&m);//初始化for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(i==j) e[i][j]=0;else e[i][j]=inf;//读入边for(i=1;i<=m;i++){scanf("%d %d %d",&t1,&t2,&t3);e[t1][t2]=t3;}//Floyd-Warshall算法核心语句for(k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(e[i][j]>e[i][k]+e[k][j] )e[i][j]=e[i][k]+e[k][j];//输出最终的结果for(i=1;i<=n;i++){for(j=1;j<=n;j++){printf("%10d",e[i][j]);}printf("\n");}return 0;
}

数据结构--图(Graph)详解(四)相关推荐

  1. 数据结构--图(Graph)详解(一)

    数据结构–图(Graph)详解(一) 文章目录 数据结构--图(Graph)详解(一) 一.图的基本概念 1.图的分类 2.弧头和弧尾 3.入度和出度 4.(V1,V2) 和 < V1,V2 & ...

  2. 数据结构--图(Graph)详解(三)

    数据结构–图(Graph)详解(三) 文章目录 数据结构--图(Graph)详解(三) 一.深度优先生成树和广度优先生成树 1.铺垫 2.非连通图的生成森林 3.深度优先生成森林 4.广度优先生成森林 ...

  3. 数据结构--图(Graph)详解(二)

    数据结构–图(Graph)详解(二) 文章目录 数据结构--图(Graph)详解(二) 一.图的存储结构 1.图的顺序存储法 2.图的邻接表存储法 3.图的十字链表存储法 4.图的邻接多重表存储法 二 ...

  4. echart关系树状图_echart——关系图graph详解

    VueEchart组件见上一篇 export default { data () { const title = { // show: true, //是否显示 text: "画布关系图&q ...

  5. 数据结构之链表详解(2)——双向链表

    目录 前言 一.双向链表 A.双向链表的含义 B.双向链表的实现 1.双向链表的结构 2.链表的初始化 初始化图解: 函数代码: 3.动态申请节点函数 函数代码: 4.打印双向链表函数 函数代码: 5 ...

  6. linux 进程间通信 dbus-glib【实例】详解四(上) C库 dbus-glib 使用(附代码)(编写接口描述文件.xml,dbus-binding-tool工具生成绑定文件)(列集散集函数)

    linux 进程间通信 dbus-glib[实例]详解一(附代码)(d-feet工具使用) linux 进程间通信 dbus-glib[实例]详解二(上) 消息和消息总线(附代码) linux 进程间 ...

  7. chia 的 p 图过程详解及优化攻略

    仅仅两个⽉的时间,chia 全⽹算⼒就突破了 15EP,1p 算⼒单⽇收益也从最初的三十多个变成了零点⼏个.在这场激烈的博弈中,⼤家为了更快的⼊场,都想加快 p 图速度,⽽事实证明,较快的 p 图速度 ...

  8. 数据结构之树结构详解

    树的定义 树是一种很特别的数据结构,树这种数据结构叫做"树"就是因为它长得像一棵树.但是这棵树画成的图长得却是一棵倒着的树,根在上,叶在下. 树是图的一种,树和图的区别就在于:树是 ...

  9. AI 绘画Stable Diffusion 研究(五)sd文生图功能详解(下)

    大家好,我是风雨无阻. 上一篇文章详细介绍了sd文生图的功能及使用注意事项,感兴趣的朋友可以前往查看:AI 绘画Stable Diffusion 研究(四)sd文生图功能详解(上) . 那今天这篇文章 ...

最新文章

  1. java 视图对象转换,使用spring boot开发时java对象和Json对象转换的问题_JavaScript_网络编程...
  2. Linux启动重启停止DNS,ubuntu怎么开机停止启动smbd
  3. prototype 1.5 中文说明.doc
  4. Android Loader 异步加载详解一:基础概念
  5. Android学习资源网站
  6. Hybris商品图片导入与压缩有关的配置.
  7. Visual SourceSafe使用流程指南
  8. wamp php不可用_解析wamp的php.ini设置不生效
  9. 贪吃蛇程序 php,PHP下利用PHPMailer Web程序【tofacebook.com】 - 贪吃蛇
  10. 《数据结构:c语言版》(严蔚敏)知识点整合
  11. 新书推荐——Windows Server系统配置与管理项目化教程(Windows Server2016微课版)
  12. python超市管理系统流程图_python实现超市管理系统(后台管理)
  13. DVWA(全级别通关教程详解)
  14. 7-2 The Judger (25分)
  15. The Thirty-eighth Of Word-Day
  16. JTAG、SBW、BSL 三种接口的区别
  17. SmartUplod中文乱码问题(已解决)
  18. [Shader]NGUI与灰化
  19. Python爬虫爬取美女写真实例
  20. 【必备算法】动态规划:LeetCode题(六)322. 零钱兑换,518. 零钱兑换 II

热门文章

  1. php 微信机器人_微信小程序机器人自动客服功能
  2. CSS实现【表格内容超过一行的部分,用省略号代替】
  3. 腾讯云10亿扶持小程序:3元套餐可能免费
  4. Material Design 组件之 CollapsingToolbarLayout
  5. Android基础控件ProgressBar进度条的使用
  6. 礼赞 Wordpress,蝉知可直接使用 Wordpress 模板
  7. 序列变换(Lis变形)
  8. 用BlazeMeter录制JMeter测试脚本
  9. [转]run for a girl
  10. 背包 http://blog.csdn.net/insistgogo/article/details/8579597