usort() 排序算法如何工作?

2024-01-14

我有一个 usort() 示例,我添加了一些 echo 语句来查看代码如何工作:

<?php
function list_cmp($a, $b) {
    global $order;
    echo "\$a=$a, \$b=$b </br>";

    foreach ($order as $key => $value) {
        echo "\$value=$value </br>";
        if ($a == $value) {
            echo "\$a=\$value, returing 0. </br>";
            return 0;
        }
        if ($b == $value) {
            echo "\$b=\$value, returing 1. </br>";
            return 1;
        }
    }
}

$order[0] = 1;
$order[1] = 3;
$order[2] = 4;
$order[3] = 2;

$array[0] = 2;
$array[1] = 1;
$array[2] = 3;
$array[3] = 4;
$array[4] = 2;
array[5] = 1;
$array[6] = 2;

usort($array, "list_cmp");
?>

代码的输出是这样的:

$a=2, $b=1 
$value=1 
$b=$value, returing 1. 
$a=2, $b=3 
$value=1 
$value=3 
$b=$value, returing 1. 
$a=1, $b=3 
$value=1 
$a=$value, returing 0. 
$a=2, $b=4 
$value=1 
$value=3 
$value=4 
$b=$value, returing 1. 
$a=3, $b=4 
$value=1 
$value=3 
$a=$value, returing 0. 
$a=2, $b=2 
$value=1 
$value=3 
$value=4 
$value=2 
$a=$value, returing 0. 
$a=2, $b=1 
$value=1 
$b=$value, returing 1. 
$a=2, $b=1 
$value=1 
$b=$value, returing 1. 
$a=4, $b=1 
$value=1 
$b=$value, returing 1. 
$a=3, $b=1 
$value=1 
$b=$value, returing 1. 
$a=1, $b=1 
$value=1 
$a=$value, returing 0. 
$a=2, $b=2 
$value=1 
$value=3 
$value=4 
$value=2 
$a=$value, returing 0. 

创建 12 个 $a-$b 对 - 2-1、2-3、1-3、2-4、3-4、2-2、2-1、2-1(再次相同)的机制是什么?)、4-1、3-1、1-1、2-2。上面的代码返回 1,1,0,1,0,0,1,1,1,1,0,0。根据返回的值对数组进行排序的机制是什么?我试图了解 usort() 机制是如何工作的。

Thanks.


  1. 比较器如何工作?

我不确定这是否是问题的一部分,但要清楚比较器的工作原理: 您有一个由有序列表指定的订单$order和一个特别的比较器回调 list_cmp哪个(应该)返回参数

  • $a小于$b (return -1或一个值< 0)
  • $a比...更棒$b (return 1或一个值> 0)
  • $a equal $b (return 0)

list_cmp通过查找它来做到这一点订单表而是检查是否

  • $a具有较小(或相等)的顺序,在这种情况下,循环会提前退出return 0 or if
  • $b顺序较小,在这种情况下循环会提前退出return 1.

请注意,根据 PHP 文档,这是错误的,它声明它需要正/负/0 作为返回值。仅当您知道内部仅检查comparator($a,$b) > 0,这意味着它只检查是否$b小于且不等于$a,进行比较order of $a <= order of $b。如果代码开始检查,它很容易被破坏$a小于且不等于$b.

  1. How does the quicksort sorting work?

首先,我假设您使用的是 PHP 7 或更高版本。在这种情况下,您会遇到具有 6-15 个元素大小的数组的特殊情况。 PHP 7+ 似乎没有对短列表使用快速排序,而是使用插入排序变体(由于与硬件相关的东西,如缓存/代码局部性,这很可能更快)。您可以检查排序源代码 f.e.于Github PHP 镜像(例如:PHP 7.0 Zend/Zend_sort.c 第 177-198 行) https://github.com/php/php-src/blob/eac0bf11e4e97916e9688b18e6d62572e12e129f/Zend/zend_sort.c#L176.

