韩顺平_PHP程序员玩转算法公开课(第一季)12_双向链表crud操作之_水浒英雄排行_学习笔记_源代码图解_PPT文档整理

文西马龙:http://blog.csdn.net/wenximalong/

链表——双向链表



关于按照英雄的排行顺序加入,参考我的这篇博文中的详细图文说明:韩顺平_PHP程序员玩转算法公开课(第一季)03_单链表crud操作之_水浒英雄排行算法_学习笔记_源代码图解_PPT文档整理

现在分析添加的情况
已经有1号英雄和5号英雄,现在要添加3号英雄

此时cur指向了1号英雄,hero指向3号英雄
cur指向1号英雄,发现cur的下一个是5号英雄,大于要添加的3号英雄
分析图

过程

(1)让3号英雄指向5号英雄,即把这个①号线搭起来
$hero->next=$cur->next; 
//$cur指向1号英雄,$cur->next指向5号英雄
(2)然后再搭pre这根线,让3号英雄的pre指向1号英雄,即把这个②号线搭起来
$hero->per=$cur;
(3)然后让5号英雄的pre指向3号英雄,即把这个③号线搭起来
$cur->next->pre=$hero;
//$cur->next指向5号英雄,$cur->next->pre为5号英雄指向1号英雄,即图中⑥号线
//此时⑥号线就断掉了,原来的5号英雄的pre是指向1号英雄的,现在指向了3号英雄
(4)让1号英雄指向3号英雄,即把这个④号线搭起来
$cur->next=$hero;
//此时⑤号线就断掉了,原来的$cur->next是指向5号英雄的,现在指向了3号英雄
★自己对照图,走一遍,一定要线清楚了

doubleLink.php


	
		
	
	
		

双向链表完成英雄排行管理


查询英雄 添加英雄 删除英雄 修改英雄 no=$no; $this->name=$name; $this->nickname=$nickname; } //添加hero,这里我们会构建一个双向链表 //这里使用了静态函数 public static function addHero($head,$hero){ //$head头节点不能动 //$cur为辅助节点,让$cur来穿针引线 $cur=$head; //isExist 假设英雄不存在 $isExist=FALSE; //这是一个标志位 //首先看看目前的这个链表是不是空的 //当还有英雄的时候,$cur指向的$head节点的next属性必然为null,因为现在只有head节点,还没有添加英雄 //如果是空链表就直接加入 //怎样把空链表和普通链表并和在一起呢? if($cur->next==null){ //如果是,就说明是空链表,添加第一个英雄 $cur->next=$hero; //这样就首尾相连了 $hero->pre=$cur; }else{ //当不是空节点的时候 //如果不是空节点,按照排名来添加 //首先找到添加的位置 while($cur->next!=null){ //只要$cur->next不等于null,就说明没有到队尾 if($cur->next->no > $hero->no){ //说明位置找到了,则说明$hero要加在$cur的后面 break; }else if($cur->next->no==$hero->no){ $isExist=TRUE; echo'
不能添加相同的编号'; } //继续下一个 $cur=$cur->next; } //退出该while循环时候,我们只需要把$hero添加到$cur后面即可,(队尾) //说明还没有这个排名,可以添加,并可以和上面的空链表判断合并 if(!$isExist){ //如果不存在就添加,因为在上面break跳出的时候,就说明找到了,跳出的时候$isExist为假,则!$isExist为真 //添加,有点麻烦 $hero->next=$cur->next; $hero->pre=$cur; $cur->next->pre=$hero; $cur->next=$hero; } } } //遍历显示双向链表,显示所有英雄的函数 public static function showHero($head){ //上来线做一个辅助指针,穿针引线 $cur=$head->next; //头本来就是空的,这样就输出打印的时候,不输出头了 //$cur=$head; 如果是这样,每次输出的时候会输出:编号= 名字= 外号= 把头也输出了 while($cur->next!=null){ //为空就说明到队尾了 echo'
编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname; //继续下移一步 $cur=$cur->next; } //当退出while的时候,$cur就指向了最后的那个节点了 //要是不输出,就会少一个人,少队尾的那个人 echo'
编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname; } } //创建一个头节点 $head=new Hero(); //创建一个英雄 $hero=new Hero(1,'宋江','及时雨'); //静态方法的调用 Hero::addHero($head,$hero); $hero=new Hero(2,'卢俊义','玉麒麟'); Hero::addHero($head,$hero); $hero=new Hero(6,'林冲','豹子头'); Hero::addHero($head,$hero); $hero=new Hero(3,'吴用','智多星'); Hero::addHero($head,$hero); $hero=new Hero(56,'孙二娘','母夜叉'); Hero::addHero($head,$hero); echo '
英雄排行榜.....'; //显示 Hero::showHero($head); ?>


