对象数组的排序算法

时间:2016-10-17 07:48:41

标签: php arrays algorithm sorting

我正在寻找最聪明的解决方案。

我有一组包含不同位置的数组,它有起点和终点。

我想对该数组进行排序,以便“Start / From”Point始终从前一个“To / End”点开始。

例如:

 Array
(
  [0] => planner\boarding\BoardingPasses Object
    (
        [from] => Gerona Airport
        [to] => Stockholm
    )

  [1] => planner\boarding\BoardingPasses Object
    (
        [from] => Madrid
        [to] => Barcelona
    )

  [2] => planner\boarding\BoardingPasses Object
    (
        [from] => Stockholm
        [to] => New York JFK
    )

  [3] => planner\boarding\BoardingPasses Object
    (
        [from] => Barcelona
        [to] => Gerona Airport
    )

 )

对该数组进行排序后,它将如下所示。

 Array
 (
  [0] => planner\boarding\BoardingPasses Object
    (
        [from] => Madrid
        [to] => Barcelona
    )

  [1] => planner\boarding\BoardingPasses Object
    (
        [from] => Barcelona
        [to] => Gerona Airport
    )

  [2] => planner\boarding\BoardingPasses Object
    (
        [from] => Gerona Airport
        [to] => Stockholm
    )

  [3] => planner\boarding\BoardingPasses Object
    (
        [from] => Stockholm
        [to] => New York JFK
    )

)

请看看我的算法是否运行完美,但我正在寻找更快速的解决方案而不是使用foreach循环来代替使用array_map来获得起点。

<?php
class SortPlan implements SortInterface
{
public static $cards;

public static $ArrangedArray = [];

public static $items = [];

public static function ArrangePlan($cards)
{
    // TODO: Implement SortPlan() method.

    self::$items = $cards;

    $from = array_map(function($e) { return $e->from; }, self::$items);
    $to = array_map(function($e) {return $e->to;}, self::$items);

    $getDiff = array_diff($from,$to);

    array_push(self::$ArrangedArray,$cards[key($getDiff)]);

    unset(self::$items[key($getDiff)]);

    for($i = 0; $i < count(self::$items); $i++)
    {
        foreach (self::$items as $p) {
            $des = end(self::$ArrangedArray);

            if ($des->to == $p->from) {
                array_push(self::$ArrangedArray, $p);
                unset($cards[key($p)]);
            }
        }
    }

    print_r(self::$ArrangedArray);


    return self::$ArrangedArray;
}
 }
 ?>

2 个答案:

答案 0 :(得分:2)

我不知道php,所以我会提供一般的算法。我的算法的时间复杂度应该是 O(nlogn)

首先根据frmarray属性制作一个名为from的所有对象的不同数组。

然后根据toarray属性对名为to的所有对象进行不同的数组。

到目前为止, O(nlogn)用于排序。

现在找到对象 Obj ,使Obj.To != any object's From,这可以通过一次考虑一个对象然后在数组上应用二进制搜索轻松完成frmarray

由于二进制搜索需要O(log n)时间,我们会为每个元素执行此操作,因此时间仍为 O(nlogn)

对象 Obj 将是排序数组的最后一个元素。

可以使用数组second last上的二进制搜索轻松找到toarray元素,我们将搜索To = Obj.From这样的对象。

同样可以通过应用二进制搜索找到third last元素。

此步骤同样也是 O(nlogn)因此,我算法的总时间复杂度为O(nlogn)

答案 1 :(得分:1)

要求:使用路线信息对登机牌列表进行排序,以便“...”和&#39;来自&#39;相邻路线是相同的。即有序的目的​​地列表。

示范:https://eval.in/662878

我使用的方法是:

  • 创建符合规则的路线的部分列表
  • 每次机会发生时合并部分列表。

为此,我们需要快速查找相邻路线。

我们需要:

  • 由&#39;
  • 键入的路线列表
  • 由&#39;至&#39;
  • 键入的路线列表
  • 快速访问&#39;部分列表&#39;这条路线目前在。

处理:

对于每条路线:

  • 将其添加到from / to查找列表
  • 将其添加到相应的部分列表&#39;

这是有趣的地方:

