图的定义与术语

1、图按照有无方向分为无向图和有向图。无向图由顶点和边构成,有向图由顶点和弧构成。弧有弧尾和弧头之分。

2、如果任意两个顶点之间都存在边叫完全图,有向的叫有向完全图。若无重复的边或顶点到自身的边则叫简单图。

3、图中顶点之间有邻接点、依附的概念。无向图顶点的边数叫做度,有向图顶点分为入度和出度。

4、图上的边或弧上带权则称为网。

5、图中顶点间存在路径,两顶点存在路径则说明是连通的,如果路径最终回到起始点则称为环,当中不重复叫简单路径。若任意两顶点都是连通的,则图就是连通图,有向则称为强连通图。

6、无向图中连通且n个顶点n-1条边叫生成树。有向图中一顶点入度为0其余顶点入度为1的叫有向树。

图的存储结构

邻接矩阵

图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组存储图中的边或弧的信息。

邻接矩阵存储的结构代码如下:

typedef  char VertexType;/*顶点类型应由用户定义*/

typedef int  EdgeType;/*边上的权值类型应由用户定义*/

#define MAXVEX  100//最大顶点数,由用户定义

#define INFINITY  65535//用65535来代表无限

typedef struct {

     VertexType vexs[MAXVEX];//顶点表

     EdgeType arc[MAXVEX][MAXVEX];//邻接矩阵,可看作边表

     int  numVertexes, numEdges;//图中当前的顶点数和边数

}MGraph;

/*建立无向网图的邻接矩阵表示*/

void CreateMGraph(MGraph *G) {

    int i,j,k,w;

    printf("输入顶点数和边数:\n");

    scanf("%d,%d",&G->numVertexes,&G->numEdges);/*输入顶点数和边数*/

    for(i=0;i<G->numVertexes;i++)/*读入顶点信息,建立顶点表*/

          scanf(&G->vexs[i]);

    for(i=0;i<G->numVertexes;i++)

           for(j=0;j<G->numVertexes;j++)

                     G->arc[i][j]=INFINITY;/*邻接矩阵初始化*/

     for(k=0;k<G->numEdges;k++)/*读入numEdges条边,建立邻接矩阵*/

     {

            printf("输入边(vi,vj)上的下标i,小标j和权w:\n");

             scanf("%d,%d,%d",&i,&j,&w);/*输入边(vi,vj)上的权w*/

            G->arc[i][j]=w;

            G->arc[j][i] =G->arc[i][j];/*因为是无向图,矩阵对称*/

       }

}

邻接表

邻接表存储方法:

1、图中顶点用一维数组存储,每个数据元素存储指向第一个邻接点的指针,以便于查找该顶点的边信息。

2、图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。

结点定义代码:

typedef char  VertexType;/*顶点类型应由用户定义*/

typedef  int EdgeType;/*边上的权值类型应由用户定义*/

typedef  struct  EdgeNode/*边表结点*/

{
    int adjvex;                         /*邻接点域,存储该顶点对应下标*/

    EdgeType  weight;           /*用于存储权值,对于非网图可以不需要*/

    struct  EdgeNode *next; /*链域,指向下一个邻接点*/

}EdgeNode;

typedef struct VertexNode/*顶点表结点*/

{
     VertexType data;           /*顶点域,存储顶点信息*/

     EdgeNode  *firstedge;/*边表头指针*/

}VertexNode,AdjList[MAXVEX];

typedef  struct{

     AdjList adjList;

     int  numVertexes,numEdges;    /*图中当前顶点数和边数*/

}GraphAdjList;

无向图邻接表创建代码:

/*建立图的邻接表结构*/

