韩顺平老师算法连载12(转载)

文西马龙: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

  1. <html>  
  2.     <head>  
  3.         <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />  
  4.     </head>  
  5.     <body>  
  6.         <h1>双向链表完成英雄排行管理</h1>  
  7.         <hr/>  
  8.         <a href="#">查询英雄</a>  
  9.         <a href="#">添加英雄</a>  
  10.         <a href="#">删除英雄</a>  
  11.         <a href="#">修改英雄</a>  
  12.   
  13.         <?php  
  14.             //使用PHP面向对象的方式来完成  
  15.   
  16.             class Hero{  
  17.                 public $pre=null; //表示指向前一个节点的引用  
  18.                 public $no;  
  19.                 public $name;  
  20.                 public $nickname;  
  21.                 public $next=null; //表示指向后一个节点的引用  
  22.   
  23.                 public function __construct($no='',$name='',$nickname=''){  
  24.                     $this->no=$no;  
  25.                     $this->name=$name;  
  26.                     $this->nickname=$nickname;  
  27.                 }  
  28.   
  29.                 //添加hero,这里我们会构建一个双向链表  
  30.                 //这里使用了静态函数  
  31.                 public static function addHero($head,$hero){  
  32.                     //$head头节点不能动  
  33.                     //$cur为辅助节点,让$cur来穿针引线  
  34.                     $cur=$head;  
  35.                     //isExist 假设英雄不存在  
  36.                     $isExist=FALSE; //这是一个标志位  
  37.   
  38.                     //首先看看目前的这个链表是不是空的  
  39.                     //当还有英雄的时候,$cur指向的$head节点的next属性必然为null,因为现在只有head节点,还没有添加英雄  
  40.                     //如果是空链表就直接加入  
  41.                     //怎样把空链表和普通链表并和在一起呢?  
  42.                     if($cur->next==null){ //如果是,就说明是空链表,添加第一个英雄  
  43.                         $cur->next=$hero;  
  44.                         //这样就首尾相连了  
  45.                         $hero->pre=$cur;  
  46.                     }else//当不是空节点的时候  
  47.                         //如果不是空节点,按照排名来添加  
  48.   
  49.                         //首先找到添加的位置  
  50.                         while($cur->next!=null){ //只要$cur->next不等于null,就说明没有到队尾  
  51.                             if($cur->next->no > $hero->no){ //说明位置找到了,则说明$hero要加在$cur的后面  
  52.                                 break;  
  53.   
  54.                             }else if($cur->next->no==$hero->no){  
  55.                                 $isExist=TRUE;  
  56.                                 echo'<br/>不能添加相同的编号';  
  57.                             }  
  58.                             //继续下一个  
  59.                             $cur=$cur->next;  
  60.                         }  
  61.   
  62.                         //退出该while循环时候,我们只需要把$hero添加到$cur后面即可,(队尾)  
  63.                         //说明还没有这个排名,可以添加,并可以和上面的空链表判断合并  
  64.                         if(!$isExist){ //如果不存在就添加,因为在上面break跳出的时候,就说明找到了,跳出的时候$isExist为假,则!$isExist为真  
  65.                             //添加,有点麻烦  
  66.                             $hero->next=$cur->next;  
  67.                             $hero->pre=$cur;  
  68.                             $cur->next->pre=$hero;  
  69.                             $cur->next=$hero;  
  70.                         }  
  71.                     }  
  72.                 }  
  73.   
  74.                 //遍历显示双向链表,显示所有英雄的函数  
  75.                 public static function showHero($head){  
  76.                     //上来线做一个辅助指针,穿针引线  
  77.                     $cur=$head->next; //头本来就是空的,这样就输出打印的时候,不输出头了  
  78.                     //$cur=$head; 如果是这样,每次输出的时候会输出:编号= 名字= 外号=  把头也输出了  
  79.   
  80.                     while($cur->next!=null){ //为空就说明到队尾了  
  81.                         echo'<br/>编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname;  
  82.                         //继续下移一步  
  83.                         $cur=$cur->next;  
  84.                     }  
  85.   
  86.                     //当退出while的时候,$cur就指向了最后的那个节点了  
  87.                     //要是不输出,就会少一个人,少队尾的那个人  
  88.                     echo'<br/>编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname;  
  89.                 }  
  90.             }  
  91.   
  92.             //创建一个头节点  
  93.             $head=new Hero();  
  94.             //创建一个英雄  
  95.             $hero=new Hero(1,'宋江','及时雨');  
  96.   
  97.             //静态方法的调用  
  98.             Hero::addHero($head,$hero);  
  99.               
  100.             $hero=new Hero(2,'卢俊义','玉麒麟');  
  101.             Hero::addHero($head,$hero);  
  102.   
  103.             $hero=new Hero(6,'林冲','豹子头');  
  104.             Hero::addHero($head,$hero);  
  105.   
  106.             $hero=new Hero(3,'吴用','智多星');  
  107.             Hero::addHero($head,$hero);  
  108.   
  109.             $hero=new Hero(56,'孙二娘','母夜叉');  
  110.             Hero::addHero($head,$hero);  
  111.             echo '<br/>英雄排行榜.....';  
  112.             //显示  
  113.             Hero::showHero($head);  
  114.         ?>  
  115.     </body>  
  116. </html>  


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

