转自:http://blog.csdn.net/liuzhanchen1987/article/details/7325293

平衡二叉树(解惑)

平衡二叉树定义(AVL):它或者是一颗空树,或者具有以下性质的二叉树:它的左子树和右子树的深度之差的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。

平衡因子(bf):结点的左子树的深度减去右子树的深度,那么显然-1<=bf<=1;

很显然,平衡二叉树是在二叉排序树(BST)上引入的,就是为了解决二叉排序树的不平衡性导致时间复杂度大大下降,那么AVL就保持住了(BST)的最好时间复杂度O(logn),所以每次的插入和删除都要确保二叉树的平衡,那么怎么保持平衡呢?

我努力看了看数据结构上的讲解,但是看的只晕+_+!我对他的讲解很无语,他所谓的“旋转”操作讲的不明不白,看的我气的蛋疼!你说你旋转,你得说清是如何旋转?以那个结点为中心,那些或者说那个结点转了,那些结点不动。你就在哪里旋转来旋转去的,谁知道你是咋转的,你在哪慢慢转吧!哥转不过你,另找高明!于是在网上找啊找,只为想搞明白是到底怎么转的!点击打开链接让我对“旋转”有所领悟,表示感谢!

插入时:

那究竟是如何“转”的呢?

首先必须明白一个核心操作,不让它叫“旋转”!而叫——>“两个结点的变换”

如图:

就拿第一个来说->点100和101的变换:

点101占据点100的位置,点100换到了101的对面的位置,其他点的相对位置不变。

我们可以更直观的理解为:把101结点“上提”一下!

分析:101>100,所以100可以作为101的左孩子;

也就是在二叉排序树中,两个结点这样的变换操作是可行的,是符合二叉排序树的性质。

不仅这个简单的图,任何复杂的二叉排序树都可以,你可以试试,也许你会说如果点101左边有孩子怎么办?别着急~,当然有办法!

下边正式说这个图的四种不平衡情况(插入时)及操作:

首先还需要明白一个概念->最小不平衡子树的根结点:也就是当你进行插入操作时,找到该需要插入结点的位置并插入后,从该结点起向上寻找(回溯),第一个不平衡的结点即平衡因子bf变为-2或2。

为什么要引入这个最小不平衡根结点的概念,因为在插入时,对该子树进行保持平衡操作后,其它的结点的平衡因子不会变,也就是整棵树又恢复平衡了。为什么呢?

你想想不平衡点的bf一定是-2或2吧,经过平衡操作后,他会把一边子树的一个结点分给另一边的子树,也就是一边的深度分给另一边,这样就平衡了!

比如,插入前:左边是深度1,右边深度是0;插入后左边深度是2,右边深度是0,经过平衡后左边深度是1,右边深度是1;

那么你说插入前和插入后该根结点所领导的子树的深度变没??仍是1,显然没变!那么就仍保持了这棵树的平衡了!

下面即四种情况分别为:左左、右右、左右、右左,每种情况又有两个图:①、②,①是该情况的最简单的图形,②是该情况的一般的图形;

设x为最小不平衡子树的根结点,y为刚插入的点

左左:

即在x的左孩子a的左孩子c上插入一个结点y(该结点也可以是c,如图①),即y可以是c,也可以是c的左孩子(如图②),也可以是c的右孩子(不在画出)

图①就不用说了,结点x和结点a变换,则树平衡了;那么图②就是树中的一般情况了a结点有右孩子d,那要进行x和a变换,那么a的右孩子放哪啊?

很简单,如图放在x的左孩子上;分析:x>d,d>a,所以d可作为x的左孩子,且可作为a的右孩子中的孩子。下边这样的类似情况不再一一分析,自己分析分析~

实现:找到根结点x,与它的左孩子a进行交换即可使二叉树树再次平衡;

右右:

即在x的右孩子a的右孩子c上插入一个结点y(该结点也可以是c,如图①),即y可以是c,也可以是c的右孩子(如图②),也可以是c的左孩子(不在画出)