=========
改进:不严密的地方,可以和前面的进行合并的
添加英雄,把添加时是空链表和不是空链表的情况,合并到一起了
现在考虑:要添加的人就在末尾

有一个head和1号人物,要添加的是6号人物
此时cur指向1号人物,

当按照原先的代码执行,既下面这段代码,会有一些小问题的
$hero->next=$cur->next;
$hero->pre=$cur;
$cur->next->pre=$hero;
$cur->next=$hero;
执行
(1)$hero->next=$cur->next; 
此时cur指向1号人物,1号人物此时是最后的一个节点,$cur->next就是空了,那么$hero->next=$cur->next; 此时这句话就 没有意义了,因为$hero->next本身就是空啊,你再赋给它空,没有意义,干脆这句话此时就不执行了。所以要加一个一个判断条件,即$cur->next不等于空,执行$hero->next=$cur->next; 才有价值
if($cur->next!=null){
$hero->next=$cur->next;
}
(2)$hero->pre=$cur; 
这句话还是很有价值的,如下图所示, 把①号线搭起来了
(3)
$cur->next->pre=$hero;
此时cur指向1号人物,$cur->next就是空了,空的pre就 没意义了,所有也要和上面的(1)一样,加判断
if($cur->next!=null){
$cur->next->pre=$hero;
}
(4)$cur->next=$hero;
这句是有意义的,如下图所示,把 ②号线搭起来了


如果前面的代码修改成了如下的代码:
if($cur->next!=null){
$hero->next=$cur->next;
}
$hero->pre=$cur;
if($cur->next!=null){
$cur->next->pre=$hero;
}
$cur->next=$hero;

就和前面的一个if判断语句不谋而合了