void  CreateALGraph(GraphAdjList *G) {

     int i,j,k;

     EdgeNode  *e;

     printf("输入顶点数和边数:\n");

      scanf("%d,%d",&G->numVertexes,&G->numEdges);/*输入顶点数和边数*/

      for(i = 0; i < G->numVertexes;i++)/*读入顶点信息,建立顶点表*/

       {

                  scanf(&G->adjList[i].data);/*输入顶点信息*/

                  G->adjList[i].firstedge=NULL;/*将边表置空*/

         }

        for(k = 0; k < G->numEdges;k++) {/*建立边表*/

                printf("输入边(vi,vj)上的顶点序号:\n");

                 scanf("%d,%d",&i, &j);/*输入边(vi,vj)上的顶点序号*/

                 e=(EdgeNode*)malloc(sizeof(EdgeNode));/*向内存申请空间,生成边表结点*/

                 e->adjvex=j;/*邻接序号为j*/

                 e->next=G->adjList[i].firstedge;/*将e指针指向当前顶点指向的结点*/

                 G->adjList[i].firstedge=e;/*将当前顶点的指针指向e*/

                 e=(EdgeNode*)malloc(sizeof(EdgeNode));/*向内存申请空间,生成边表结点*/

                  e->adjvex=i;

                 e->next=G->adjList[j].firstedge;/*将e指针指向当前顶点指向的结点*/

                 G->adjList[j].firstedge=e;/*将当前顶点的指针指向e*/

           }

}

剩余的图的存储结构还有,十字链表,邻接多重表,边集数组。

图的遍历

深度优先遍历

//邻接矩阵的深度优先遍历

typedef int Boolean;  /*Boolean是布尔类型,其值是TRUE或FALSE*/

Boolean visited[MAX];/*访问标志的数组*/

/*邻接矩阵的深度优先递归算法*/

void DFS(MGraph G, int i) {

    int j;

    visited[i] = TRUE;

    printf("%c", G.vexs[i]);/*打印顶点,也可以其他操作*/

     for(j = 0; j < G.numVertexes; j++)

               if(G.arc[i][j] == 1 && !visited[j])

                               DFS(G, j);/*对为访问的邻接顶点递归调用*/

}

/*邻接矩阵的深度遍历操作*/

void DFSTraverse(MGraph G) {

    int i;

    for(i = 0; i < G.numVertexes; i++)

                  visited[i] = FALSE;/*初始所有顶点状态都是未访问状态*/

      for(i = 0; i < G.numVertexes; i++)

                  if(!visited[i])/*对未访问过的顶点调用DFS,若是连通图,只会执行一次*/

                             DFS(G, i);

}

/*邻接表的深度优先递归算法*/

void DFS(GraphAdjList GL, int i) {

    EdgeNode *p;

    visited[i] = TRUE;

    printf("%c", GL->adjList[i].data);/*打印顶点,也可以其他操作*/

    p = GL->adjList[i].firstedge;

    while(p) {

         if(!visited[p->adjvex])

                   DFS(GL, p-adjvex);/*对为访问的邻接顶点递归调用*/

         p = p ->next;

        }

}

/*邻接表的深度遍历操作*/

void DFSTraverse(GraphAdjList GL) {

      int i;

        for(i = 0; i < GL->numVertexes; i++)

                   visited[i] = FALSE;/*初始所有顶点状态都是未访问过状态*/

         for(i = 0; i < GL->numVertexes; i++)

                   if(!visited[i])/*对未访问过的顶点调用DFS,若是连通图,只会执行一次*/

                               DFS(GL, i);

}

广度优先遍历

/*邻接矩阵的广度优先遍历*/