实现:找到根结点x,与它的右孩子a进行交换即可使二叉树树再次平衡;

左右:

即在x的左孩子a的右孩子c上插入一个结点y(该结点也可以是c,如图①),即y可以是c,也可以是c的右孩子(如图②),也可以是c的左孩子(不在画出)

这个左右和下边的右左,稍微复杂了点,需要进行两次交换,才能达到平衡,注意这时y是c的右孩子,最终y作为x的左孩子;若y是c的左孩子,最终y作为a

的右孩子,画图分析一下~~下边类似,不再敖述。

实现:找到根结点x,让x的左孩子a与x的左孩子a的右孩子c进行交换,然后再让x与x此时的左孩子c进行交换,最终达到平衡;

右左:

即在x的右孩子a的左孩子c上插入一个结点y(该结点也可以是c,如图①),即y可以是c,也可以是c的右孩子(如图②),也可以是c的左孩子(不在画出)

实现:找到根结点x,让x的右孩子a与x的右孩子a的左孩子c进行交换,然后再让x与x此时的右孩子c进行交换,最终达到平衡;

上边的四种情况包含了所有插入时导致不平衡的情况,上面给出的仅仅是一棵大树中的最小不平衡子树,一定要想清楚,别迷了!

另外一定要注意这个交换操作,比如a与b交换(a在上,b在下),b一定要占据a的位置!什么意思?就是b要放在(覆盖)储存a的那块内存上,

再通俗点说,若a是x的左孩子,则交换后b要做x的左孩子;这就是所谓的b占据a的位置!

那么如何找到最小不平衡子树的根结点x,并判断出它是属于那种情况的?

插入一个结点时,我们首先找到需要插入的位置,并插入;数据结构上用的是递归,不要说递归太浪费时空,你想想一个含2^31个结点的平衡二叉树的深度大约是31吧,它递归再多也不就是31层!而且递归代码短小、精悍、富含艺术之美!所以我认为对于这个平衡二叉树,用递归很合适!

显然插入之后就要检查是否出现不平衡的结点,那么如何检查?

我们知道,你插入的时候用的是递归,一条线找到要插的位置,并插入;那么谁的平衡因子的有可能变呢?

不难想象,只有该条线上的结点的平衡因子有可能改变!那么我们在回溯的时候不就可以找到第一个不平衡的子树的结点?!

可是我们如何判断该结点的平衡因子是否应该改变,显然要看它被插入结点的一边的深度是否增加;

如何看它被插入结点的一边的深度是否增加?

如上图,如何看x的右孩子a(即被插入结点的一边)的深度增加?我们知道在a的右孩子上插入了结点y那么a的bf是一定要减1

那么x结点的bf?可根据a的bf决定是否改变!

若a:bf=-1或1,那么a之前一定为0,表示a的深度增加了,那么x的bf可根据a是x哪一边的孩子决定+1或-1;

若a:bf=0,那么a之前一定为-1或1,表示a的深度每增加,那么不仅x的bf就不用变,该条线上的所有结点的bf都不用变,直接返回即可;

当然了,那么找到最小不平衡子树的根结点x了,如何判断它属于哪种不平衡呢?

①根据上边的几种情况,我们需要知道两个方向,在回溯时可以记录一下该结点到下一个结点的方向0:左、1:右为第二个方向,传递给上一层中,那么上层中的方向就是一个方向,有了这两个方向就能确定是哪种不平衡了。

还就上边的图说吧~可以定义一个全局变量secdirection(第二个方向),也可在递归中定义一个局部变量,返回给上一层。在回溯到a中时,secdirection=1,到x的时候

x->a的方向也为1,定义firdirection=1;而这时x:bf=-2;那么就找到了最小不平衡子树的根结点x,又知道了两个方向,那么进行相应的平衡操作不就行了。