即在doubleLink.php中的这段代码
if($cur->next==null){ //如果是,就说明是空链表,添加第一个英雄
$cur->next=$hero;
//这样就首尾相连了
$hero->pre=$cur;
}else{...

都是在说当$cur->next为空的时候,就执行
$cur->next=$hero;
$hero->pre=$cur;

所以可以把前面的if和现在的合并了,修改后的代码为
doubleLink2.php


	
		
	
	
		

双向链表完成英雄排行管理


查询英雄 添加英雄 删除英雄 修改英雄 no=$no; $this->name=$name; $this->nickname=$nickname; } //添加hero,这里我们会构建一个双向链表 //这里使用了静态函数 //添加英雄,把添加时是空链表和不是空链表的情况,合并到一起了 public static function addHero($head,$hero){ //$head头节点不能动 //$cur为辅助节点,让$cur来穿针引线 $cur=$head; //isExist 假设英雄不存在 $isExist=FALSE; //这是一个标志位 //首先看看目前的这个链表是不是空的 //当还有英雄的时候,$cur指向的$head节点的next属性必然为null,因为现在只有head节点,还没有添加英雄 //如果是空链表就直接加入 //怎样把空链表和普通链表并和在一起呢? while($cur->next!=null){ //只要$cur->next不等于null,就说明没有到队尾 if($cur->next->no > $hero->no){ //说明位置找到了,则说明$hero要加在$cur的后面 break; }else if($cur->next->no==$hero->no){ $isExist=TRUE; echo'
不能添加相同的编号'; } //继续下一个 $cur=$cur->next; } //退出该while循环时候,我们只需要把$hero添加到$cur后面即可,(队尾) //说明还没有这个排名,可以添加,并可以和上面的空链表判断合并 if(!$isExist){ //如果不存在就添加,因为在上面break跳出的时候,就说明找到了,跳出的时候$isExist为假,则!$isExist为真 //添加,有点麻烦 //当你添加的人物就在最后,比如现在只有1号英雄,再添加一个5号英雄,肯定是添加在1号英雄的后面 //这里加上if判断,会更稳健型些 if($cur->next!=null){ $hero->next=$cur->next; } $hero->pre=$cur; if($cur->next!=null){ $cur->next->pre=$hero; } $cur->next=$hero; } } //遍历显示双向链表,显示所有英雄的函数 public static function showHero($head){ //上来线做一个辅助指针,穿针引线 $cur=$head->next; //头本来就是空的,这样就输出打印的时候,不输出头了 //$cur=$head; 如果是这样,每次输出的时候会输出:编号= 名字= 外号= 把头也输出了 while($cur->next!=null){ //为空就说明到队尾了 echo'
编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname; //继续下移一步 $cur=$cur->next; } //当退出while的时候,$cur就指向了最后的那个节点了 //要是不输出,就会少一个人,少队尾的那个人 echo'
编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname; } } //创建一个头节点 $head=new Hero(); //创建一个英雄 $hero=new Hero(1,'宋江','及时雨'); //静态方法的调用 Hero::addHero($head,$hero); $hero=new Hero(2,'卢俊义','玉麒麟'); Hero::addHero($head,$hero); $hero=new Hero(6,'林冲','豹子头'); Hero::addHero($head,$hero); $hero=new Hero(3,'吴用','智多星'); Hero::addHero($head,$hero); $hero=new Hero(56,'孙二娘','母夜叉'); Hero::addHero($head,$hero); echo '
英雄排行榜.....'; //显示 Hero::showHero($head); ?>

代码不是一步到位的,优化前进


======
此时删除英雄就不需要辅助节点了
分析图


当要删除的节点在最后的时候,还需要判断下

此时$cur->next本身就是空了,所有要加判断了
$cur->pre->next=$cur->next; //这个就相当于把1号节点的next置空了,这个没错
所以要这样:
if($cur->next!=null){
$cur->next->pre=$cur->pre;
}
$cur->pre->next=$cur->next;
这样执行的话,把上图中的蓝线就去掉了,这样6号英雄就访问不到了,6号英雄出列了,因为我们是从表头开始找的,$cur-next就访问不到6号英雄节点了,6号英雄指向别人是没有用的,★关键★是没有人指向它。

注意:
要考虑删除第一个和最后一个,(当你的代码考虑不周全的时候)这是最容易出错的,所以要测试

doubleLink3.php


	
		
	
	
		

双向链表完成英雄排行管理