有一个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

  1. <html>  
  2.     <head>  
  3.         <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />  
  4.     </head>  
  5.     <body>  
  6.         <h1>双向链表完成英雄排行管理</h1>  
  7.         <hr/>  
  8.         <a href="#">查询英雄</a>  
  9.         <a href="#">添加英雄</a>  
  10.         <a href="#">删除英雄</a>  
  11.         <a href="#">修改英雄</a>  
  12.   
  13.         <?php  
  14.             //使用PHP面向对象的方式来完成  
  15.   
  16.             class Hero{  
  17.                 public $pre=null; //表示指向前一个节点的引用  
  18.                 public $no;  
  19.                 public $name;  
  20.                 public $nickname;  
  21.                 public $next=null; //表示指向后一个节点的引用  
  22.   
  23.                 public function __construct($no='',$name='',$nickname=''){  
  24.                     $this->no=$no;  
  25.                     $this->name=$name;  
  26.                     $this->nickname=$nickname;  
  27.                 }  
  28.   
  29.                 //添加hero,这里我们会构建一个双向链表  
  30.                 //这里使用了静态函数  
  31.                 //添加英雄,把添加时是空链表和不是空链表的情况,合并到一起了  
  32.                 public static function addHero($head,$hero){  
  33.                     //$head头节点不能动  
  34.                     //$cur为辅助节点,让$cur来穿针引线  
  35.                     $cur=$head;  
  36.                     //isExist 假设英雄不存在  
  37.                     $isExist=FALSE; //这是一个标志位  
  38.   
  39.                     //首先看看目前的这个链表是不是空的  
  40.                     //当还有英雄的时候,$cur指向的$head节点的next属性必然为null,因为现在只有head节点,还没有添加英雄  
  41.                     //如果是空链表就直接加入  
  42.                     //怎样把空链表和普通链表并和在一起呢?  
  43.                       
  44.                         while($cur->next!=null){ //只要$cur->next不等于null,就说明没有到队尾  
  45.                             if($cur->next->no > $hero->no){ //说明位置找到了,则说明$hero要加在$cur的后面  
  46.                                 break;  
  47.   
  48.                             }else if($cur->next->no==$hero->no){  
  49.                                 $isExist=TRUE;  
  50.                                 echo'<br/>不能添加相同的编号';  
  51.                             }  
  52.                             //继续下一个  
  53.                             $cur=$cur->next;  
  54.                         }  
  55.   
  56.                         //退出该while循环时候,我们只需要把$hero添加到$cur后面即可,(队尾)  
  57.                         //说明还没有这个排名,可以添加,并可以和上面的空链表判断合并  
  58.                         if(!$isExist){ //如果不存在就添加,因为在上面break跳出的时候,就说明找到了,跳出的时候$isExist为假,则!$isExist为真  
  59.                             //添加,有点麻烦  
  60.                             //当你添加的人物就在最后,比如现在只有1号英雄,再添加一个5号英雄,肯定是添加在1号英雄的后面  
  61.                             //这里加上if判断,会更稳健型些  
  62.                             if($cur->next!=null){  
  63.                                 $hero->next=$cur->next;  
  64.                             }  
  65.                             $hero->pre=$cur;  
  66.                             if($cur->next!=null){  
  67.                                 $cur->next->pre=$hero;  
  68.                             }  
  69.                             $cur->next=$hero;  
  70.                         }  
  71.                 }  
  72.   
  73.                 //遍历显示双向链表,显示所有英雄的函数  
  74.                 public static function showHero($head){  
  75.                     //上来线做一个辅助指针,穿针引线  
  76.                     $cur=$head->next; //头本来就是空的,这样就输出打印的时候,不输出头了  
  77.                     //$cur=$head; 如果是这样,每次输出的时候会输出:编号= 名字= 外号=  把头也输出了  
  78.   
  79.                     while($cur->next!=null){ //为空就说明到队尾了  
  80.                         echo'<br/>编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname;  
  81.                         //继续下移一步  
  82.                         $cur=$cur->next;  
  83.                     }  
  84.   
  85.                     //当退出while的时候,$cur就指向了最后的那个节点了  
  86.                     //要是不输出,就会少一个人,少队尾的那个人  
  87.                     echo'<br/>编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname;  
  88.                 }  
  89.             }  
  90.   
  91.             //创建一个头节点  
  92.             $head=new Hero();  
  93.             //创建一个英雄  
  94.             $hero=new Hero(1,'宋江','及时雨');  
  95.   
  96.             //静态方法的调用  
  97.             Hero::addHero($head,$hero);  
  98.               
  99.             $hero=new Hero(2,'卢俊义','玉麒麟');  
  100.             Hero::addHero($head,$hero);  
  101.   
  102.             $hero=new Hero(6,'林冲','豹子头');  
  103.             Hero::addHero($head,$hero);  
  104.   
  105.             $hero=new Hero(3,'吴用','智多星');  
  106.             Hero::addHero($head,$hero);  
  107.   
  108.             $hero=new Hero(56,'孙二娘','母夜叉');  
  109.             Hero::addHero($head,$hero);  
  110.             echo '<br/>英雄排行榜.....';  
  111.             //显示  
  112.             Hero::showHero($head);  
  113.         ?>  
  114.     </body>  
  115. </html>  

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


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


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

