使用PHP'排序时保留键顺序(稳定排序);UAS排序

使用PHP'排序时保留键顺序(稳定排序);UAS排序,php,arrays,algorithm,sorting,Php,Arrays,Algorithm,Sorting,这个问题实际上是从这里的另一个问题中得到启发的,所以我想把它扩展一点 在PHP中使用一个关联数组可以对其值进行排序,但是如果这些值相等,则可以使用一个(或多个)PHP内置的排序函数来保留原始键顺序 下面是我用来测试可能的解决方案的脚本(没有找到任何): 陷阱:由于键是按原始数组排序的,请不要尝试建议按键排序以恢复到原始顺序。我对它们进行了排序,以便于直观地检查它们在输出中的顺序。因为,您需要编写自己的函数。 这似乎符合你的要求: 正如手册所说,“如果两个成员比较相等,那么它们在排序数组中的顺

这个问题实际上是从这里的另一个问题中得到启发的,所以我想把它扩展一点

在PHP中使用一个关联数组可以对其值进行排序,但是如果这些值相等,则可以使用一个(或多个)PHP内置的排序函数来保留原始键顺序

下面是我用来测试可能的解决方案的脚本(没有找到任何):


陷阱:由于键是按原始数组排序的,请不要尝试建议按键排序以恢复到原始顺序。我对它们进行了排序,以便于直观地检查它们在输出中的顺序。

因为,您需要编写自己的函数。

这似乎符合你的要求:

正如手册所说,“如果两个成员比较相等,那么它们在排序数组中的顺序是未定义的。”这意味着使用的排序不是“稳定的”,可能会改变比较相等的元素的顺序

有时候你真的需要一个稳定的排序。例如,如果您按一个字段对列表进行排序,然后再按另一个字段对其进行排序,但不希望失去上一个字段的排序。在这种情况下,最好将usort与考虑两个字段的比较函数一起使用,但如果不能这样做,则使用下面的函数。它是一种合并排序,保证了O(n*log(n))的复杂性,这意味着即使使用更大的列表,它也能保持相当快的速度(与冒泡排序和插入排序不同,它们是O(n^2))




此外,您可能会发现有趣的内容。

为完整起见,您还应查看:

PHP的默认排序算法适用于数组,因为:

array(1, 0) < array(2, 0); // true
array(1, 1) < array(1, 2); // true
很方便,只需使用一个有序范围作为第二个数组(
$order
只是临时的,它用于按原始顺序对第一个数组的等效项进行排序):

输出

array(4) {
  ["key-99"]=>
  int(3)
  ["key-2"]=>
  int(3)
  ["key-0"]=>
  int(5)
  ["key-3"]=>
  int(7)
}
我使用带有未排序键的测试数据来证明它工作正常。尽管如此,以下是测试脚本的输出:

缺点 它只适用于预定义的比较,不能使用自己的比较函数。可能的值(array_multisort()的第二个参数)为:

排序类型标志

  • 排序\u ASC
    -按升序排序项目
  • 排序\u DESC
    -对项目进行向下排序
  • 常规排序
    -正常比较项目(不更改类型)
  • 数字排序
    -对项目进行数字比较
  • SORT\u STRING
    -将项目作为字符串进行比较
  • SORT\u LOCALE\u STRING
    -根据当前区域设置将项目作为字符串进行比较。它使用区域设置,可以使用
    setlocale()
  • SORT\u NATURAL
    -使用类似
    natsort()
  • SORT\u FLAG\u CASE
    -可与
    SORT\u STRING
    SORT\u NATURAL
    组合(按位或)对字符串大小写进行不敏感排序

以一些非常具体的案例来完成回答。如果
$array
的数组键是默认值,那么一个简单的
数组_值(asort($array))
就足够了(这里以升序为例)

作为将来的参考,我在Github上放置了一组内置PHP函数的稳定排序变体:,基于@Jack的解决方案和其他一些技巧。

这是一个解决方案,使用它可以在usort函数中实现稳定的排序