void BFSTraverse(MGraph G) {

       int i, j;

       Queue Q;

        for(i =0; i < G.numVertexes; i++)

                 visited[i] = FALSE;

          InitQueue(&Q);/*初始化一辅助队列*/

          for(i = 0; i < G.numVertexes; i++)/*对每一个顶点做循环*/

           {

                          if(!visited[i]){/*若是未访问过就处理*/

                                  visited[i] = TRUE;/设置当前顶点访问过*/

                                   printf("%c", G.vexs[i]);/*打印顶点,也可以其他操作*/

                                     EnQueue(&Q,i);/*将此顶点入队列*/

                                      while(!QueueEmpty(Q)){ /*若当前队列不为空*/ 

                                                    DeQueue(&Q, &i);/*将队中元素出队列,赋值给i*/

                                                     for(j = 0; j< G.numVertexes; j++) {

                                                              /*判断其他顶点若与当前顶点存在边且未访问过*/

                                                               if(G.arc[i][j] == 1 && !visited[j]) {

                                                                               visited[j]=TRUE;/*找到的此顶点标记已访问*/

                                                                               printf("%c",G.vexs[j]);/*打印顶点*/

                                                                                EnQueue(&Q, j);/*将找到的此顶点入队列*/

                                                                     }

                                                             }

                                               }

                                     }

                    }

}

/*邻接表的广度优先遍历算法*/

void BFSTraverse(GraphAdjList GL) {

        int i;

         EdgeNode *po;

         Queue Q;

         for(i = 0; i < GL->numVertexes; i++)

                   visited[i] = FALSE;

          InitQueue(&Q);

          for( i = 0; i < GL->numVertexes; i++) {

                       if(!visited[i]) {

                                  visited[i] = TRUE;

                                  printf("%c", GL->adjList[i].data);/*打印顶点,也可以其他操作*/

                                   EnQueue(&Q, &i);

                                   while(!QueueEmpty(Q)) {

                                           DeQueue(&Q, &i);

                                              p = GL ->adjList[i].firstedge; /*找到当前顶点边表链表头指针*/

                                                while(p) {

                                                         if(!visitied[p->adjvex]) {

                                                                     visited[p->adjvex] = TRUE;

                                                                       printf("%c", GL->adjList[p->adjvex].data);

                                                                       EnQueue(&Q, p ->adjvex);/*将此顶点入队列*/

                                                             }

                                                             p=p->next;

                                                      }

                                             }

                                 }

              }

}

最小生成树

/*prim算法生成最小生成树*/

void MiniSpanTree_Prim(MGraph G) {

            int min, i , j, k;

            int  adjvex[MAXVEX];/*保存相关顶点下标*/

            int lowcost[MAXVEX];/*保存相关顶点间边的权值*/

            lowcost[0] = 0;/*初始化第一个权值为0,即V0加入生成树*/

                                     /*lowcost的值为0,在这里就是此下标的顶点已经加入生成树*/

            adjvex[0] = 0;/*初始化第一个顶点下标为0*/

            for(i = 1; i < G.numVertexes; i ++)/*循环除小标为0外的全部顶点*/

            {

                             lowcost[i] = G.arc[0][i];/*将V0顶点与之有边的权值存入数组*/

                              adjvex[i] = 0;/*初始化都为V0的下标*/

             }

            for(i = 0; i < G.numVertexes; i++) {

                       min = INFINITY;/*初始化最小权值*/

                      j = 1; k = 0;

                      while(j < G.numVertexes) { /*循环全部顶点*/

                                if(lowcost[j] != 0 && lowcost[j] < min) {/*如果权值不为0且权值小于min*/

                                              min = lowcost[j];                      /*则让当前权值成为最小值*/

                                               k = j;

                                  }

                                 j++;

                         }

                        printf("(%d,%d)",adjvex[k], k);/*打印当前顶点边中权值最小边*/

                         lowcost[k] = 0;/*将当前顶点的权值设置为0,表示此顶点已经完成任务*/

                        for(j = 1; j < G.numVertexes; j ++)/*循环所有顶点*/

                          {

                                       if(lowcost[j] != && G.arc[k][j] <lowcost[j]) {

                                           /*若小标为k顶点各边权值小于此前这些顶点未被加入生成树权值*/

                                                              lowcost[j] = G.arc[i][j];/*将较小权值存入lowcost*/

                                                               adjvex[j] = k;/*将下标为k的顶点存入adjvex*/

                                               }

                                   }

                    }

}