查询英雄 添加英雄 删除英雄 修改英雄 no=$no; $this->name=$name; $this->nickname=$nickname; } //添加hero,这里我们会构建一个双向链表 //这里使用了静态函数 //添加英雄,把添加时是空链表和不是空链表的情况,合并到一起了 public static function addHero($head,$hero){ //$head头节点不能动 //$cur为辅助节点,让$cur来穿针引线 $cur=$head; //isExist 假设英雄不存在 $isExist=FALSE; //这是一个标志位 //首先看看目前的这个链表是不是空的 //当还有英雄的时候,$cur指向的$head节点的next属性必然为null,因为现在只有head节点,还没有添加英雄 //如果是空链表就直接加入 //怎样把空链表和普通链表并和在一起呢? while($cur->next!=null){ //只要$cur->next不等于null,就说明没有到队尾 if($cur->next->no > $hero->no){ //说明位置找到了,则说明$hero要加在$cur的后面 break; }else if($cur->next->no==$hero->no){ $isExist=TRUE; echo'
不能添加相同的编号'; } //继续下一个 $cur=$cur->next; } //退出该while循环时候,我们只需要把$hero添加到$cur后面即可,(队尾) //说明还没有这个排名,可以添加,并可以和上面的空链表判断合并 if(!$isExist){ //如果不存在就添加,因为在上面break跳出的时候,就说明找到了,跳出的时候$isExist为假,则!$isExist为真 //添加,有点麻烦 //当你添加的人物就在最后,比如现在只有1号英雄,再添加一个5号英雄,肯定是添加在1号英雄的后面 //这里加上if判断,会更稳健型些 if($cur->next!=null){ $hero->next=$cur->next; } $hero->pre=$cur; if($cur->next!=null){ $cur->next->pre=$hero; } $cur->next=$hero; } } //删除某位英雄,现在不需要辅助节点了 public static function delHero($head,$herono){ //我们不使用辅助节点了 //先找到$head //$head不能动 //直接让$cur指向下一个,$head->next就是具体的英雄了,当然还要先判断,$cur->next是不是空,要是空,就是光有head了,也没有要删除的了 $cur=$head->next; $isFind=flase; //先搞个标志位 while($cur!=null){ if($cur->no==$herono){ //找到了,就置为true,并跳出 $isFind=true; break; } //如果上面的if没有找到,就不停的向下走 $cur=$cur->next; } if($isFind){ //当$isFind为真的时候,就说明找到了 //执行删除 //自我删除 //这里考虑了要删除的英雄在末尾的时候情况 if($cur->next!=null){ $cur->next->pre=$cur->pre; } $cur->pre->next=$cur->next; echo '
要删除的英雄编号是'.$cur->no; }else{ echo'
要删除的英雄没有'; } } //遍历显示双向链表,显示所有英雄的函数 public static function showHero($head){ //上来线做一个辅助指针,穿针引线 $cur=$head->next; //头本来就是空的,这样就输出打印的时候,不输出头了 //$cur=$head; 如果是这样,每次输出的时候会输出:编号= 名字= 外号= 把头也输出了 while($cur->next!=null){ //为空就说明到队尾了 echo'
编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname; //继续下移一步 $cur=$cur->next; } //当退出while的时候,$cur就指向了最后的那个节点了 //要是不输出,就会少一个人,少队尾的那个人 echo'
编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname; } } //创建一个头节点 $head=new Hero(); //创建一个英雄 $hero=new Hero(1,'宋江','及时雨'); //静态方法的调用 Hero::addHero($head,$hero); $hero=new Hero(2,'卢俊义','玉麒麟'); Hero::addHero($head,$hero); $hero=new Hero(6,'林冲','豹子头'); Hero::addHero($head,$hero); $hero=new Hero(3,'吴用','智多星'); Hero::addHero($head,$hero); $hero=new Hero(56,'孙二娘','母夜叉'); Hero::addHero($head,$hero); echo '
英雄排行榜.....'; //显示 Hero::showHero($head); echo'
'; echo '
删除后的英雄排行榜.....'; Hero::delHero($head,3); Hero::showHero($head); echo'
'; echo'
下面测试删除最前面的和最后面的英雄
'; echo '
删除后的英雄排行榜.....'; Hero::delHero($head,1); Hero::showHero($head); echo'
'; echo '
删除后的英雄排行榜.....'; Hero::delHero($head,56); Hero::showHero($head); ?>

双向链表的好处:
双向链表可以完成自我删除,效率高一些,而且学明白双向链表后,为你将来,学习二叉树,霍夫曼树等等, 打下了良好的基础

通过学习,一定 要建立编程的思想,思考复杂的现象,在内存中是怎么倒腾的。


韩顺平_PHP程序员玩转算法公开课_学习笔记_源代码图解_PPT文档整理_目录

你可能感兴趣的:(算法)