②其实我代码中的就是按照①写的,可是刚又想了,其实不用用个变量记录第二个方向,可以根据a的bf确定它的第二个方向,a:bf=-1说明右孩子的深度增加,y加到右孩子上;

a:bf=1;说明左孩子的深度增加,y加到左孩子上;

好了,找到了最小不平衡子树的根结点x了,也知道了那种不平衡,调用keepbalance(...)就使树平衡了,可是某些结点的平衡因子在变换是变了~~咋办?

我就是一种一种情况推出来的,不知道别人怎么变的,每一种情况都有固定的几个点变了,变成了一个固定的值!谁有更好的办法,请多多指教!

下边一一列出(插入操作中)变换后bf改变的结点及值:

左左:前a->bf=1 后 x->bf=0,a->bf=0;右右:前a->bf=-1 后x->bf=0,a->bf=0;显然左左与右右的x与a变换后的bf都为0;

左右、右左中结点bf的改变要根据之前c的bf!

左右:若c->bf=1,则x->bf=-1,a->bf=0,c->bf=0;若c->bf=-1,则x->bf=0,a->bf=1,c->bf=0;若c->bf=0,则x->bf=0,a->bf=0,c->bf=0;

右左:若c->bf=1,则x->bf=0,a->bf=-1,c->bf=0;若c->bf=-1,则x->bf=1,a->bf=0,c->bf=0;若c->bf=0,则x->bf=0,a->bf=0,c->bf=0;

可以发现,当左右、右左的c->bf相同时x与a的bf刚好取相反的值。

好了,到现在插入一个结点的二叉树终于平衡了,相应的平衡因子也修改了!插入算是完成了!!

删除时:

删除类似插入的操作,蛋又不同,删除会有一些特殊情况需要特殊处理,当然核心操作“保持平衡”是不变的!

删除时少一个结点,也就是该结点所在的子树深度可能会减小,而插入时多一个结点,该结点所在的子树深度可能会增加,

所以递归删除一个结点时,回溯时找到最小不平衡子树的根结点时,要向相反的方向去找属于哪种情况;

如图:

y为要删除的结点;

图①:y结点删除后,回溯到x结点x:bf=-1变为x:bf=-2;则需从相反方向即从x的右孩子的方向向下检查属于哪种情况,显然第一个方向为1:右;

第二个方向看a:bf的值——若为1时,那就相当于插入时‘右左’的情况;若为-1时,那就相当于插入时‘左左’的情况;可现在a:bf既不是1也不是-1

而是0,这就是删除的特殊情况了!我们不妨试试对他进行类似于插入时的‘右右’操作,看怎么样~    如上图,经过变换后该子树平衡了!但是因子的

修改就跟插入时的‘右右’不一样了!此时变为:x:bf=-1,a:bf=1;所以我们不妨就把a:bf=0也归纳为删除的‘右右’或‘左左’(如图②,不再敖述)操作;

那么删除时因子的改变需在插入时因子的改变中添加上:

左左:前a:bf=0 后x:bf=1,a:bf=-1; 右右:前a:bf=0 后x:bf=-1,a:bf=1;其他不变!

插入时结束结点平衡因子的修改,直接返回(也就是该树已经平衡了):

回溯时发现儿子结点的平衡因子为0(当发现不平衡结点,并进行平衡操作后,平衡后根结点的bf一定为0,也结束了)

但是删除时结束结点平衡因子的修改,直接返回,就与插入时不一样了:回溯时发现儿子结点的平衡因子为-1或1!

再删除操作中,平衡一个子树后,该子树的深度不一定不变,而只有上边那种特殊情况该子树的深度不变,其他都会变!

可以想象,其实是很简单的道理:除了特殊情况其他都与插入的情况一模一样,说白了就是把深度大的子树(根结点的其中一个)向深度小子树贡献一个深度,

那么这样一来,该子树(对于根结点所领导的树)的深度是不是比原来的小1了?!所以要继续向上一个一个进行检索,直到根结点为止!