/*Kruskal算法生成最小生成树*/

void MiniSpanTree_Kruskal(MGraph G) /*生成最小生成树*/

{

          int i, n , m;

           Edge edges[MAXEDGE];/*定义边集数组*/

          int parent[MAXVEX];/*定义一数组用来判断边与边是否形成回路*/

           /*此处省略将邻接矩阵G转化为边集数组edges并按权有小到大排序的代码*/

         for(i = 0; i < G.numVertexes; i++)

                     parent[i] = 0;/*初始化数组值为0*/

        for(i = 0; i <G.numEdges; i++) { /*循环每一条边*/

                    n = Find(parent, edges[i].begin);

                     m = Find(parent, edges[i].end);

                    if(n != m)/*假如n与m不等,说明此边没有与现有生成树形成环路*/

                      {

                                   parent[n] = m;/*将此边的结尾顶点放入下标为起点的parent中,表示此顶点已经在生成树集合中*/

                                  printf("(%d, %d) %d", edges[i].begin, edges[i].end, edges[i].weight);

                         }

               }

}

int Find(int *parent, int f) /*查找连线顶点的尾部下标*/

{

     while(parent[f]>0)

              f = parent[f];

     return f;

}

最短路径

对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。

Dijkstra算法

#define  MAXVEX  9

#define INFINITY 65535

typedef  int  Patharc[MAXVEX];/*用于存储最短路径下标的数组*/

typdef  int ShortPathTable[MAXVEX];/*用于存储到各点最短路径的权值和*/

/*Dijkstra算法,求有向网G的V0顶点到其余顶点V最短路径P[v]及带权长度D[v]*/

/* P[v]的值为前驱顶点下标,D[v]表示V0到V的最短路径长度和。*/

void  ShortestPath_Dijkstra (MGraph G,int v0, Patharc *p, ShortPathTable *D) {

    int v, w, k ,min;

     int  final[MAXVEX];/*final[w]=1表示求得顶点V0至Vw的最短路径*/

     for(v=0; v< G.numberVertexes; v++)/*初始化数据*/

     {

               final[v]= 0;/*全部顶点初始化为未知最短路径状态*/

                (*D)[v]= G.arc[v0][v];/*将与V0点有连线的顶点加上权值*/

                 (*P)[v]=0;/*初始化路径数组P为0*/

          }

       (*D)[v0] = 0;/*V0至V0路径为0*/

        final[v0] = 1;/*V0至V0不需要路径*/

        /*开始主循环,每次求得V0到某个V顶点的最短路径*/

       for(v=1; v< G.numVertexes; v++) {

                   min = INFINITY;/*当前所知离V0顶点的最近距离*/

                    for(w=0; w<G.numVertexes; w++)/*寻找离V0最近的顶点*/

                   {

                                if(!final[w] && (*D)[w]<min){

                                              k = w;

                                               min=(*D)[w];/*w顶点离V0顶点更近*/

                                     }

                          }

                         final[k] = 1;/*将目前找到的最近的顶点置为1*/

                        for(w=0; w < G.numVertexes; w++) /*修正当前最短路径及距离*/

                        {

                                         /*如果经过V顶点的路径比现在这条路径的长度短的话*/

                                         if(!final[w]&& (min + G.arc[k][w] < (*D)[w]))

                                         {/*说明找到了更短的路径,修改D[w]和P[w]*/

                                                        (*D)[w] = min+G.arc[k][w];/*修改当前路径长度*/

                                                          (*P)[w]= k;

                                             }

                                }

               }

}

Floyd算法

typedef int Pathmatirx[MAXVEX][MAXVEX];

typedef int ShortPathTable[MAXVEX][MAXVEX];

/*Floyd算法,求网图G中各顶点v到其余顶点w最短路径P[v][w]及带权长度D[v][w]*/

