【问题标题】:How to sort a linked list in PHP?如何在 PHP 中对链表进行排序?
【发布时间】:2010-02-07 04:32:34
【问题描述】:
$arr[] = array(...,'id'=1,'prev'=>2,'next'=>null);
$arr[] = array(...,'id'=2,'prev'=>3..,'next'=>1);
$arr[] = array(...,'id'=3,'prev'=>4,'next'=>2);
..

每条记录的顺序可以任意。

如何对这种数组进行排序,使prev的值为null的记录排在第一位,nullnext的记录排在最后?

【问题讨论】:

  • 为什么同一行代码复制/粘贴了3次?
  • 我认为我们需要比这个更好的示例代码。
  • 为什么要用arrays来实现链表?对我来说,这听起来很麻烦,因为你可以简单地使用对象来实现同样的目标。
  • 这看起来不像是一个非常有效的链表实现。它甚至看起来不像是实际的链表实现。您应该使用对象(即标准方式)正确实现链接列表。然后您的解决方案变得容易得多。您可以将其设为按插入排序的列表,以便在插入时进行排序。
  • @zneak ,因为它是数据库的输出。

标签: php arrays linked-list


【解决方案1】:

数组不是链表的容器。 A linked list is a list with linked objects,不是包含有关系的对象的列表。基本上,你得到的是两个容器中最差的一个。我会尝试将该结构转换为其他一些数据容器;真正的链表永远不需要按照您需要对数据进行排序的方式进行排序。

好的方法会涉及到类似的事情。我将把在列表中间插入对象的方法留给你,这并不难。

<?php
class LinkedObject
{
    var $value;
    var $prev;
    var $next;

    public function __construct($value, $prev = null, $next = null)
    {
        $this->value = $value;
        $this->prev = $prev;
        $this->next = $next;
    }

    public function append(LinkedObject $insertee)
    {
        $link = $this;
        while($link->next != null)
            $link = $link->next;

        $link->next = $insertee;
        $insertee->prev = $link;
    }

    public function __toString()
    {
        $str = $this->value;
        if($this->next != null)
        {
            $str .= " » ";
            $str .= $this->next;
        }
        return $str;
    }
}

$head = new LinkedObject("foo");
$head->append(new LinkedObject("bar"));
$head->append(new LinkedObject("baz"));
echo $head . "\n"; // gives "foo » bar » baz"
?>

但是,如果出于某种神秘的原因,您真的非常需要将它们放在一个数组中,这就是您需要的:

<?php
function find_row($array, $id)
{
    foreach($array as $current_row)
    {
        if($current_row['id'] === $id)
            return $current_row;
    }
    return null;
}

function what_the_heck_sort($array)
{
    $start_record = $array[0];
    $working_record = $array[0];
    $result = array($working_record);
    while($working_record['prev'] !== null)
    {
        $working_record = find_row($array, $working_record['prev']);
        array_unshift($result, $working_record);
    }

    $working_record = $start_record;
    while($working_record['next'] !== null)
    {
        $working_record = find_row($array, $working_record['next']);
        array_push($result, $working_record);
    }
    return $result;
}

// the test code
$test = array(
    array("foo 01", 'id' => 0, 'prev' => null, 'next' => 1),
    array("foo 02", 'id' => 1, 'prev' => 0, 'next' => 2),
    array("foo 03", 'id' => 2, 'prev' => 1, 'next' => 3),
    array("foo 04", 'id' => 3, 'prev' => 2, 'next' => 4),
    array("foo 05", 'id' => 4, 'prev' => 3, 'next' => 5),
    array("foo 06", 'id' => 5, 'prev' => 4, 'next' => 6),
    array("foo 07", 'id' => 6, 'prev' => 5, 'next' => 7),
    array("foo 08", 'id' => 7, 'prev' => 6, 'next' => 8),
    array("foo 09", 'id' => 8, 'prev' => 7, 'next' => 9),
    array("foo 10", 'id' => 9, 'prev' => 8, 'next' => null));

shuffle($test);
print_r(what_the_heck_sort($test));
?>

但是说真的,帮自己一个忙,做一个真正的链表,使用对象而不是数组。在我看来,上面的排序方法在知道约束的情况下是相当不错的,但是它慢得离谱,因为它需要查找每个 id 的数组。