好了,到这里删除也算是说完了,可以贴代码了吧~

[cpp]  view plain copy
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. typedef int Elemtype;
  5. typedef struct Balanced_Binary_Tree
  6. {
  7. Elemtype e;
  8. int bf;
  9. struct Balanced_Binary_Tree *child[2];
  10. }*AVL;
  11. ///------------简单的位操作-------------------
  12. void setbit(char *i,char val,char pos)
  13. {
  14. if(pos==1)
  15. (*i)=(*i)|1;
  16. else
  17. {
  18. if(val==1)    (*i)=(*i)|2;
  19. else    (*i)=(*i)&1;
  20. }
  21. }
  22. char getbit(char i,char pos)
  23. {
  24. ///调试时,发现这里能返回2///
  25. //    return (i&pos); 出错的地方
  26. return (i&pos)&&1;
  27. /
  28. }
  29. ///--------------------------------------------
  30. ///-----------生成一个结点---------------------
  31. AVL createnode(Elemtype e)
  32. {
  33. AVL node=NULL;
  34. node=(AVL)malloc(sizeof(struct Balanced_Binary_Tree));
  35. node->e=e;    node->bf=0;
  36. node->child[0]=node->child[1]=NULL;
  37. return node;
  38. }
  39. ///---------------------------------------------
  40. ///★★★★★★★★AVL的核心操作★★★★★★★★★★★★
  41. ///★★★★★★★★★保持平衡★★★★★★★★★★★★★★
  42. //改变因子函数
  43. void setfactor(AVL f,int button)
  44. {
  45. char fir=button/10,sec=button%10;
  46. AVL s=f->child[fir],ss=s->child[sec];
  47. char choice=ss->bf;
  48. int a=1,b=0;
  49. //调试时发现,删除时的特殊情况/
  50. /插入时,不会有这种情况,若button=0,则s->bf=1//
  51. /若button=11,则s->bf=-1;然而删除时,却会出现/
  52. /button=0或者button=11时 s->bf=0!!!!!!!
  53. /那么这种特殊情况,平衡后所得的因子就跟一般的//
  54. /不一样了!!!如下///
  55. if(button==0 && s->bf==0)    f->bf=1,s->bf=-1;
  56. else if(button==11 && s->bf==0)    f->bf=-1,s->bf=1;
  57. ///
  58. else if(button==0 || button==11)
  59. {
  60. f->bf=0;
  61. s->bf=0;
  62. }
  63. else
  64. {
  65. /写博客时,发现这里有问题///
  66. //    if(button==1)    choice=-choice;
  67. /但是为什么我测试的时候怎么都对?!///
  68. /经再次测试,上边确实错了!!!
  69. /改为下边应该就对了吧///
  70. if(button==1)    {a^=b,b^=a,a^=b;}
  71. if(choice==-1)    f->bf=a,s->bf=b;
  72. else if(choice==0)    f->bf=s->bf=0;
  73. else    f->bf=-b,s->bf=-a;
  74. ss->bf=0;
  75. }
  76. }
  77. //两节点变换函数
  78. void conversion(AVL *T,char direction)
  79. {
  80. AVL f=*T,s=f->child[direction];
  81. f->child[direction]=s->child[!direction];
  82. s->child[!direction]=f;
  83. *T=s;
  84. }
  85. //保持平衡函数
  86. void keepbalance(AVL *T,char fir,char sec)
  87. {
  88. AVL *s=&((*T)->child[fir]);
  89. int button=fir*10+sec;
  90. if(button==0 || button==11)
  91. {
  92. setfactor((*T),button);
  93. conversion(T,fir);
  94. }
  95. else
  96. {
  97. setfactor((*T),button);
  98. conversion(s,sec);
  99. conversion(T,fir);
  100. }
  101. }
  102. ///★★★★★★★★★★★★★★★★★★★★★★★★★★
  103. ///------------插入时的选向操作-------------------
  104. void selectforInsert(AVL *T,char *info,int direction)
  105. {
  106. AVL cur=*T;
  107. char firdirection,secdirection;
  108. if(direction==0)    (cur->bf)++;
  109. else    (cur->bf)--;
  110. if(cur->bf==0)    setbit(info,1,1);
  111. else if(cur->bf==-1 || cur->bf==1)    setbit(info,direction,2);
  112. else
  113. {
  114. firdirection=direction;
  115. secdirection=getbit(*info,2);
  116. keepbalance(T,firdirection,secdirection);
  117. setbit(info,1,1);
  118. }
  119. }
  120. //----------------------------------------------
  121. //*************插入操作************************//
  122. char InsertAVL(AVL *T,Elemtype e)
  123. {                                //可用于查询
  124. char info;
  125. if(!(*T))
  126. {
  127. (*T)=createnode(e);
  128. return 0;
  129. }
  130. else if((*T)->e==e)        return -1;
  131. else if((*T)->e>e)//左
  132. {
  133. info=InsertAVL(&((*T)->child[0]),e);
  134. if(getbit(info,1))    return info;
  135. selectforInsert(T,&info,0);
  136. }
  137. else              //右
  138. {
  139. info=InsertAVL(&((*T)->child[1]),e);
  140. if(getbit(info,1))    return info;
  141. selectforInsert(T,&info,1);
  142. }
  143. return info;
  144. }
  145. //*********************************************//
  146. //-------------删除时的选向操作--------------------
  147. void selectforDelete(AVL *T,char *info,char direction)
  148. {
  149. AVL cur=(*T);
  150. char firdirection,secdirection;
  151. if(direction==0)    (cur->bf)--;
  152. else    (cur->bf)++;
  153. if(cur->bf==0)    *info=0;
  154. else if(cur->bf==-1 || cur->bf==1)    *info=1;
  155. else
  156. {
  157. firdirection=!direction;
  158. ///调试时,发现这里少写了一个等号
  159. //        if(cur->child[firdirection]->bf=1)    secdirection=0;草,真帅气!原来1==a这样写确实有必要!
  160. if(1==cur->child[firdirection]->bf)    secdirection=0;
  161. /
  162. else    secdirection=1;
  163. keepbalance(T,firdirection,secdirection);
  164. /
  165. ///调试时,发现经过子树平衡操作后,*info不一定都是0,就是那个特殊情况,在setfactor中/
  166. ///的那种特殊情况时,这里*info应改为1! 所以代码改如下://
  167. /
  168. //    *info=1; 写代码时:这跟插入可不一样啊...该子树平衡了,它父节点的因子比变!
  169. //    *info=0;//因此,这还没完还要是0!! ............啊……这里不一定是0!
  170. 还是那个特殊情况搞的鬼!//
  171. if(cur->bf==0)    *info=0;
  172. else    *info=1;
  173. /
  174. }
  175. }
  176. //------------------------------------------------
  177. //-------------变向递归--辅助删点-----------------
  178. char find(AVL *gogal,AVL *p)
  179. {
  180. char info;
  181. AVL tp=NULL;
  182. if(NULL!=(*p)->child[0])
  183. {
  184. info=find(gogal,&((*p)->child[0]));
  185. if(info!=0)    return info;
  186. selectforDelete(p,&info,0);
  187. }
  188. else
  189. {
  190. (*gogal)->e=(*p)->e;
  191. tp=(*p)->child[1];
  192. free((*p));
  193. *p=tp;
  194. info=0;
  195. }
  196. return info;
  197. }
  198. //------------------------------------------------
  199. //**************删除操作*************************//
  200. char DeleteAVL(AVL *T,Elemtype e)
  201. {
  202. char info;
  203. AVL tp=NULL;
  204. if(!(*T))    return -1;//原if(!T)    return -1;于2011年11月29日8:59:15修改
  205. else if((*T)->e==e)
  206. {
  207. if(NULL!=(*T)->child[1])
  208. {
  209. info=find(T,&((*T)->child[1]));
  210. if(info!=0)    return info;
  211. selectforDelete(T,&info,1);
  212. }
  213. else
  214. {
  215. //调试时,发现这样写不对!!!///
  216. //    (*T)=(p=(*T)->child[0])-(free((*T)),0);//Just save a variable! 这里出问题
  217. //    (*T)=p-(free((*T)),0); 可以
  218. //    (*T)=(p=((*T)->child[0]))+(free((*T)),0); 不可以
  219. tp=((*T)->child[0]);
  220. free((*T));
  221. *T=tp;
  222. //调试时,发现这里漏了给info赋值
  223. info=0;
  224. ///
  225. }
  226. }
  227. else if((*T)->e>e)
  228. {
  229. info=DeleteAVL(&(*T)->child[0],e);
  230. if(info!=0)    return info;
  231. selectforDelete(T,&info,0);
  232. }
  233. else
  234. {
  235. info=DeleteAVL(&(*T)->child[1],e);
  236. if(info!=0)    return info;
  237. selectforDelete(T,&info,1);
  238. }
  239. return info;
  240. }
  241. //************************************************//
  242. //*****************JUST FOR TEST************************//
  243. #define MOVE(x)    (x=(x+1)%1000)
  244. AVL queue[1000];
  245. void print(AVL p,int i)
  246. {
  247. int front,rear,temp,count;
  248. front=rear=-1; count=temp=0;
  249. queue[MOVE(rear)]=p; count++;
  250. printf("%d\n",i);
  251. while(front!=rear)
  252. {
  253. p=queue[MOVE(front)];    count--;
  254. if(p->child[0])    queue[MOVE(rear)]=p->child[0],temp++;
  255. if(p->child[1])    queue[MOVE(rear)]=p->child[1],temp++;
  256. printf("%d->%d ",p->e,p->bf);
  257. if(count==0)
  258. {
  259. printf("\n");
  260. count=temp;
  261. temp=0;
  262. }
  263. }
  264. printf("\n");
  265. }
  266. //**************************************************//
  267. int main()
  268. {
  269. AVL T=NULL;
  270. int i,nodenum=0;
  271. freopen("input.txt","w",stdout);
  272. nodenum=100;
  273. for(i=0;i<nodenum;i++)
  274. {
  275. InsertAVL(&T,i);
  276. }
  277. print(T,-1);
  278. for(i=0;i<nodenum-1;i++)
  279. {
  280. DeleteAVL(&T,i);
  281. print(T,i);
  282. }
  283. return 0;
  284. }