public function sortBy(array &$array, $value_compare_func)
{
    $index = 0;
    foreach ($array as &$item) {
        $item = array($index++, $item);
    }
    $result = usort($array, function($a, $b) use ($value_compare_func) {
        $result = call_user_func($value_compare_func, $a[1], $b[1]);
        return $result == 0 ? $a[0] - $b[0] : $result;
    });
    foreach ($array as &$item) {
        $item = $item[1];
    }
    return $result;
}

作为稳定排序的解决方案:


换句话说,这个问题的解决方案是一个稳定的排序算法,从表面上看,PHP的排序算法都不是。我对此很怀疑,但我想要一个明确的答案和/或一个可能的解决方法。是否有理由只在函数中使用biult?-如果这些排序函数中的任何一个将两个成员计算为相等,则顺序未定义(排序不稳定)。请看:它解决了我的问题。论坛线程不有趣。其他部分或你的答案是。@Alin,这就是为什么我写了“可能会觉得有趣…”:)@Alin,事实上它得出了一个结论,那就是文中描述的所有方法都不起作用。因此,在某种程度上,我们知道不要尝试这些方法。从这个意义上说很有帮助。我认为你应该把你的消息来源归为。我发现这个方法是重复的。请看@Martijn的答案,它使用了修饰和
uasort
,并带有包装比较函数,并且这个答案需要五分之一的时间(这是一个很好的例子,但并不有效)。请注意问题中的陷阱。您不应该依赖原始数组中要排序的键。@AlinPurcaru好的,很公平。编辑:)您也可以使用
array\u sort($a,sort\u ASC,array\u key($a),sort\u NATURAL)
进行类似形式的稳定排序。这将
['Sick'=>8,'Vacation'=>12,'Other'=>4,'Vacation'=>0,'Bereavement'=>0]
更改为
['Other'=>4,'Bereavement'=>0,'Vacation'=>0,'Sick'=>8,'Vacation'=>12]
comparator中的4x嵌套
array\u搜索
/
array\u键
调用从时间复杂度的角度来看确实很有害。本质上,在排序之前对索引进行一次编码,然后将其剥离,这两种操作都是线性操作,不会破坏底层的复杂性。另一方面,如图所示,遍历数组进行每次比较会使其达到O(n*n*log(n))——比冒泡排序更差,但如果只有几个元素,则可以。
array(1, 0) < array(2, 0); // true
array(1, 1) < array(1, 2); // true
// each parameter is an array with two elements
// [0] - the original item
// [1] - the array key
function mysort($a, $b)
{
    if ($a[0] != $b[0]) {
        return $a[0] < $b[0] ? -1 : 1;
    } else {
        // $a[0] == $b[0], sort on key
        return $a[1] < $b[1] ? -1 : 1; // ASC
    }
}
$a = [
  "key-0" => 5,
  "key-99" => 3,
  "key-2" => 3,
  "key-3" => 7
];

$order = range(1,count($a));
array_multisort($a, SORT_ASC, $order, SORT_ASC);

var_dump($a);
array(4) {
  ["key-99"]=>
  int(3)
  ["key-2"]=>
  int(3)
  ["key-0"]=>
  int(5)
  ["key-3"]=>
  int(7)
}
Array
(
    [key-1] => 10
    [key-4] => 10
    [key-5] => 20
    [key-8] => 20
    [key-6] => 30
    [key-9] => 30
    [key-2] => 40
    [key-0] => 50
    [key-3] => 50
    [key-7] => 50
)
public function sortBy(array &$array, $value_compare_func)
{
    $index = 0;
    foreach ($array as &$item) {
        $item = array($index++, $item);
    }
    $result = usort($array, function($a, $b) use ($value_compare_func) {
        $result = call_user_func($value_compare_func, $a[1], $b[1]);
        return $result == 0 ? $a[0] - $b[0] : $result;
    });
    foreach ($array as &$item) {
        $item = $item[1];
    }
    return $result;
}