该代码分 3 个步骤运行:

  1. 比较:比较相邻元素array[j] and array[j+1], if array[j] <= array[j+1]继续,否则转到 2。
  2. 找到插入点: 现在如果array[j] > array[j+1],向后扫描以找到其中的点array[x] < array[j+1] <= array[x+1] for x < j(显然只有到x开始)
  3. insert: 移位元素x+1 ... j一个起来,这样它就变成了x+2 ... j+1并在位置插入前一个元素x+1

如果将该代码应用于配对 (2-1, 2-3, 1-3, 2-4, 3-4, 2-2, 2-1, 2-1, 4-1, 3-1, 1 -1, 2-2) 代码的作用显而易见。

-- [2,1],3,4,2,1,2 -> 1./2./3. compare [2,1], find and insert 1 before 2
-- 1,[2,3],4,2,1,2 -> 1./2. compare [2,3], find insert point for 3 (since order of 3 < order of 2)
-- [1,3],2,4,2,1,2 -> 3. compare [1,3], found insert point for 3 before 2
-- 1,3,[2,4],2,1,2 -> 1./2. compare [2,4], find insert point for 4 (since order of 4 < order of 2) 
-- 1,[3,4],2,2,1,2 -> 3. compare [3,4], found insert point for 4 before 2
-- 1,3,4,[2,2],1,2 -> 1. compare [2,2], skip
-- 1,3,4,2,[2,1],2 -> 1./2. compare [2,1], find insert point for 1
-- 1,3,4,[2,1],2,2 -> 2. compare [2,1], find insert point for 1
-- 1,3,[4,1],2,2,2 -> 2. compare [4,1], find insert point for 1
-- 1,[3,1],4,2,2,2 -> 2. compare [3,1], find insert point for 1
-- [1,1],3,4,2,2,2 -> 3. compare [1,1], fond insert point for 1 before 3
-- 1,1,3,4,2,[2,2] -> 1. compare [2,2], skip
-- sorted: 1,1,3,4,2,2,2

附: 在这里您已经看到,即使是简单的排序算法(22 行代码)通过其比较模式导出其工作也是相当复杂的。 PHP 7 快速排序实现的代码行数大约是原来的 10 倍,并且有一些奇怪的优化(除了由于枢轴选择和递归而导致的正常疯狂之外)。

大多数时候,最好忽略深入的实现细节,只将其减少到所需的内容。排序算法的典型问题是它是否稳定/不稳定以及执行情况如何O(log n) with O(n)内存消耗。有更简单的方法可以学习这些优化实现背后的核心算法,例如快速排序舞蹈或任何其他可视化或带有示例的好旧(电子)书或网页。

——编辑

添加了一个(糟糕的、未优化的、不安全的)插入排序的 php 实现,以直观地展示其工作原理:

<?php

