喜悦国际村 
» 游客:  注册 | 登录 | 搜索 | 统计 | 喜悦证交所 | 帮助

RSS 订阅当前论坛  

上一主题 下一主题
 25  1/3  1  2  3  > 
     
标题: [原创] PHP数组模拟常用数据结构  
 
玉面修罗
注册会员
Rank: 2


UID 78111
精华 1
积分 128
帖子 1575
金钱 118 喜悦币
威望 10
人脉 0
阅读权限 20
注册 2006-8-11
状态 在线
PHP数组模拟常用数据结构

前段时间受村里一个帖子的启发,突然想到了写这些代码。

众所周知,PHP的数组系统功能超级强大,关联数组+变量的弱类型特性使得PHP的数组可以实现非常多的灵活的功能。以前在学习C的时候,就学过数据结构,当时是利用C的指针来实现,但是接触PHP半年,没有发现谁在PHP中讨论数据结构,这大概是由语言的特性以及其应用领域所造成的。不过,我们依然可以在PHP中“模拟”出一些常用的数据结构,当然这些并不能算做真正意义上的数据结构。
我写这个完全是凭借个人兴趣,可能实用性并不是非常强。但是也许偶尔一两个比较复杂的问题正好需要这来求解,我会给出一些简单的应用实例。也许你会说,那些例子直接用PHP的数组也能够做到,的确如此,但是你仔细观察也会发现,这些结构的引入简化了程序的设计,划分了不同的关注层次,使我们思考范围缩小。比如我们熟悉的ISO/OSI模型和MVC开发模式等都是把复杂问题分层的机制。而直接用数组不仅掩盖了问题的本质,还要分散精力去考虑数组下标增减等问题。
这些代码还没有经过非常严格的测试,所以如果大家发现一些BUG,请回贴说明,或则给我发邮件。
QQ:21058652 MSN\E-mail:xiuluo-999@163.com
另外关于设计方面有不合理的地方,还希望各位大鸟帮忙指出,感激不尽。

1:单链表
单链表是最简单的链表表示。用它来表示线性表时,每一个数据元素占用一个结点(node)。一个结点一般由两个域组成,一个域存放数据元素data; 另一个域存放一个指向链表中下一个结点的指针link,它指出下一个结点 的开始存储地址。而最后一个结点的指针为空。单链表中数据元素之间的逻 辑关系是由结点中的指针指示的,换句话说,指针为数据元素之间的逻辑关系的映象,则逻辑上相邻的两个元素其存储的物理位置不要求紧邻,因此, 这种存储结构为非顺序映像或链式映像。

<?php
/**
 * PHP数组实现单链表结构
 *   此类主要是依靠PHP强大的数组系统来模拟出单链表类型的数据结构。 本人完全凭借自己的
 * 兴趣来编写此类,并未考虑其实用性,主要是给大家理解一些简单的数据结构知识,同时也训练
 * 一下PHP中的数组运用能力。
 *单链表简介:
 * 单链表是最简单的链表表示。用它来表示线性表时,每一个数据元素占用一个结点(node)。一个
 * 结点一般由两个域组成,一个域存放数据元素data; 另一个域存放一个指向链表中下一个结点的
 * 指针link,它指出下一个结点 的开始存储地址。而最后一个结点的指针为空。单链表中数据元素
 * 之间的逻 辑关系是由结点中的指针指示的,换句话说,指针为数据元素之间的逻辑关系的映象,则
 * 逻辑上相邻的两个元素其存储的物理位置不要求紧邻,因此, 这种存储结构为非顺序映像或链式映像。
 * 当然,在PHP没有指针这个概念,但是我们可以用关联数组来模拟。
 * @version 1.0
 * @author 玉面修罗<[email]xiuluo-999@163.com[/email]>
 */