平衡二叉树——如何实现不平衡二叉树到平衡二叉树相关推荐

  1. 带父节点的平衡二叉树_深入理解(二叉树、平衡二叉树、B-Tree、B+Tree )的区别

    一.背景 一般说MySQL的索引,都清楚其索引主要以B+树为主,此外还有Hash.RTree.FullText.本文简要说明一下MySQL的B+Tree索引,以及和其相关的二叉树.平衡二叉树.B-Tr ...

  2. 带父节点的平衡二叉树_学习数据结构--第四章:树与二叉树(平衡二叉树)

    第四章:树与二叉树(平衡二叉树) 1.平衡二叉树 平衡二叉树:AVL,任意结点的平衡因子的绝对值不超过一. 平衡因子:左子树高度 - 右子树高度 如上图二叉树,是否是平衡二叉树? 可以把所有结点的平衡 ...

  3. MySQL的二叉树、平衡二叉树、2,3查找树、B树、B+树索引

    索引这个词,相信大多数人已经相当熟悉了,很多人都知道MySQL的索引主要以B+树为主,但是要问到为什么用B+树,恐怕很少有人能把前因后果讲述的很完整.本文就来从头到尾介绍下数据库的索引. 索引是一种数 ...

  4. 二叉树、平衡二叉树、完全二叉树、满二叉树

    基本概念 结点的层次(Level)从根开始定义,根为第一层,根的孩子为第二层. 二叉树的高度:树中结点的最大层次称为树的深度(Depth)或高度. 二叉树 在计算机科学中,二叉树是每个结点最多有两个子 ...

  5. 关于二叉树、平衡二叉树、红黑树

    目录 1.二叉树缺点 2.平衡二叉树 3.为什么有了平衡二叉树,还要红黑树? 参考文章:记一次腾讯面试:有了二叉查找树.平衡树(AVL)为啥还需要红黑树? 1.二叉树缺点 二叉查找树,相信大家都接触过 ...

  6. 数据结构—树与二叉树(Part Ⅵ)——平衡二叉树哈夫曼树

    目录 平衡二叉树 平衡二叉树的定义 平衡二叉树的插入 LL型 RR型 代码思路 LR型 RL型 哈夫曼树和哈夫曼编码 哈夫曼树的定义 哈夫曼树的构造 哈夫曼树的结点类型 哈夫曼树构造的算法 哈夫曼编码 ...

  7. 剑指offer之判断二叉树是不是平衡二叉树

    1 问题 判断二叉树是不是平衡二叉树 平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右 ...

  8. 二叉树,平衡二叉树,B-Tree,B+Tree,跳表详解

    二叉树,平衡二叉树,B-Tree,B+Tree,跳表详解 1.二叉查找树(BST) 1.1 二叉查找树概念 1.2 二叉查找树特点 2. 平衡二叉树(AVL) 2.1 平衡二叉树概念 2.2 平衡二叉 ...

  9. 浅谈mysql 平衡二叉树理解_浅析二分查找,二叉树,平衡二叉树,B树,B+树

    二分查找 二分查找是最基本的,后面的二叉树,平衡二叉树,B树,B+树都是基于二分查找演变而来的. 二分查找法作为一种常见的查找方法,将原本是线性时间提升到了对数时间范围,大大缩短了搜索时间,但它有一个 ...