【讨论】:

  • 原因并不神秘,只是因为它是数据库的输出。
  • 如何创建一个以 id 值为 key 的数组?我认为这比每次遍历数组都要快得多。
【解决方案2】:

嗯,所以你想把你的数组变成这样的:

$array[] = array('id'=>1324, 'prev'=>null, 'next'=>15834);
$array[] = array('id'=>15834, 'prev'=>1324, 'next'=>1023);
$array[] = array('id'=>1023, 'prev'=>15834, 'next'=>12482);
$array[] = array('id'=>12482, 'prev'=>1023, 'next'=>null);

无论他们以什么顺序开始?好吧,这不是基本的排序模式,所以我会使用类似的东西:

// Find the first entry
foreach($arr as $index => $row) {
  if ($row['prev'] == null) {
    // This is the first row
    $cur_row = $row;
    break; // Jump out of the foreach loop
  }
}
$sorted = array();
$sorted[] = $cur_row;
while ($cur_row['next'] != null) {
  // Find the next row
  foreach($arr as $index => $row) {
    if ($row['id'] = $cur_row['next']) {
      // This is the next row
      $sorted[] = $row;
      $cur_row = $row;
      break; // Jump out of the foreach loop
    }
  }
}
print_r($sorted); // $sorted now has your sorted array

【讨论】:

  • 这个解决方案的复杂度为 O ( n^2 )。还不错,但请参阅我的答案以获得复杂度较低的解决方案。
【解决方案3】:

我会先将 id 作为数组键进行一轮。同时记录第一个元素。

$newArray = array():
$firstElement = null;
foreach( $array as $row ) {
    $newArray[$row['id']] = $row;
    if( $row['prev'] == null ) $firstElement = $row['id'];
}

之后,您可以像这样遍历列表:

$curId = $firstElement;
while($curId != null) {
    do_something($newArray[ $curId ])
    $curId = $newArray[ $curId ]['next'];
}

为了提高效率,您可能会考虑查看数据水合器(从数组中的数据库获取数据的函数)以查看或者它可以立即将 id 添加为数组键。您还可以使用查询排序顺序确保第一个元素始终是原始数组中的第一个元素,以便轻松找到该 id。

顺便说一句,不要将其称为链表实现,因为链表的特征在于对下一个元素(不是索引)的对象引用。

编辑:有一件事我还没有提到。如果要将排序列表放在数组中,请替换 do_something($newArray[ $curId ]);与 $array[] = $newArray[ $curId ];.

我相信这个解决方案比大多数其他解决方案更透明/更快,因为这会在整个阵列上花费两轮,或者如果您可以免费将第一部分集成到您的水合方法中,则只需对阵列进行一次迭代。

【讨论】:

    【解决方案4】:

    我相信内置的 usort 可以很好地适用于您描述的数据结构。

    编辑:这不能正常工作。请取消接受,以便我删除它。

    <?php
    //$arr = your array as described
    
    usort($arr, 'subkey_compare_next');
    
    function subkey_compare_next($a, $b) {
        $a = $a['next'];
        $b = $b['next'];
        if($a === null) {
            return -1;
        }
        if ($a == $b) {
            return 0;
        }
        return ($a < $b) ? -1 : 1;
    
    }
    ?>
    

    【讨论】:

    • 这实际上行不通;您的函数根据“下一个”属性对数组进行排序,对列表进行排序,使下一个的“空”值位于底部,其余按数字顺序排列。但这不是正确的排序顺序;它需要根据查找下一个 ID 的上一个/下一个值进行排序。使用我回答中的示例数组,您的数组将按 id = 15834、1023、1324、12482 的顺序排列,而不是 1324、15834、1023、12482,这是正确的。
    【解决方案5】:

    此代码将起作用

    $a[0] = array('0', '00', '000');
    $a[1] = array('1', '11', '111');
    $a[2] = array('2', '22', '222');
    $a[3] = array('3', '33', '333');
    $a[4] = array('4', '44', '444');
    $result = count($a);
    echo $result; // print count
    
    
    list ($result1, $result2, $result3) = $a[4]; // array to list
    echo $result3; // print data in list
    

    【讨论】:

      猜你喜欢
      • 2017-03-22
      • 1970-01-01
      • 2010-10-05
      • 1970-01-01
      • 2011-09-08
      • 1970-01-01
      • 2010-10-20
      相关资源
      最近更新 更多