我们检查相邻路线是否已经在列表中。可以有各种各样的可能性:

注意:Adjacent Routes非常重要,并且不需要快速排序&#39;我们希望如此。

问题在于,没有明显的方法可以通过比较路线来相互比较,以确定之前的路线是什么?或者&#39;之后&#39;另一个。我们可以比较的是它们是否是直接相邻的。对彼此。即&#39;来自&#39;一个匹配&#39; To&#39;另一个。或者相反。

因此,我们的想法是建立部分路径&#39;我们可以合并它们。

1)不匹配 - 开始新的部分路径&#39;。

2)&#39;来自&#39;或者&#39; To&#39;匹配 - 添加到适当的列表。

3)&#39;来自&#39;和&#39;到&#39;两者都匹配。这意味着我们可以加入两个部分列表&#39;一起!

在所有合并结束时,将有一条路径(RoutePath),所有路线都将指向该路径。

守则:

构建路径

处理各个路由的类,并确定要处理的RoutePath

class BuildPath {

   /**
    *
    * Generate partially sorted lists of boarding passes
    * when adding one at a time to an output list. 
    * 
    * Method: 
    *   o Each route will get appended to the approriate partial list as it processed.
    * 
    *   o When the route will join two partial lists then that are merged
    * 
    * Needed:
    *   o Fast lookup of adjacent routes to find out which list to appended to.
    * 
    *   o Fast lookup of the partial list
    */ 

    /**
    * Initial source array of boarding passes that hold routes
    * 
    * @var array  
    */
    private $boardingPasses = null;


    /**
    * stdClass Route object indexed by 'From' keys
    * 
    * @var array 
    */

    private $keyFrom = array();

    /**
    * stdClass Route object indexed by 'To' keys
    * 
    * @var array 
    */
    private $keyTo  = array();


    /**
    * @return void
    */    
    public function __construct(array $boardingPasses) 
    {
        $this->boardingPasses = $boardingPasses;

        foreach ($boardingPasses as  $key => $pass) {
            $route = current($pass);
            $route->passId = $key; // so I can get as the full data later                    

            $this->addRoute($route);
        }
    }


    public function addRoute($route)
    {
        /*
         *  Can new route be joined to an existing route? 
         */


         // Will this route join two partial lists    
         if (    $this->canAddFrom($route->from)
              && $this->canAddTo($route->to) ) { // join two partial lists together

            $this->keyFrom[$route->from] = $route;
            $this->keyTo[$route->to] = $route;

             // add to one list first - it doesn't matter which
            $this->getAddFromPath($route->from)->add($route);  

            // merge the two partial paths together
            $this->getAddFromPath($route->from)->merge($this->getAddToPath($route->to));


         } elseif ($this->canAddFrom($route->from)) { // add to this ;ist

            $this->keyFrom[$route->from] = $route;
            $this->keyTo[$route->to] = $route;

            $this->getAddFromPath($route->from)->add($route);  

         } elseif ($this->canAddTo($route->to)) { // add to existing path

            $this->keyFrom[$route->from] = $route;
            $this->keyTo[$route->to] = $route;

            $this->getAddToPath($route->to)->add($route);  

         } else { // start a new path

            $this->keyFrom[$route->from] = $route;
            $this->keyTo[$route->to] = $route;

            $path = new \RoutePath(); 

            $route->path = $path; // the path may change later  
            $path->add($route);

//            $this->routes[] = $route;
            return $route;
         }
    }  

    /**
    * The original array in path order
    * 
    * @return array
    */
    public function getSortedRoutes()
    {
        $out = array();
        foreach($this->getPath()->getRoutes() as $route) {
           unset($route->path);
           $out[] = $this->boardingPasses[$route->passId];         
        }

        return $out;
    }

    /*
     *  All routes should point to the same path
     *
     *  Whatever happens:  each route will point to the path it is in :)
     *             
     *  So, a scan of all the routes for different paths will find all the partial paths :)     
     */               
    public function getPath()
    {
        reset($this->keyFrom);
        return current($this->keyFrom)->path;
    }

    // helpers
    public function canAddFrom($from)
    {
        return isset($this->keyTo[$from]); 
    }

    public function canAddTo($to)
    {
        return isset($this->keyFrom[$to]); 
    }       

