范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

长文警告!红黑树详解!

  红黑树
  首先红黑树是一种平衡树,通过红黑染色的方式保持平衡 术语解释NIL :NIL节点是一个空节点,我们可以认为它是一个空指针指向的节点,代表这里什么也没有,在红黑树中我们认为一个NIL节点的颜色默认为黑色,虽然并没有一个实际的节点储存这个颜色信息红黑树必须满足以下四条性质:Every node is either red or black. All NIL nodes (figure 1) are considered black. A red node does not have a red child. Every path from a given node to any of its descendant NIL nodes goes through the same number of black nodes.
  由以上四个限制可以得到以下性质:
  the path from the root to the farthest leaf is no more than twice as long as the path from the root to the nearest leaf
  这个结果很容易想到,根节点到达每个叶节点上的黑色节点数一样,那么最长路径和最短路径之间的节点数量差别只有红色节点,而红色节点不能连续出现(由限制3)可知),所以最长路径最多是最短路径的两倍(红黑交替和全黑)
  这就使得红黑树能够维持一个高平衡性,保证了最长路径和最短路径的差值不会太大Linux中的红黑树的使用
  在具体学习红黑树的代码之前,我们先看一下Linux源码中对红黑树使用的文档和代码: searchstruct mytype *my_search(struct rb_root *root, char *string) {       struct rb_node *node = root->rb_node;        while (node) {               struct mytype *data = container_of(node, struct mytype, node);               int result;                result = strcmp(string, data->keystring);                if (result < 0)                       node = node->rb_left;               else if (result > 0)                       node = node->rb_right;               else                       return data;       }       return NULL; }
  其中用到的数据结构定义如下: struct rb_root { 	struct rb_node *rb_node; };  struct rb_node { 	unsigned long  __rb_parent_color; 	struct rb_node *rb_right; 	struct rb_node *rb_left; };  struct mytype {       struct rb_node node;       char *keystring; };
  然后我们看一下  container_of   的定义:#define container_of(ptr, type, member)                        ({                                                             const typeof(((type*)0)->member)* __mptr = (ptr);          (type*)((char*)__mptr - offsetof(type, member));       })
  宏的作用是:
  接收指向一个对象(数据)的指针,这个数据所属于的结构体的类型,以及这个数据在结构体中的成员名,返回指向这个结构体的指针
  简而言之就是取容器
  所以  container_of(node, struct mytype, node)  这个宏的作用就是从 node  这个属于红黑树部分的结构体,获得它的容器,从而得到具体的数据
  这里 container_of()  的原理是通过计算指定成员的偏移量,以及其地址,计算出包含它的结构的地址
  这里采用了类似装饰器的想法, mytype  把红黑树节点包起来,使得数据可以和红黑树分开单独编写逻辑,这是在没有类和模板之类的东西的C语言下对代码复用的一种实现
  知道这一部分的设计思想后,搜索部分就是简单的BST的搜索过程,由于储存的是字符串,所以使用  strcmp()  来比较数据的大小insert
  下面是插入操作的代码: int my_insert(struct rb_root *root, struct mytype *data) {       struct rb_node **new = &(root->rb_node), *parent = NULL;        /* Figure out where to put new node */       while (*new) {               struct mytype *this = container_of(*new, struct mytype, node);               int result = strcmp(data->keystring, this->keystring);                parent = *new;               if (result < 0)                       new = &((*new)->rb_left);               else if (result > 0)                       new = &((*new)->rb_right);               else                       return FALSE;       }        /* Add new node and rebalance tree. */       rb_link_node(&data->node, parent, new);       rb_insert_color(&data->node, root);        return TRUE; }
  在寻找插入节点部分和一般的BST一样,找到插入位置后使用  rb_link_node()  将节点插入到红黑树中,然后使用 rb_insert_color()  来维护红黑树的性质(之前提到的四条规则)
  关于如何维护红黑树的性质及其具体代码,我们在后面的实现中再讨论removing and replacing
  删除操作和替换操作是Linux中自带实现的,只需要调用函数即可: void rb_erase(struct rb_node *victim, struct rb_root *tree); void rb_replace_node(struct rb_node *old, struct rb_node *new,                       struct rb_root *tree);
  只要找到了指定的节点,就可以通过这两个函数来删除或者替换节点 次序相关的操作struct rb_node *rb_first(struct rb_root *tree); struct rb_node *rb_last(struct rb_root *tree); struct rb_node *rb_next(struct rb_node *node); struct rb_node *rb_prev(struct rb_node *node);
  这四个函数可以在任意一棵红黑树中找到最小值、最大值、后继、前驱
  相关的操作在BST中都会介绍到 红黑树的操作
  现在知道了红黑树的限制,我们的工作就是如何让BST的操作能够满足上面的四个性质,下面我们一个个操作来说:
  (这里采用的是wikipedia的代码,linux的代码我们最后再看)
  首先请记住上图第一张中的N、P、S、C、D节点的位置,本文来自我的学习笔记,所以并没有很多配图,红黑树的核心操作就来源于这五个节点的变化,请对号入座插入
  首先关于红黑树所使用的结构如下: enum color_t { BLACK, RED };  struct RBnode {     // node of red–black tree   RBnode* parent;   // == NIL if root of the tree   RBnode* child[2]; // == NIL if child is empty     // The index is:     //   LEFT  := 0, if (key < parent->key)     //   RIGHT := 1, if (key > parent->key)   enum color_t color;   int key; };  #define NIL   NULL // null pointer  or  pointer to sentinel node #define LEFT  0 #define RIGHT 1 #define left  child[LEFT] #define right child[RIGHT]  struct RBtree { // red–black tree   RBnode* root; // == NIL if tree is empty };
  首先我们要插入一个新的节点,通过一般BST的方式找到该节点对应的位置: RBnode** find_place(RBtree* tree, int key) {         RBnode** node = &tree->root;         while (*node != nullptr) {                 if (key < (*node)->key)                         node = &(*node)->left;                 else if (key > (*node)->key)                         node = &(*node)->right;                 else                         return nullptr;         } }
  这里和Linux源码一样使用了指针的指针,用来得到结构  RBnode  的中指针 left  和 right  ,这样能够方便直接修改一个节点的父节点
  找到这个节点的位置后我么可以直接将其插入红黑树,为了维持红黑树的第四性质:
  Every path from a given node to any of its descendant NIL nodes goes through the same number of black nodes.
  我们默认插入的节点为红色,并且检测当前的节点的case
  这里的case指的是一个节点附近和它相连的一系列节点的构成情况,是否构成一个违反红黑树性质的情况
  我们把插入后的情况检查分为6种: case 1
  插入的节点的父节点是黑色,此时红黑树性质不被破坏,不做任何改动 case 2
  如果P节点和U节点都是红色,那么祖父节点一定是黑色,为了维护红黑树,我们需要把P和U节点染成黑色,然后把G节点染成红色,这样局部满足了红黑树性质
  同时,为了让祖父节点不破坏 role3 ,我们把N节点设置为G,然后循环这个过程,直到N节点为空,或者构成不需要修改的情况case 3
  如果插入的节点为根节点,则不需要改动 case 4
  如果插入的父节点是红色且为根节点,则改变父节点的颜色为黑色即可
  上述四种情况都很简单,代码如下: void RBinsert1(RBtree* T,         // -> red–black tree                struct RBnode* N,  // -> node to be inserted                struct RBnode* P,  // -> parent node of N ( may be NULL )                byte dir)  // side ( LEFT or RIGHT ) of P where to insert N {     struct RBnode* G;  // -> parent node of P     struct RBnode* U;  // -> uncle of N      N->color = RED;     N->left = NIL;     N->right = NIL;     N->parent = P;     if (P == NULL) {  // There is no parent         T->root = N;  // N is the new root of the tree T.         return;       // insertion complete     }     P->child[dir] = N;  // insert N as dir-child of P     // start of the (do while)-loop:     do {         if (P->color == BLACK) {             // Case_I1 (P black):             return;  // insertion complete         }         // From now on P is red.         if ((G = P->parent) == NULL)             goto Case_I4;  // P red and root         // else: P red and G!=NULL.         dir = childDir(P);  // the side of parent G on which node P is located         U = G->child[1 - dir];              // uncle         if (U == NIL || U->color == BLACK)  // considered black             goto Case_I56;                  // P red && U black                                             // Case_I2 (P+U red):         P->color = BLACK;         U->color = BLACK;         G->color = RED;         N = G;  // new current node                 // iterate 1 black level higher                 //   (= 2 tree levels)     } while ((P = N->parent) != NULL);     // end of the (do while)-loop
  wikipedia的代码和Linux源码实现的代码架构不太一样
  这里的代码将数据包含在节点类中,我们通过迭代找到需要插入位置的父节点,然后通过  dir  来确定插入的方向,然后直接将 N  接到 P  的后面
  同时设置 N  的默认颜色为红色等case 5 and 6
  接下来我们进入case 5和6,首先我们已经确定P节点为红色,然后U节点为黑色,这样我们就不能通过染色解决问题,这样不能两边同时改变 黑色节点数量 (我们接下来称为Black height)
  红黑树选择的解决方式如下: 首先我们需要保证  N  节点和P  节点都在同一方向,也就是说,如果N  节点是 P  节点的左子节点,那么 P  节点就是 G  节点的左子节点;如果 N  节点是 P  节点的右子节点,那么 P  节点就是 G  节点的右子节点
  如果不能满足以上要求的话,进入case 5: 将  P  节点按照自己所在的方向旋转,即,如果 P  节点是左子节点, N  节点是右子节点,那么将 P  节点进行左旋,然后将旋转后的 P  节点设置为新的 N  节点
  这样我们就构建出了一个case 6:
  此时我们发现一个问题,旋转操作是会改变RBtree的Black Height的,简单列举几种情况即可发现这个问题
  但是这里旋转的两个节点都是红色节点,所以对于Black Height并没有影响,但是对于case 6而言,我们就需要对黑色节点进行旋转了 我们将  G  节点按照 P  节点所在方向的反方向旋转,使得 P  节点代替原本 G  节点的位置,此时右子树的Black height增加1,左子树的Black Height减少1,为了维护role 4,我们将 G  节点染为红色,P  节点染为黑色,此时我们发现,role 3和role 4同时满足了
  这么一来,插入的6种case就考虑完毕了,下面是case 5、6的代码部分: if (N == P->child[1 - dir]) {  // Case_I5 (P red && U black && N inner                                // grandchild of G):     RotateDir(P, dir);         // P is never the root     N = P;                     // new current node     P = G->child[dir];         // new parent of N                                // fall through to Case_I6 } // Case_I6 (P red && U black && N outer grandchild of G): RotateDirRoot(T, G, 1 - dir);  // G may be the root P->color = BLACK; G->color = RED; return;  // insertion complete          // end of RBinsert1 删除
  删除操作的简单情况: 删除节点为根节点,直接删除即可 一个节点如果只有一个非NIL子节点,那么这个子节点一定是红色,如果是黑色,那么Role 4一定会被破坏,黑色子节点至少多贡献一个Black Height 这样的话,如果 N  是红色节点,那么它就不能只拥有一个节点,而是要么没有子节点,要么有两个黑色子节点如果  N  是黑色节点,那么它可以拥有一个红色子节点,或者没有子节点,或者两个黑色子节点
  对于一个只有一个子节点的红色节点,由于一定没有子节点,所以我们可以直接删除这个节点
  对于只有一个子节点的黑色节点,其子节点一定是红色,我们可以将其直接替换删除节点,然后将其染为黑色,从而满足Role 4
  考虑了只有一个子节点和根节点的简单情况后,我们来考虑有两个子节点的情况: 如果一个节点同时拥有左右节点的话,我们可以寻找其前驱或者后继进行替换,然后将后继节点作为对象删除即可
  前驱或后继节点一定是只有一个子节点,或者本身就是叶子节点,所以至此我们只剩下最后一种情况:
  如果一个黑色节点没有子节点,我们将其删除后必然会破坏Role 4,此时需要进行删除后的红黑树维护 删除后的维护
  删除后操作我们总共要关注5个节点的情况:
  S  、 N  、P  、C  和 D  节点case 1
  如果删除的黑色节点是一个新的根节点,那么直接删除即可 Case_D1 (P == NULL):         return; // deletion complete case 2
  C  和 D  节点我们认为是 S  节点的子节点
  S  节点是 N  节点的兄弟节点,当 S  节点和 P  节点都是黑色,且C  和 D  节点也是黑色时,采取以下操作:我们将  S  节点染为红色,但是这样会使得经过S节点的路径Black Height减少,局部维护的同时破坏了整体的Role 4,所以我们把 P  节点当做新的 N  节点,继续进行删除后的维护void RBdelete2(   RBtree* T,         // -> red–black tree   struct RBnode* N)  // -> node to be deleted  {   struct RBnode* P = N->parent;  // -> parent node of N   byte dir;          // side of P on which N is located (∈ { LEFT, RIGHT })   struct RBnode* S;  // -> sibling of N   struct RBnode* C;  // -> close   nephew   struct RBnode* D;  // -> distant nephew    // P != NULL, since N is not the root.   dir = childDir(N); // side of parent P on which the node N is located   // Replace N at its parent P by NIL:   P->child[dir] = NIL;   goto Start_D;      // jump into the loop    // start of the (do while)-loop:   do {     dir = childDir(N);   // side of parent P on which node N is located Start_D:     S = P->child[1-dir]; // sibling of N (has black height >= 1)     D = S->child[1-dir]; // distant nephew     C = S->child[  dir]; // close   nephew     if (S->color == RED)       goto Case_D3;                  // S red ===> P+C+D black     // S is black:     if (D != NIL && D->color == RED) // not considered black       goto Case_D6;                  // D red && S black     if (C != NIL && C->color == RED) // not considered black       goto Case_D5;                  // C red && S+D black     // Here both nephews are == NIL (first iteration) or black (later).     if (P->color == RED)       goto Case_D4;                  // P red && C+S+D black     // Case_D2 (P+C+S+D black):     S->color = RED;     N = P; // new current node (maybe the root)     // iterate 1 black level     //   (= 1 tree level) higher   } while ((P = N->parent) != NULL);   // end of the (do while)-loop
  来自Wikipedia的伪代码,有着详细的注释 case 3
  若  S  节点是红色节点,C  和 D  是黑色节点,进行以下操作:将  P  节点按 N  节点的方向旋转,然后将 P  节点染为红色,将 S  节点染为黑色,此时的 S  节点就变为了原来的 C  节点(一定是黑色节点)Case_D3: // S red && P+C+D black:   RotateDirRoot(T,P,dir); // P may be the root   P->color = RED;   S->color = BLACK;   S = C; // != NIL   // now: P red && S black   D = S->child[1-dir]; // distant nephew   if (D != NIL && D->color == RED)     goto Case_D6;      // D red && S black   C = S->child[  dir]; // close   nephew   if (C != NIL && C->color == RED)     goto Case_D5;      // C red && S+D black   // Otherwise C+D considered black.   // fall through to Case_D4
  这样一来,我们要考虑的case就变成了: N  黑,P  红,S  黑
  接下来我们要考虑的case就是 C  和 D  节点的不同情况:case 4
  如果  D  和 C  都是黑色,那么将 S  染为红色,将 P  染为黑色,这样相当于给右子树的Black height加一,直接完成了红黑树的维护Case_D4: // P red && S+C+D black:   S->color = RED;   P->color = BLACK;   return; // deletion complete case 5
  我们认为  C  节点是左子节点,D  节点是右子节点
  如果 C  节点为红色,不管 P  节点是黑还是红,我们都可以直接执行下面的操作:将  S  节点和 C  节点进行旋转,然后交换两者的颜色Case_D5: // C red && S+D black:   RotateDir(S,1-dir); // S is never the root   S->color = RED;   C->color = BLACK;   D = S;   S = C;   // now: D red && S black   // fall through to Case_D6
  这么一来,红色就从  C  节点转移到了 D  节点的位置上,于是我们进入了最后的case 6case 6
  此时的  D  节点是红色,不管 P  节点是黑还是红,我们都可以直接执行下面的操作:将  P  和 S  节点旋转,然后交换两者的颜色,并把 D  节点染为黑色Case_D6: // D red && S black:   RotateDirRoot(T,P,dir); // P may be the root   S->color = P->color;   P->color = BLACK;   D->color = BLACK;   return; // deletion complete } // end of RBdelete2
  到此为止,基本的红黑树过程已经阐述完毕,我们下面总结一下红黑树的这些操作到底在干什么,问什么要这样干 旋转
  首先要提及的就是旋转操作,旋转操作我们可以看作一个子节点(N)和一个父节点(P)交换位置
  对于图中的三个框起来的子树部分,旋转操作的影响如下: N  子树上的路径全部会多经过一个S  点D  子树上的路径全部会少经过一个 P  点C  子树上的路径不会有任何改变
  结合起  P  和 S  这两个旋转对象的颜色,旋转操作可以在不改变BST性质的情况下改变 N  子树和 D  子树Black height,这是旋转操作在红黑树维护中扮演的角色
  但是旋转会改变Black height,也有可能将红色的  C  节点接到红色的 P  节点上,所以一般旋转后还需要重新进行染色
  于是我们重新来审视以下删除后的维护过程为什么要这么做:
  首先可以直接处理完毕的case有: case 1:  N  节点是根节点,直接删除即可case 4:  P  节点是红色,其他四个节点都是黑色,那么只需要将 P  节点染为黑色,S  节点染为红色,删除 N  节点即可case 6:当  N  和 S   和 C  是黑色,D  是红色时,将 P  和 S  旋转,使得 N  子树Black Height加一,而为了防止 P  节点是黑色时导致 D  子树的Black Height减一,我们将 S  节点改为 P  节点的颜色,此时相当于从右子树移动了一个黑色到左子树,为了保持平衡,我们将D  节点也染为黑色,这样就使左子树的Black Height加一,在移除 N  节点后,整棵树的Black Height不变
  合理地使用旋转和染色可以用来操控子树的Black Height,另外的三种情况就是想办法把旋转操作的五个关键节点变成可以直接处理的情况 case 2:这种情况的思路时让  P  的右子树也集体Black height减一,然后就可以把 N  子树减一的状态扩展到 P  子树上,从而向上寻求一个可以直接处理的解case 3:这种情况的主要目的是将子节点的红色转移到父节点上,从而进入case4、5、6 case 5:将红色节点从  C  转移到 D  上,从而进入case 6
  为什么不能直接染色更改?
  直接修改成红色可能打破role 3,所以通过旋转后,保证了修改节点的子节点一定是黑色,然后再修改颜色
  到这里红黑树的基本思想已经介绍完成了
  完全把这些情况和旋转、染色的意义理清楚还是花费了超出想象的篇幅,所以关于Linux源码的部分暂且就不再看了,其实现的原理都是基于红黑树罢

朱德去世后,中央到攀枝花找到一老妇朱老总找了你几十年了朱德前言1976年周恩来去世后,意识到自己时间不多了的朱德,开始更加拼命的工作。但是,仅仅几个月后,朱德因为感冒住院,十多天后,他永远的离开了他热爱的国家与人民。朱德去世没多久,中宋希濂晚年透露如果蒋介石同意将30万人撤到缅甸,后果不堪设想解放战争后期,国民党军队已经溃败不堪,从渡江战役之后,蒋介石自知已没有能力再与解放军继续抗衡。蒋介石命自己的两大心腹爱将宋希濂和胡宗南,镇守住西南地区,因为西南地势险要,容易防御,损伤性不大,侮辱性极强汉献帝被挟迫禅位于曹丕,诏书内容有相国华钦监督。汉献帝也因为群臣依附魏王曹丕,于是召集大小官员在宗庙祭告祖先,也命令兼任御史大夫张音持符节把皇帝御玺,绶带进献给曹丕,把皇帝的位置让三星GalaxyZFold4ZFilp4今日首销12999元7499元IT之家9月2日消息,三星GalaxyZFold4以及ZFilp4国行今日正式开售,分别为12999元7499元起。三星GalaxyZFold4国行提供空山绿铂萃黑云粉金,以及勃艮紧急提醒!你的微信支付可能被他人绑定!这样解绑如今微信已不单单是沟通工具,不少账号绑定了支付功能,一旦泄漏后果不堪设想!重要提醒你的微信账号可能在不知情的情况下被他人绑定并开通微信支付!是不是细思极恐?!有网友发现,自己身份信太空宝藏韦伯首次拍摄到一颗系外行星的直接图像稿源cnBeta据CNET报道,詹姆斯韦伯太空望远镜(JWST)首次拍摄到一颗系外行星的直接图像。这颗系外行星被称为HIP65426b。埃克塞特大学的天文学家SashaHinkle新消费日报华为Mate50将支持卫星通信28家上市乳企上半年营收1866。38亿元抖音旗下虚拟数字人平台获批新消费日报9月2日讯,今日新消费日报的主要内容有上半年中国ARVR市场出货超50万台无菌包装供应商新巨丰登陆创业板苹果多款iPod将被列入过时名单行业要闻华为Mate50将支持卫星2017年那个当着央视镜头,向刘强东坦白怀孕的京东女高管,离职了职场如战场这句话对职场的描述一点也没有夸大,身处职场时,无论你身居何位,是高级管理人员也好,是基层工作人员也好,都有可能因为一个不经意的举动或者说错一句话,最终葬送自己的前途。而对华为一神秘新机入网或同Mate50系列一并发布中关村在线消息华为秋季新品发布会将于9月6日正式发布,本次发布会的主角是华为Mate50。今日华为又一神秘新品入网,或将同Mate50系列一起发布。除了上述提到的两款新品,华为新品给国货点赞华为Mate50在iPhone14发布前实现卫星通信9月2日,华为执行董事终端业务集团CEO余文生在接受官方视频采访时表示,华为即将发布一项刺天的新技术。一些业内人士猜测,这一评论意味着卫星通信将由即将推出的Mate50智能手机系列谢娜3胎女儿近照曝光!2名保姆陪同,全程不哭不闹,五官像张杰谢娜3胎女儿近照曝光!2名保姆陪同,全程不哭不闹,五官像张杰众所周知,娱乐圈中有一对相守11年的模范夫妻张杰与谢娜。他们两人育有3个宝贝女儿,感情稳定,一直都是网友们羡慕的对象。不
天津女孩入日本籍,率队4次打败中国女篮,退役回到中国不后悔天津女孩入日本籍,率队4次打败中国女篮,退役回到中国不后悔经常看篮球赛的球迷都知道,篮球运动员一般都比较高。我们中国不缺高个子篮球运动员,但是邻国日本就不一样了。由于先天原因,高个中美之间的芯片之争,为什么说美国输不起,中国肯定会赢呢?作者李云飞来源原创一位久经商场的创业老司机,关注我,每天分享一点财富干货,让你的人生少走弯路。老美在近二十年来对我国的芯片制裁就从未停歇过。先是在1996年,老美就联合了30多个西陈进骗国家11亿经费,让中国芯片停滞13年,出逃美国仍逍遥法外我们现在经常用的手机平板电脑等智能产品,都离不开处理器显卡芯片等核心硬件。而说到芯片行业的龙头企业,很多人都会想到英特尔,三星等。当然还有民族品牌华为的国产芯片,这让很多人都为之自解读2022年的全球产业链重构局势之下,两个关于中国的重要动向头条创作挑战赛这是熊猫贝贝的第1346篇原创文章2022年10月份,一篇由有着瑞信明星分析师利率之神头衔的ZoltanPozsar在当地时间8月24日发布的文章战争产业政策与世界秩经济观察中国领域,最新的几个重要动向和关键信号,如何解读?头条创作挑战赛这是熊猫贝贝的第1345篇原创文章中国共产党第二十次全国代表大会于10月16日上午10时在北京人民大会堂开幕,会期10月16日至10月22日。随着2022年对中国未来中国50城住宅价格报告京沪蓉抗跌,廊坊郑州仍在调整中国楼市整体仍处于下行周期,不过,各大城市房地产市场分化明显。部分城市在经历房价短暂下跌后已企稳反弹,而部分城市楼市仍在深度调整。易居房地产研究院11日发布的一份报告显示,中国50我们的十年曲阜孔繁鹏说在口中的史书,越来越耐听记者易雪康岩通讯员张艳孔大儒宋大壮游客步入景区,要想真正领略这里的文化,单靠提前做功课人生感悟显然力不从心。一个好的讲解员让人受益匪浅,尤其是在孔子故里曲阜,讲解员还是一位优秀传统新零售之互联网零售行业发展特征及驱动因素市场规模竞争格局新零售之互联网零售行业发展特征及驱动因素市场规模竞争格局1互联网零售行业特征由于我国存在不同区域经济发展不均衡的情况,不同区域人均可支配收入和对品牌的了解认知程度存在差异,因此目前嫁日本老公3年后,林志玲深夜发文上热搜,婚姻状态再成焦点10月9日,林志玲在生完孩子后,首度复出出席自己新书的签售会。在签售会上,林志玲身着一席白裙,棕色长发,气质依旧。但不少网友却觉得她生完孩子后更消瘦了,而且非常憔悴。在之前,林志玲百城市长县长种草家乡点燃江西文旅消费热潮刘市长的推介真挚热情,太有感染力了!点赞点赞,为我们大井冈打call!拍得真好!美丽的婺源,多才多艺的县长!梦里老家,百城相见!第一次了解到珠山的夜晚如此迷人!欢迎大家来我家乡做客徐峥与陶虹的婚姻爱情故事!这一场合谋,远比你想象中的还要荒唐俗话说家里红旗不倒,外面彩旗飘飘!这虽然看似是个很无耻的想法,但却是无数男人心目中的梦想。当然,这要是放在古代,是很稀松平常的事情,可要是放到现代,不仅是犯法,还会被放到道德制高点