function my_usort($A, $comparator) {
  // Start .. End Positions
  $current_pos = 0;
  $last_pos = count($A)-1;
  // Outer Loop: each step checks that A[0] up to A[current_pos] is sorted. 
  // When the algorithm finishes we know that A[0] ... A[last_pos] is sorted
  while($current_pos < $last_pos) {
    echo "Sorted Subarray from \$A is " . json_encode(array_slice($A, 0, $current_pos+1)) . "<br>\n";
    echo "\$A looks like this now: " . json_encode($A) . 
    ", comparing [" . $A[$current_pos] . "," . $A[$current_pos +1] . "] (verify step)<br>\n";
    // "Verification Step"
    // At this point A[0] ... A[current_pos] is sorted.
    // Check A[current_pos] <= A[current_pos +1]
    if($comparator($A[$current_pos], $A[$current_pos +1]) > 0) {
      // nope, A[current_pos] > A[current_pos +1] (list_cmp/comparator returns value > 0)
      // "Insertion Step" start, find the correct position for A[current_pos+1] in the already
      // sorted list A[0] ... A[current_pos]
      $insert_point = $current_pos;
      // Swap the missmatching Neighbor pair
      echo "swapping: \$A[" . $insert_point . "] and \$A[" . ($insert_point+1) . "]<br>\n";
      $tmp = $A[$insert_point +1];
      $A[$insert_point +1] = $A[$insert_point];
      $A[$insert_point] = $tmp;
      $sorted_up_to_current_pos = false;
      // Inner Loop: find correct insertion point
      while($insert_point > 0 && !$sorted_up_to_current_pos) {
        echo "\$A looks like this now: " . json_encode($A) . 
        ", comparing [" . $A[$insert_point-1] . "," . $A[$insert_point] . "] (insertion step)<br>\n";
      // "Insertion Step", Swap the missmatching Neighbor pairs until A[0] ... A[current_pos] is sorted again
      if($comparator($A[$insert_point-1], $A[$insert_point]) > 0) {
          // Swap the missmatching Neighbor pair
          echo "swapping: \$A[" . ($insert_point-1) . "] and \$A[" . $insert_point . "]<br>\n";
          $tmp = $A[$insert_point];
          $A[$insert_point] = $A[$insert_point-1];
          $A[$insert_point-1] = $tmp;
          // goto new pair
          $insert_point = $insert_point -1;
        } else {
          // found correct spot, done
          $sorted_up_to_current_pos = true;
        }
      }
      $A[$insert_point] = $tmp;
      echo "\$A looks like this now: " . json_encode($A) . ", insertion done<br>\n";
    }
    $current_pos = $current_pos + 1;
  }
  echo "Sorted Array \$A is " . json_encode(array_slice($A, 0, $current_pos+1)) . "<br>\n";
}

function list_cmp($a, $b) {
  global $order;
  //echo "\$a=$a, \$b=$b </br>\n";

  foreach ($order as $key => $value) {
      //echo "\$value=$value </br>\n";
      if ($a == $value) {
          echo "\$a=\$value, returing 0. </br>\n";
          return 0;
      }
      if ($b == $value) {
          echo "\$b=\$value, returing 1. </br>\n";
          return 1;
      }
  }
}

$order[0] = 1;
$order[1] = 3;
$order[2] = 4;
$order[3] = 2;

$array[0] = 2;
$array[1] = 1;
$array[2] = 3;
$array[3] = 4;
$array[4] = 2;
$array[5] = 1;
$array[6] = 2;

my_usort($array, "list_cmp");

输出现已完成,当前排序的数组、位置:

Sorted Subarray from $A is [2] 
$A looks like this now: [2,1,3,4,2,1,2], comparing [2,1] (verify step) 
$b=$value, returing 1.  
swapping: $A[0] and $A[1] 
$A looks like this now: [1,2,3,4,2,1,2], insertion done 
Sorted Subarray from $A is [1,2] 
$A looks like this now: [1,2,3,4,2,1,2], comparing [2,3] (verify step) 
$b=$value, returing 1.  
swapping: $A[1] and $A[2] 
$A looks like this now: [1,3,2,4,2,1,2], comparing [1,3] (insertion step) 
$a=$value, returing 0.  
$A looks like this now: [1,3,2,4,2,1,2], insertion done 
Sorted Subarray from $A is [1,3,2] 
$A looks like this now: [1,3,2,4,2,1,2], comparing [2,4] (verify step) 
$b=$value, returing 1.  
swapping: $A[2] and $A[3] 
$A looks like this now: [1,3,4,2,2,1,2], comparing [3,4] (insertion step) 
$a=$value, returing 0.  
$A looks like this now: [1,3,4,2,2,1,2], insertion done 
Sorted Subarray from $A is [1,3,4,2] 
$A looks like this now: [1,3,4,2,2,1,2], comparing [2,2] (verify step) 
$a=$value, returing 0.  
Sorted Subarray from $A is [1,3,4,2,2] 
$A looks like this now: [1,3,4,2,2,1,2], comparing [2,1] (verify step) 
$b=$value, returing 1.  
swapping: $A[4] and $A[5] 
$A looks like this now: [1,3,4,2,1,2,2], comparing [2,1] (insertion step) 
$b=$value, returing 1.  
swapping: $A[3] and $A[4] 
$A looks like this now: [1,3,4,1,2,2,2], comparing [4,1] (insertion step) 
$b=$value, returing 1.  
swapping: $A[2] and $A[3] 
$A looks like this now: [1,3,1,4,2,2,2], comparing [3,1] (insertion step) 
$b=$value, returing 1.  
swapping: $A[1] and $A[2] 
$A looks like this now: [1,1,3,4,2,2,2], comparing [1,1] (insertion step) 
$a=$value, returing 0.  
$A looks like this now: [1,1,3,4,2,2,2], insertion done 
Sorted Subarray from $A is [1,1,3,4,2,2] 
$A looks like this now: [1,1,3,4,2,2,2], comparing [2,2] (verify step) 
$a=$value, returing 0.  
Sorted Array $A is [1,1,3,4,2,2,2] 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