    public function getAddFromPath($from)
    {
        return $this->keyTo[$from]->path; 
    }

    public function getAddToPath($to)
    {
        return $this->keyFrom[$to]->path; 
    }       
}

RoutePath

The class that holds the 'partial list' of adjacent routes:

class RoutePath {

    private $path = array();

    /**
    * Add the route to the appropriate end of the list
    * 
    * @param stdClass $route
    * 
    * @return void
    */    
    public function add($route)
    {
        $route->path = $this; // ensure it pounts to correct path

        if (empty($this->path)) {

            array_push($this->path, $route);

        } elseif  ($this->canAddFrom($route->from)) {

            array_push($this->path, $route);

        } elseif ($this->canAddTo($route->to)) {

            array_unshift($this->path, $route);

        } else {

            throw new \Exception('Cannot add node: From: '. $route->from . ', To: '. $route->to, 500);

        }
    }

    /**
    * Merge two partial lists together
    * 
    * o Decide which list to append to 
    *
    * o Update all the routes to point to rthe merged path 
    * 
    * @param RoutePath $path
    * 
    * @return void
    */
    public function merge(\RoutePath $path)
    {
        if ($this->canAddFrom($path->getFrom())) {

            $path->updateRoutePath($this);             
            $this->path = array_merge($this->path, $path->getpath());

        } elseif ($this->canAddTo($path->getTo())) {

            $path->merge($this);

        } else {

            throw new \Exception('Cannot merge paths: '  
                                 . ' (this): From: '. $this->getFrom() .', To: '. $this->getTo()
                                 . ' (path): From: '. $path->getFrom() .', To: '. $path->getTo()
                                 , 500);

        }        
    }

    /**
    * Make all the routes point to the correct list
    * 
    * @param RoutePath $path
    * 
    * @return void
    */
    public function updateRoutePath(\RoutePath $path)
    {
        foreach ($this->path as $route) {
            $route->path = $path;
        }    
    }

    // always check the first entry in the list
    public function canAddTo($to)
    {
        return $this->getFrom() === $to; 
    }       

    // alway check last entry in the list
    public function canAddFrom($from)
    {
        return $this->getTo() === $from; 
    }

    public function getFrom()
    {
        reset($this->path);
        return current($this->path)->from;
    }           

    public function getTo()
    {
        return end($this->path)->to;
    }           

    public function getpath()
    {
        return $this->path;
    }

    public function getRoutes()
    {
        return $this->path;
    }
}

运行它并显示输出:

$path = new BuildPath($passes);
// print output...

echo '<pre>Sorted...', PHP_EOL;
    print_r($path->getSortedRoutes());
echo '</pre>';    

样本输出:

Sorted...
Array
(
    [0] => Array
        (
            [planner\boarding\BoardingPasses] => stdClass Object
                (
                    [from] => Madrid
                    [to] => Barcelona
                    [passId] => 1
                )
        )
    [1] => Array
        (
            [planner\boarding\BoardingPasses] => stdClass Object
                (
                    [from] => Barcelona
                    [to] => Gerona Airport
                    [passId] => 3
                )
        )
    [2] => Array
        (
            [planner\boarding\BoardingPasses] => stdClass Object
                (
                    [from] => Gerona Airport
                    [to] => Stockholm
                    [passId] => 0
                )
        )
    [3] => Array
        (
            [planner\boarding\BoardingPasses] => stdClass Object
                (
                    [from] => Stockholm
                    [to] => New York JFK
                    [passId] => 2
                )
        )
)

测试数据:

$passes = Array(
  0 =>   array('planner\boarding\BoardingPasses' => 
            (object) array('from' => 'Gerona Airport', 'to' => 'Stockholm')
         ),   

  1 => array('planner\boarding\BoardingPasses' => 
           (object) array('from' => 'Madrid', 'to' => 'Barcelona')
       ),

  2 => array('planner\boarding\BoardingPasses' =>
           (object) array('from' => 'Stockholm', 'to' => 'New York JFK')
       ),    

  3 => array('planner\boarding\BoardingPasses' =>
           (object) array('from' => 'Barcelona', 'to' => 'Gerona Airport')
       ),
 );