此时$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

  1. <html>  
  2.     <head>  
  3.         <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />  
  4.     </head>  
  5.     <body>  
  6.         <h1>双向链表完成英雄排行管理</h1>  
  7.         <hr/>  
  8.         <a href="#">查询英雄</a>  
  9.         <a href="#">添加英雄</a>  
  10.         <a href="#">删除英雄</a>  
  11.         <a href="#">修改英雄</a>  
  12.   
  13.         <?php  
  14.             //使用PHP面向对象的方式来完成  
  15.   
  16.             class Hero{  
  17.                 public $pre=null; //表示指向前一个节点的引用  
  18.                 public $no;  
  19.                 public $name;  
  20.                 public $nickname;  
  21.                 public $next=null; //表示指向后一个节点的引用  
  22.   
  23.                 public function __construct($no='',$name='',$nickname=''){  
  24.                     $this->no=$no;  
  25.                     $this->name=$name;  
  26.                     $this->nickname=$nickname;  
  27.                 }  
  28.   
  29.                 //添加hero,这里我们会构建一个双向链表  
  30.                 //这里使用了静态函数  
  31.                 //添加英雄,把添加时是空链表和不是空链表的情况,合并到一起了  
  32.                 public static function addHero($head,$hero){  
  33.                     //$head头节点不能动  
  34.                     //$cur为辅助节点,让$cur来穿针引线  
  35.                     $cur=$head;  
  36.                     //isExist 假设英雄不存在  
  37.                     $isExist=FALSE; //这是一个标志位  
  38.   
  39.                     //首先看看目前的这个链表是不是空的  
  40.                     //当还有英雄的时候,$cur指向的$head节点的next属性必然为null,因为现在只有head节点,还没有添加英雄  
  41.                     //如果是空链表就直接加入  
  42.                     //怎样把空链表和普通链表并和在一起呢?  
  43.                       
  44.                         while($cur->next!=null){ //只要$cur->next不等于null,就说明没有到队尾  
  45.                             if($cur->next->no > $hero->no){ //说明位置找到了,则说明$hero要加在$cur的后面  
  46.                                 break;  
  47.   
  48.                             }else if($cur->next->no==$hero->no){  
  49.                                 $isExist=TRUE;  
  50.                                 echo'<br/>不能添加相同的编号';  
  51.                             }  
  52.                             //继续下一个  
  53.                             $cur=$cur->next;  
  54.                         }  
  55.   
  56.                         //退出该while循环时候,我们只需要把$hero添加到$cur后面即可,(队尾)  
  57.                         //说明还没有这个排名,可以添加,并可以和上面的空链表判断合并  
  58.                         if(!$isExist){ //如果不存在就添加,因为在上面break跳出的时候,就说明找到了,跳出的时候$isExist为假,则!$isExist为真  
  59.                             //添加,有点麻烦  
  60.                             //当你添加的人物就在最后,比如现在只有1号英雄,再添加一个5号英雄,肯定是添加在1号英雄的后面  
  61.                             //这里加上if判断,会更稳健型些  
  62.                             if($cur->next!=null){  
  63.                                 $hero->next=$cur->next;  
  64.                             }  
  65.                             $hero->pre=$cur;  
  66.                             if($cur->next!=null){  
  67.                                 $cur->next->pre=$hero;  
  68.                             }  
  69.                             $cur->next=$hero;  
  70.                         }  
  71.                 }  
  72.   
  73.                 //删除某位英雄,现在不需要辅助节点了  
  74.                 public static function delHero($head,$herono){  
  75.                     //我们不使用辅助节点了  
  76.                     //先找到$head  
  77.                     //$head不能动  
  78.                     //直接让$cur指向下一个,$head->next就是具体的英雄了,当然还要先判断,$cur->next是不是空,要是空,就是光有head了,也没有要删除的了  
  79.                     $cur=$head->next;  
  80.                     $isFind=flase; //先搞个标志位  
  81.                     while($cur!=null){  
  82.                         if($cur->no==$herono){  
  83.                             //找到了,就置为true,并跳出  
  84.                             $isFind=true;  
  85.                             break;  
  86.                         }  
  87.   
  88.                         //如果上面的if没有找到,就不停的向下走  
  89.                         $cur=$cur->next;  
  90.                     }  
  91.   
  92.                     if($isFind){  
  93.                         //当$isFind为真的时候,就说明找到了  
  94.                         //执行删除  
  95.                         //自我删除  
  96.                         //这里考虑了要删除的英雄在末尾的时候情况  
  97.                         if($cur->next!=null){  
  98.                             $cur->next->pre=$cur->pre;  
  99.                         }  
  100.                         $cur->pre->next=$cur->next;  
  101.                         echo '<br/>要删除的英雄编号是'.$cur->no;  
  102.                     }else{  
  103.                         echo'<br/>要删除的英雄没有';  
  104.                     }  
  105.                 }  
  106.   
  107.                 //遍历显示双向链表,显示所有英雄的函数  
  108.                 public static function showHero($head){  
  109.                     //上来线做一个辅助指针,穿针引线  
  110.                     $cur=$head->next; //头本来就是空的,这样就输出打印的时候,不输出头了  
  111.                     //$cur=$head; 如果是这样,每次输出的时候会输出:编号= 名字= 外号=  把头也输出了  
  112.   
  113.                     while($cur->next!=null){ //为空就说明到队尾了  
  114.                         echo'<br/>编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname;  
  115.                         //继续下移一步  
  116.                         $cur=$cur->next;  
  117.                     }  
  118.   
  119.                     //当退出while的时候,$cur就指向了最后的那个节点了  
  120.                     //要是不输出,就会少一个人,少队尾的那个人  
  121.                     echo'<br/>编号='.$cur->no.'名字='.$cur->name.'外号='.$cur->nickname;  
  122.                 }  
  123.             }  
  124.   
  125.             //创建一个头节点  
  126.             $head=new Hero();  
  127.             //创建一个英雄  
  128.             $hero=new Hero(1,'宋江','及时雨');  
  129.   
  130.             //静态方法的调用  
  131.             Hero::addHero($head,$hero);  
  132.               
  133.             $hero=new Hero(2,'卢俊义','玉麒麟');  
  134.             Hero::addHero($head,$hero);  
  135.   
  136.             $hero=new Hero(6,'林冲','豹子头');  
  137.             Hero::addHero($head,$hero);  
  138.   
  139.             $hero=new Hero(3,'吴用','智多星');  
  140.             Hero::addHero($head,$hero);  
  141.   
  142.             $hero=new Hero(56,'孙二娘','母夜叉');  
  143.             Hero::addHero($head,$hero);  
  144.             echo '<br/>英雄排行榜.....';  
  145.             //显示  
  146.             Hero::showHero($head);  
  147.   
  148.             echo'<br/>';  
  149.             echo '<br/>删除后的英雄排行榜.....';  
  150.             Hero::delHero($head,3);  
  151.             Hero::showHero($head);  
  152.   
  153.             echo'<br/>';  
  154.             echo'<br/>下面测试删除最前面的和最后面的英雄<br/>';  
  155.             echo '<br/>删除后的英雄排行榜.....';  
  156.             Hero::delHero($head,1);  
  157.             Hero::showHero($head);  
  158.   
  159.             echo'<br/>';  
  160.             echo '<br/>删除后的英雄排行榜.....';  
  161.             Hero::delHero($head,56);  
  162.             Hero::showHero($head);  
  163.         ?>  
  164.     </body>  
  165. </html>  

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

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


韩顺平_PHP程序员玩转算法公开课_学习笔记_源代码图解_PPT文档整理_目录
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值