usort() 排序算法如何工作? 的相关文章

  • 如何确定 n 高数字金字塔中的最大路线成本

    我有一个像这样的数字金字塔 7 4 8 1 8 9 2 4 6 7 4 6 7 4 9 4 9 7 3 8 8 routes 32 每个数字都按其系列中的强大程度进行索引 0 9 gt 1 1 8 gt 5 2 8 gt 4 3 7 gt
  • 用于分页的php示例脚本[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 任何人都可以建议一个好的分页 php 脚本 其中人们想要分页显示数据库中的大量项目 以下链接可以帮助您
  • 在php中获取二进制数据大小的正确方法是什么?

    我已阅读文件的一部分 现在想确保该部分的大小正确 我怎样才能在 php 中做到这一点 part fread file 1024 return some function part 1024 我已经阅读了这些示例 但我怀疑是否要使用 strl
  • 返回变量在 PHP 中不起作用(即没有返回值!)

    我更喜欢使用 Python 语言进行编程 但必须使用 PHP 来处理特定的网站应用程序项目 在 PHP 中 我试图将函数中的值 返回 到主程序环境 用于后续计算 但无论我尝试什么 函数中计算的值都不会返回该值 而是从函数中回显 工作正常 在
  • 计算两个数字之间的差异

    我想计算两个给定数字之间的差异 例如 1 5 或 24 35 我必须将数组中的数字分开并写出它们之间的差异 如下所示 1 2 3 4 5 或 24 25 26 27 28 29 30 31 32 33 34 35 是否有任何 php 函数可
  • 如何通过键查找最大和最小日期

    我有一个数组 a array 2010 05 03 gt 100 2010 05 04 gt 400 2008 05 01 gt 800 2011 01 01 gt 800 我如何找到最大值和最小值key date 例如 max gt 20
  • 在 symfony2 中为特定控制器设置 max_execution_time

    Using ini set 我可以扩展最大执行时间一个脚本的 在Symfony2 我可以添加ini set to web app php and web app dev php将增加的执行时间应用于所有控制器 但在这种情况下 我只想扩展最大
  • 如何在Web服务中传递URL

    我想将此 URL 作为网址中的值传递http localhost h2orn php verify php email emails hash hash但是 我只能在 符号之前传递 我想传递所有 URL 我正在使用java网络服务 代码在这
  • Symfony php.ini Debian

    我在 Oracle VM VirtualBox 上使用 Debian 7 amd64 LAMP 服务器 我正在尝试安装 Symfony 但遇到了一些小问题 当我运行命令时 php my project name app check php
  • Laravel 5 与 SAML 2 和现有 IDP 集成

    我使用 Laravel 5 我正在尝试将 SAML 2 0 与其集成 我找到了这个包 https github com aacotroneo laravel saml2 https github com aacotroneo laravel
  • 如何将 ctype_alpha 与 UTF-8 结合使用

    如何将 ctype alpha 与 UTF 8 一起使用 我有这个代码 if empty POST false if isset POST first name empty POST first name if ctype alpha PO
  • 适用于 Windows 的 PHP 支持的 GUI 应用程序

    我知道 PHP 是一种解释性语言 对于基于 Web 的事物来说 不是为在实际操作系统上运行 GUI 应用程序而设计的 但是有没有办法呢 基本上 是否有一个框架 系统允许我创建 本机 基本上是二进制文件 exe 看起来像带有本机控件和所有内容
  • 如何在javascript中使用自动递增id?

  • 使用 PDO 在 SQLite 中检索单个(且唯一)行的最简单方法

    我有这个 PDO stmt db gt prepare SELECT FROM channels WHERE id id stmt gt bindValue id id SQLITE3 INTEGER result stmt gt exec
  • 登录后所有页面都应该是https吗?

    这有点难以解释 但我会尽力 有一个网站 每个页面上都有登录表单 其中包含用户名 密码字段 这些页面未使用 SSL 用户填写用户名 密码并提交表单后 表单将被发送到 https 的身份验证页面 对于这种情况我有几个疑问 向 https 页面提
  • Yii2 DropDownList Onchange 更改自动完成小部件“源”属性?

    我已经尝试过这个 yii2 依赖的自动完成小部件 https stackoverflow com questions 27025791 yii2 dependent autocomplete widget 但我不知道为什么它不起作用 这是我
  • 将秒转换为天、小时、分钟和秒

    我想转换一个变量 uptime这是秒 分为天 小时 分钟和秒 Example uptime 1640467 结果应该是 18 days 23 hours 41 minutes 这可以通过以下方式实现DateTime http php net
  • php,in_array,0值

    我试图理解in array下一个场景的行为 arr array 2 gt Bye 52 77 3 gt Hey var dump in array 0 arr 返回值in array 是布尔值true 正如你所看到的no值等于0 所以有人可
  • 无法在 Centos 上安装 php-mysqli 扩展

    我正在尝试将 mysqli 扩展安装到 php yum install php mysqli 我收到下一个错误 Transaction Check Error file usr share mysql charsets Index xml
  • PHP 中的嵌套 JSON 输出

    我正在为 iOS 应用程序构建 API 并尝试将 mySQL 数据转换为 JSON 字符串进行处理 所需的输出将需要顶级订单详细信息 例如客户名称和地址 然后是订购的产品子数组 我需要的两个表中有相当多的字段 我希望拥有所有字段 我已经构建

随机推荐

  • Flask,无法分配请求的地址[重复]

    这个问题在这里已经有答案了 我正在尝试在远程服务器上运行烧瓶应用程序 以便我可以从其他计算机访问它 服务器有一个公共 IP 我将 Flask 配置为在该 IP 上运行 但是当我运行脚本时 我得到以下回溯 注意 我已从回溯和代码中删除了公共
  • UITextField——观察 selectedTextRange 的变化?

    有什么方法可以观察 UITextField 的 selectedTextRange 的变化吗 我尝试观察所有 UIControlEvents 但更改 selectedTextRange 不会触发 UIControlEvent 另一个死胡同
  • java中如何替换空值空字符串? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我得到了null来自数据库的值 但我
  • Highchart 在 x 轴上显示字符串

    我试图将我的 x 轴值显示为系列的 highcharts 上的字符串 但得到 0 1 2 这是 xaxis 数组索引位置 如何在 higcharts 上显示格式化为字符串的 xaxis 值 这是我所拥有的 Highcharts chart
  • 最后命名的参数不是函数或数组?

    这个问题是关于 vararg 函数 以及省略号之前的最后一个命名参数 void f Type paramN va list ap va start ap paramN va end ap 我在阅读 C 标准时 发现了以下限制va start
  • Django 内联-允许添加禁用编辑

    你好 我在阅读以下问题后提出这个问题 问题 1 https stackoverflow com q 2951781 1095090 and 问题2 https stackoverflow com q 9504371 Question 1 没
  • 查看 pandas 系列的每一行中是否有项目

    我有一个包含以下数据的 pandas 系列 2015 07 24 Business Corporate 2015 07 24 Business Corporate 2015 07 08 Commentary World 2015 07 05
  • 具有静态成员的静态结构

    今天 我发现自己创建了一个 2 个 int 的静态数组 并且由于 C 不是 C 11 中不允许其内联初始化 因此我恢复使用 struct 类型的静态变量 class MyWidget static const struct Margin c
  • Nest.Js 不接受任何更改

    我尝试在里面创建一个新方法应用程序控制器但它没有反映变化 我什至尝试更改默认值获取你好 方法 但它正在输出 你好世界 这怎么可能 Insomnia 应用程序控制器 应用服务 Update npm run build npm run star
  • 将值从 servlet 传递到 html [重复]

    这个问题在这里已经有答案了 我有一个 Servlet 它处理来自 Web 的一些内容并生成一个字符串值 我需要在 html 页面的表标记内显示此字符串值 如何使用 setAttribute 方法和 getrequestdispatcher
  • 异常:将数据发布到 Google Pub/Sub 时出现 503 无法连接到所有地址

    我正在使用 Google Pub Sub 教程中的示例代码 当尝试发布消息时 抛出异常 503无法连接到所有地址 我向服务帐户授予了 Pub Sub 发布者角色 直到前天一切都运转良好 从控制台或 gcloud 命令发布消息时没有问题 Cl
  • Azure架构设计

    我是 Azure 新手 对 blob 存储有点困惑 我需要客户端通过 FTP SFTP 访问来推送和拉取文件 XML CSV EDI 等 推送的文件由 net 应用程序读入并写入数据库 据我了解 我们将使用 VM 角色来创建 FTP SFT
  • 将文件插入 mysql Blob

    我尝试在 blob 字段上插入 Open Office 文档 为此我尝试 INSERT INTO my table stamp docFile VALUES NOW LOAD FILE tmp my file odt 这在 Windows
  • 如何循环使用多个关键帧定义的 CSS 动画?

    问题 我有两个 css 关键帧动画 它们在单个元素上运行 fade bg animation name fade bg 1 fade bg 2 animation delay 0 6s animation iteration count i
  • 在 Java 中创建 JSONArray

    我如何创建 JSONArray 因为创建 JSONObject 非常简单 JSONObject j new JSONObject j put key value 现在我可以在 JSONObject 中放入另一个字符串 或者 JSONObje
  • LaTeX:在页边空白处排版章节号

    我正在尝试用 LaTeX 排版一些东西 我想知道我做得是否正确 基本思想是节号挂在左边距 对于章节标题 该数字采用标题的高度 2 行 对于节标题 该数字采用 1 行 并且与小节的标题高度相同 并且与标题的顶部对齐 请参阅下图以了解我在说什么
  • Capistrano 和环境变量

    我已经改用配置的环境变量 http 12factor net config它工作得很好 除非我必须使用 capistrano 部署或运行 任务 Capistrano 3 似乎执行每个带有前缀的命令 usr bin env这会删除我设置的所有
  • 如果我将代码添加到(不相关的)打印部分,“用于测量执行周期的双线程计数器模型”会更改输出

    标题有点晦涩难懂 解释如下 我有 2 线程模型 1 个线程正在繁忙循环内递增变量 另一个线程读取计数器t1 进行测量 再次读取计数器t2并将差异存储在数组中以供将来打印 你为什么不使用rdtscp 它正在序列化 并且已经作为指令内置在硬件中
  • Android - 将 URL 中的图像保存到 SD 卡上

    我想将 URL 中的图像保存到 SD 卡 以供将来使用 然后从 SD 卡加载该图像以将其用作 Google 地图的可绘制叠加层 这是该函数的保存部分 SAVE TO FILE String filepath Environment getE
  • usort() 排序算法如何工作?

    我有一个 usort 示例 我添加了一些 echo 语句来查看代码如何工作