void ShortestPath_Floyd(MGraph G, Pathmatirx *P, ShortPathTable *D) {

     int  v, w, k;

     for(v = 0; v < G.numVertexes; ++v)/*初始化D与P*/

      {

                  for(w = 0; w < G.numVertexes; ++w)

                {

                            (*D)[v][w] = G.matirx[v][w];/*D[v][w]值即为对应点间的权值*/

                              (*P)[v][w] = w;/*初始化P*/

                  }

            }

            for(k = 0; k < G.numVertexes; ++k)

             {

                       for(v = 0; v < G.numVertexes; ++v)

                       {

                                   for(w=0; w < G.numVertexes; ++w)

                                     {

                                                  if((*D)[v][w]>(*D)[v][k]+(*D)[k][w])

                                                 {/*如果经过下标为k顶点路径比原两点间路径更短*/

                                                     /*将当前两点间权值设为更小的一个*/

                                                                 (*D)[v][w] = (*D)[v][k] + (*D)[k][w];

                                                                  (*P)[v][w]= (*P)[v][k];/*路径设置经过下标为k的顶点*/        

                                          }

                             }

               }

     }

}

求最短路径代码

for(v=0; v< G.numVertexes; v++)

{

          for(w = v+ 1; w < G.numVertexes; w++)

          {

                      printf("v%d-v%d weight:%d",v, w, D[v][w]);

                     k =P[v][w];/*获得第一个路径顶点下标*/

                      printf("path: %d",v);/*打印源点*/

                       while(k!=w)/*如果路径顶点下标不是终点*/

                       {

                                  printf("-> %d", k );/*打印路径顶点*/

                                    k=P[k][w];/*获得下一个路径顶点下标*/

                         }

                        printf("-> %d\n",w);/*打印终点*/

                }

                 printf("\n");

}

拓扑排序

设G=(V,E)是一个具有n个顶点的有向图,V中的顶点序列v1,v2,·····,vn,满足若从顶点vi到vj有一条路径,则在顶点序列中顶点vi必在顶点vj之前。则我们称这样的顶点序列为一个拓扑序列。

所谓拓扑排序,其实就是对一个有向图构造拓扑序列的过程。

拓扑排序算法

结构代码:

typedef struct EdgeNode/*边表结点*/

{
        int adjvex;/*邻接点域,存储该顶点对应的下标*/

        int weight;/*用于存储权值,对于非网图可以不需要*/

        struct EdgeNode *next;/*链域,指向下一个邻接点*/

}EdgeNode;


typedef struct VertexNode/*顶点表结点*/

{
        int in;/*顶点入度*/

         int  data;/*顶点域,存储顶点信息*/

        EdgeNode *firstedge;/*边表头指针*/

}VertexNode,AdjList[MAXVEX];


typedef struct

{

      AdjList adjList;

       int numVertexes, numEdges;/*图中当前顶点数和边数*/

}graphAdjList, *GraphAdjList;

/*拓扑排序,若GL无回路,则输出拓扑排序序列并返回OK,若有回路返回ERROR*/

Status TopologicalSort(GraphAdjList GL)

{

        EdgeNode *e;

         int i, k, gettop;

          int top= 0;/*用于栈指针下标*/

        int count = 0;/*用于统计输出顶点的个数*/

         int *stack;/*建栈存储入度为0的顶点*/

        stack = (int *)malloc(GL->numVertexes * sizeof(int));

        for(i = 0; i < GL->numVertexes; i++)

               if(GL->adjList[i].in == 0)

                       stack[++top] = i;/*将入度为0的顶点入栈*/

       while(top != 0)

       {

               gettop=stack[top--];/*出栈*/

                printf("%d -> ",GL->adjList[gettop].data);/*打印此顶点*/

               count++;/*统计输出顶点数*/

              for(e=GL->adjList[gettop].firstedge; e; e= e->next)

              {/*对此顶点弧表遍历*/

                         k=e->adjvex;

                        if(!(--GL->adjList[k].in))/*将k号顶点邻接点的入度减1*/

                                     stack[++top]=k;/*若为0则入栈,以便于下一次循环输出*/

                 }

       }

      if(count<GL->numVertexes)/*如果count小于顶点数,说明存在环*/

                return  ERROR;

        else

                return OK;

}