最新文章

  1. 谷歌逐步取消对IE6的支持
  2. Nacos配置文件覆盖问题
  3. Java Swing Mysql学生信息管理系统
  4. 通过银行卡号获取所属银行「支付宝」
  5. 应届毕业生面试程序员自我介绍
  6. 创宇区块链|重蹈覆辙?为何 DEUS 协议再受攻击?
  7. 工作用什么企业邮箱比较正式?常用的商务企业邮箱有哪些?
  8. 嵌入式硬件(一)概述
  9. 计算机应用方向的毕业设计,计算机应用(动画方向)毕业设计.pdf
  10. WK2212\WK2204\WK2168\WK2132\WK2124\WK2114 UART扩展4个增强UART
  11. 优信二手车创新发展,获业内外交口称赞
  12. 电动汽车单轮驱动防滑控制系统ASR
  13. tcpdump 网络数据包分析工具
  14. java 微信抽奖_微信随机生成红包金额算法java版
  15. 计算机学硕一志愿调剂,一志愿和调剂生一起进入考研复试,一志愿真的会更有优势吗?...
  16. 英国留学毕业论文标题部分怎么写?
  17. 速记法-python输出递减序列
  18. 分享 | .NET Framework升级到.NET 5
  19. Table实现tr数据滚动
  20. 【前端库】typed.js 打字机效果

热门文章

  1. 时隔一年才发现嵌入式到底指的是什么
  2. 【CloudCompare教程】001:CloudCompare中文版下载与安装图文教程
  3. JS实现简单的网页文本转语音阅读
  4. alt复制选区就会卡 ps_ps怎么复制选区相关常见问题解答
  5. Docker - Docker详解及使用
  6. 如何统计每个小时用户在线人数?
  7. TeamViewer用户注意:请尽快将其更新为最新版本
  8. 【调剂】苏州科技大学电子与信息工程学院2021年硕士研究生招生第二批调剂信息公告...
  9. 格式化代码 format.js
  10. Chem 3D中怎么创建立体模型