class LinkList 
{
   
/**
    * 成员变量
    * @var array    $linkList       链表数组
    * @var number   $listHeader     表头索引
    * @var number   $listLength     链表长度
    * @var number   $existedCounts  记录链表中出现过的元素的个数,和$listLength不同的是, 删除一
    *                               个元素之后,该值不需要减1,这个也可以用来为新元素分配索引。                          
    */
   
protected  $linkList  =array();
   protected  
$listLength=0;
   protected  
$listHeader=null;
   protected  
$existedCounts=0;
   
/**
    * 构造函数
    *   构造函数可以带一个数组参数,如果有参数,则调用成员方法
    * createList将数组转换成链表,并算出链表长度.如果没有参
    * 数,则生成一空链表.空链表可以通过调用成员方法createList
    * 生成链表.
    * @access public
    * @param  array $arr 需要被转化为链表的数组
    */
   
public function __construct($arr='')
   {
        
$arr!=null&&$this->createList($arr);
   }
   
/**
    * 生成链表的函数
    *   将数组转变成链表,同时计算出链表长度。分别赋值给成员标量
    * $linkList和$listLength.
    * @access public
    * @param  array $arr 需要被转化为链表的数组
    * @return boolean  true表示转换成功,false表示失败  
    */
  
public function createList($arr)
  { 
      if (!
is_array($arr)) 
       return 
false;
      
$length=count($arr);
      for(
$i=0;$i<$length;$i++)
      {   
          if(
$i==$length-1)
          {
           
//每个链表结点包括var和next两个索引,var表示结点值,next为下一个结点的索引
           //最后一个结点的next为null
           
$list[$i]['var']  =$arr[$i];
           
$list[$i]['next'] =null;
          }
          else 
          {
           
$list[$i]['var']  =$arr[$i];
           
$list[$i]['next'] =$i+1;
          }
      }
      
$this->linkList      =$list;
      
$this->listLength    =$length;
      
$this->existedCounts =$length;
      
$this->listHeader=0;
      return 
true;
  }
  
/**
   * 将链表还原成一维数组
   * @access public
   * @return array    $arr  生成的一维数组
   */
  
public function returnToArray()
  { 
      
$arr=array();
      
$tmp=$this->linkList[$this->listHeader];
    for(
$i=0;$i<$this->listLength;$i++)
      {
        
$arr[]=$tmp['var'];
        if (
$i!=$this->listLength-1
        {
        
$tmp=$this->linkList[$tmp['next']];
        }
   }
      return 
$arr;
  }
  
/**
   * 返回链表的长度
   * @access public
   * @return number $listLength 链表的长度
   */
  
public function getLength()
  {
      return 
$this->listLength;
  }
  
/**
   * 计算一共删除过多少个元素
   * @access public 
   * @return number $count 到目前为止删除过的元素个数
   */
  
public function getDeletedNums()
  {
      
$count=$this->existedCounts-$this->listLength;
      return 
$count;
  }
  
/**
   * 通过元素索引返回元素序号
   * @access protected
   * @param  $index     元素的索引号
   * @return $num       元素在链表中的序号
   */
  
public function getElemLocation($index)
  {
  if (!
array_key_exists($index,$this->linkList)) 
   return 
false;
    
$arrIndex=$this->listHeader;
    for(
$num=1;$tmp=$this->linkList[$arrIndex];$num++)
    {
        if (
$index==$arrIndex
        break;
        else 
        {
            
$arrIndex=$tmp['next'];
        }
    }
    return 
$num;
  }
  
/**
   * 获取第$i个元素的引用
   *   这个保护方法不能被外界直接访问,许多服务方法以来与次方法。
   * 它用来返回链表中第$i个元素的引用,是一个数组
   * @access protected
   * @param  number $i 元素的序号
   * @return reference 元素的引用
   */
  
protected function &getElemRef($i)
  {
      
//判断$i的类型以及是否越界
      
$result=false;
      if (!
is_numeric($i)||(int)$i<=0||(int)$i>$this->listLength
      return 
$result;
   
//由于单链表中的任何两个元素的存储位置之间没有固定关系,要取得第i个元素必须从
   //表头开始查找,因此单链表是非随机存储的存储结构。
   
$j=0;
   
$value=&$this->linkList[$this->listHeader];
   while (
$j<$i-1)
   {
       
$value=&$this->linkList[$value['next']];
       
$j++;
   }
   return 
$value;
  }
  
/**
   * 返回第i个元素的值
   * @access public
   * @param  number $i     需要返回的元素的序号,从1开始
   * @return mixed  第i个元素的值
   */
  
public function getElemvar($i)
  {
    
$var=$this->getElemRef($i);
    if (
$var!=false
    {
        return 
$var['var'];
    }
    else return 
false;
  }
  
/**
   *   在第i个元素之后插入一个值为var的新元素
   *   i的取值应该为[1,$this->listLength],如果i=0,表示在表的最前段插入,
   * 如果i=$this->listLength,表示在表的末尾插入,插入的方法为,将第$i-1个元素
   * 的next指向第$i个元素,然后将第$i个元素的next指向第$i+1个元素,这样就实现了插入
   * @access public
   * @param  number $i   在位置i插入新元素
   * @param  mixed  $var 要插入的元素的值 
   * @return boolean  成功则返回true,否则返回false
   */
  
public function insertIntoList($i,$var)
  {
      if (!
is_numeric($i)||(int)$i<0||(int)$i>$this->listLength
      return 
false;
      if (
$i==0
      {
      
//如果$i-0,则在表最前面添加元素,新元素索引为$listLength,这样是确保不会
      //覆盖原来的元素,另外这种情况需要重新设置$listHeader
          
$this->linkList[$this->existedCounts]['var'] =$var;
          
$this->linkList[$this->existedCounts]['next']=$this->listHeader;
          
$this->listHeader=$this->existedCounts;
          
$this->listLength++;
          
$this->existedCounts++;
          return 
true;    
      }
   
$value=&$this->getElemRef($i);
   
$this->linkList[$this->existedCounts]['var'] =$var;
   
$this->linkList[$this->existedCounts]['next']=($i==$this->listLength?null:$value['next']);
   
$value['next']=$this->existedCounts;
   
$this->listLength++;
   
$this->existedCounts++;
   return 
true;
  }
  
/**
   * 删除第$i个元素
   *   删除第$i个元素,该元素为取值应该为[1,$this->listLength],需要注意,删除元素之后,
   * $this->listLength减1,而$this->existedCounts不变。删除的方法为将第$i-1个元素的
   * next指向第$i+1个元素,那么第$i个元素就从链表中删除了。
   * @access public
   * @param  number $i 将要被删除的元素的序号
   * @return boolean    成功则返回true,否则返回false
   */
  
public function delFromList($i)
  {
      if (!
is_numeric($i)||(int)$i<=0||(int)$i>$this->listLength
      return 
false;
    if (
$i==1
    {
    
//若删除的结点为头结点,则需要从新设置链表头
      
$tmp=$this->linkList[$this->listHeader];
      unset(
$this->linkList[$this->listHeader]);
      
$this->listHeader=$tmp['next'];
      
$this->listLength--;
      return 
true;
    }
    else 
    {
     
$value    =&$this->getElemRef($i);
     
$prevValue=&$this->getElemRef($i-1);
     unset(
$this->linkList[$prevValue['next']]);
     
$prevValue['next']=$value['next'];
     
$this->listLength--;
     return 
true;
    }
  }
 
/**
  * 对链表的元素排序
  *  谨慎使用此函数,排序后链表将被从新初始化,原有的成员变量将会被覆盖
  * @accse public
  * @param  boolean  $sortType='true' 排序方式,true表示升序,false表示降序,默认true   
  */
 
public function listSort($sortType='true')
 {
   
//从新修改关联关系可能会更复杂,所以我选择先还原成一维数组,然后对数组排序,然后再生成链表
   
$arr=$this->returnToArray();
   
$sortType?sort($arr):rsort($arr);
   
$this->createList($arr);
 }
}
?>




PHPthink.com
待业中...
2007-1-25 06:56 PM#1
查看资料  发短消息  顶部
 
玉面修罗
注册会员
Rank: 2


UID 78111
精华 1
积分 128
帖子 1575
金钱 118 喜悦币
威望 10
人脉 0
阅读权限 20
注册 2006-8-11
状态 在线
[推荐阅读] 怎样取得当前时间的小时数(24小时)
自己先顶一下,现在只写这么多,以后有空再写写别的结构。。。



PHPthink.com
待业中...
2007-1-25 07:07 PM#2
查看资料  发短消息  顶部
 
剑枫 (雪花)
论坛元老
Rank: 8Rank: 8
欧玛嘎


UID 26144
精华 1
积分 4927
帖子 1692
金钱 4917 喜悦币
威望 10
人脉 0
阅读权限 90
注册 2004-2-14
来自 山东郓城
状态 离线
[推荐阅读] 请教过年新玩法
看似很强,只是看不懂



在场外支持奥运.....
2007-1-25 07:09 PM#3
查看资料  访问主页  发短消息  QQ  顶部
 
玉面修罗
注册会员
Rank: 2


UID 78111
精华 1
积分 128
帖子 1575
金钱 118 喜悦币
威望 10
人脉 0
阅读权限 20
注册 2006-8-11
状态 在线
[推荐阅读] 找到个理由灌水
其实很简单,大部分地方都是在操作数组。。



PHPthink.com
待业中...
2007-1-25 07:11 PM#4
查看资料  发短消息  顶部
 
剑枫 (雪花)
论坛元老
Rank: 8Rank: 8
欧玛嘎


UID 26144
精华 1
积分 4927
帖子 1692
金钱 4917 喜悦币
威望 10
人脉 0
阅读权限 90
注册 2004-2-14
来自 山东郓城
状态 离线
[推荐阅读] 为什么报错使用or die


QUOTE:
原帖由 玉面修罗 于 2007-1-25 07:11 PM 发表
其实很简单,大部分地方都是在操作数组。。
偶是半路出家,计算机只懂很很浅的皮毛

链表是啥东西?




在场外支持奥运.....
2007-1-26 01:54 AM#5
查看资料  访问主页  发短消息  QQ  顶部
 
玉面修罗
注册会员
Rank: 2


UID 78111
精华 1
积分 128
帖子 1575
金钱 118 喜悦币
威望 10
人脉 0
阅读权限 20
注册 2006-8-11
状态 在线
[推荐阅读] dedecms模板显示三级菜单
百度+搜狗........



PHPthink.com
待业中...
2007-1-26 03:06 AM#6
查看资料  发短消息  顶部
 
sanders_yao
版主
Rank: 7Rank: 7Rank: 7
or2 =333


UID 30286
精华 0
积分 2500
帖子 4653
金钱 2497 喜悦币
威望 0
人脉 3
阅读权限 100
注册 2004-7-23
来自 北京 菜户营
状态 离线
[推荐阅读] 一道高中时代的数学题
还好 学过一点c 我还认识链表
2007-1-26 11:06 AM#7
查看资料  Blog  发短消息  顶部
 
unspace (未知空间)
版主
Rank: 7Rank: 7Rank: 7
百万富翁


UID 67567
精华 0
积分 48877
帖子 4974
金钱 47798 喜悦币
威望 0
人脉 1079
阅读权限 100
注册 2005-12-28
来自 吉林
状态 离线
[推荐阅读] 怎么样删除数组里面的值?
大哥终于见到这样的贴了,顶你一下
有空把堆栈的,排序查找,遍历的都写一下,我不愿意看C代码,所以数据结构没学

偶喜欢看PHP的




7月1日起,北京市低保、最低工资标准、失业保险、工伤保险、基本养老金5项社会保障标准均将全部上调。其中,最低工资标准增加70元,提高到800元。
2007-1-26 11:19 AM#8
查看资料  访问主页  Blog  发短消息  顶部
 
玉面修罗
注册会员
Rank: 2


UID 78111
精华 1
积分 128
帖子 1575
金钱 118 喜悦币
威望 10
人脉 0
阅读权限 20
注册 2006-8-11
状态 在线
[推荐阅读] 晕了,用了半年才发现PHP5............
恩。。过几天,把堆栈的结构,模拟出来。
其实本人数据结构学的也很烂。只是了解一点点




PHPthink.com
待业中...
2007-1-26 02:21 PM#9
查看资料  发短消息  顶部
 
klht (宇宙)
中级会员
Rank: 3Rank: 3


UID 77270
精华 0
积分 427
帖子 1082
金钱 426 喜悦币
威望 0
人脉 1
阅读权限 30
注册 2006-7-30
来自 UN星系
状态 离线
[推荐阅读] PHP做批删除如何做啊?
什么树叉,  母叶子, 水仙花程序也用PHP给咱写写啊!!!



什么时候才能找到梦寐以求的PHPER工作...
2007-1-26 04:36 PM#10
查看资料  访问主页  Blog  发短消息  顶部
 25  1/3  1  2  3  > 
     


  可打印版本 | 推荐给朋友 | 订阅主题 | 收藏主题 | 开通个人空间  


 


Powered by Discuz! 6.1.0  © 2001-2010 Comsenz Inc.
Processed in 0.072789 second(s), 6 queries

(冀ICP备05009913号) 管理员:sadly 邮箱/MSN: sadly@phpx.com QQ:824008(长隐) 清除 Cookies - - Archiver - WAP