关键路径

路径上各个活动所持续的时间之和称为路径长度,从源点到汇点具有最大长度的路径叫关键路径,在关键路径上的活动叫关键活动。

关键路径算法

全局变量:

int *etv,*ltv;/*事件最早发生时间和最迟发生时间数组*/

int *stack2;/*用于存储拓扑序列的栈*/

int top2;/*用于stack2的指针*/

/*拓扑排序,用于关键路径计算*/

Status TopologicalSort(GraphAdjList GL) {

      EdgeNode *e;

       int i, k, gettop;

       int top = 0;/*用于栈指针下标*/

       int count = 0;/*用于统计输出顶点的个数*/

       int *stack;/*建栈将入度为0的顶点入栈*/

        stack = (int *)malloc(GL->numVertexes * sizeof(int));

       for(i = 0; i < GL.numVertexes; i++)

                if(0 == GL->adjList[i].in)

                       stack[++top] = i;

       top2= 0;/*初始化为0*/

        etv=(int *)malloc(GL->numVertexes * sizeof(int));/*事件最早发生时间*/

        for(i = 0; i < GL->numVertexes; i++)

                 etv[i]=0;/*初始化为0*/

          stack2=(int *)malloc(GL->numVertexes * sizeof(int));/*初始化*/

          while(top != 0) {

                  gettop=stack[top--];

                  count++;

                  stack2[++top2] =gettop;/*将弹出的顶点序号压入拓扑序列的栈*/

                   for(e = GL->adjList[gettop].firstedge; e; e= e->next) {

                              k = e->adjvex;

                              if(!(--GL->adjList[k].in))

                                       stack[++top]=k;

                                if((etv[gettop] + e->weight)>etv[k])/*求各顶点事件最早发生时间值*/

                                           etv[k] = etv[gettop] + e->weight;

                         }

             }

              if(count < GL->numVertexes)

                       return ERROR;

               else

                        return OK;

}

/*求关键路径,GL为有向网,输出GL的各项关键活动*/

void CriticalPath(GraphAdjList GL) {

         EdgeNode *e;

         int  i, gettop, k ,j;

          int ete, lte;/*声明活动最早发生时间和最迟发生时间变量*/

          TopologicalSort(GL);/*求拓扑序列,计算数组etv和stack2的值*/

            ltv = (int *)malloc(GL->numVertexes * sizeof(int));/*事件最晚发生时间*/

          for(i = 0; i < GL->numVertexes; i++)

                   ltv[i] = etv[GL->numVertexes - 1];/*初始化ltv*/

           while(top2 ! = 0)/*计算ltv*/

          {

                     gettop=stack2[top2--];/*将拓扑序列出栈,后进先出*/

                    for(e = GL->adjList[gettop].firstedge; e; e = e->next) {

                     /*求各顶点事件的最迟发生时间ltv值*/

                                k = e->adjvex;

                                if(ltv[k]- e->weight < ltv[gettop])/*求各顶点事件最晚发生时间ltv*/

                                          ltv[gettop] = ltv[k] - e->weight;

                         }

             }

             for(j=0; j < GL->numVertexes; j++)/*求ete,lte和关键活动*/

             {

                        for(e->GL->adjList[j].firstedge; e; e= e->next) {

                         {

                                       k= e->adjvex;

                                        ete =etv[j];/*活动最早发生时间*/

                                        lte=ltv[k] -e->weight;/*活动最迟发生时间*/

                                        if(ete == lte)/*两者相等即在关键路径上*/

                                                  printf("<v%d,v%d> length: %d,",GL->adjList[j].data, GL->adjList[k].data,e->weight);

                                 }

                  }

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容