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() 排序算法如何工作? 的相关文章

  • Laravel 中只向登录用户显示按钮

    如果我以 John 身份登录 如何才能只显示 John 的红色按钮而不显示 Susan 的红色按钮 测试系统环境 Win10 Laravel5 4 Mysql5 7 19 table class table table responsive
  • php,统计字符并删除超过140个字符的内容

    我需要一个 PHP 函数来计算短语的字符数 如果短语长度超过 140 个字符 则此函数应删除所有其他字符并在短语末尾添加三个点 例如我们有 message I am what I am and you are what you are et
  • 无法显示 Laravel 欢迎页面

    我的服务器位于 DigitalOcean 云上 我正在使用 Ubuntu 和 Apache Web 服务器 我的家用计算机运行的是 Windows 7 我使用 putty 作为终端 遵循所有指示https laracasts com ser
  • PHP 如何再次传输 mp3 流

    我正在尝试从 http 的无线电流 MP3 获取数据 并希望以 https 的形式将其流式传输 这是我尝试过的
  • SQL:在行中保留计数或从数据库中选择计数

    示例 我有 2 张桌子 类别 Posts 在这样的类别中保留帖子编号是一个好方法吗 类别 id title posts 1 golf 50 2 soccer 90 posts id title category id 1 news 1 1
  • “空合并”(??) 运算符的用途是什么? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 随着新的 PHP 版本 PHP 7 的发布 引入了新功能 这些新功能中有一个我不熟悉的操作符 这Nul
  • 使用 JavaScript 在 HTML 表中动态添加行并通过提交按钮获取每个文本框的文本框值

    我有一个可以动态添加行的表 当我提交保存按钮时 我想将每行中的数据获取到 php 数组 请有人帮我解决这个问题 我是java脚本的新手 对此知之甚少 谢谢你
  • 从前端更改记录顺序

    我在编写下一个功能时遇到问题 我希望用户能够重新排列记录并更改 display order 值 我使用 Jquery UI 的可拖放功能来促进这一点 我可以看到如何简单地交换 display order 值 但我想为一条记录设置一个显示顺序
  • PHP CSV VLookup

    我正在寻找一个 PHP 函数 它可以读取 CSV 文件并在第 1 列上执行 vlookup 以回显第 2 列中同一行的相关值 例如 如果 CSV 包含 Name Email John j email protected cdn cgi l
  • Magento - 从观察者方法重定向客户

    在本次活动中checkout cart add product complete 我希望客户被重定向到外部网页http www example com 为此 我使用这段代码 它根本不起作用 public function moduleMet
  • 如何从MySQL数据库获取今天/昨天的数据?

    我想从数据库中检索今天的数据 但我不知道该怎么做 我实际上想要获取不是过去 24 小时的数据 我只想获取今天的数据 因此基于实际服务器时间 我还想获取昨天的数据 谁能帮我怎么做 示例代码 SELECT id FROM folk WHERE
  • 检查用户是否连接到 Facebook,然后检查他是否喜欢某个页面

    有没有什么方法可以检查用户是否在我的外部页面上连接到 Facebook 而不让他们允许我的应用程序之一 同样的问题也适用于 检查用户是否喜欢某个页面 我检查了大约 20 个问题和 3 4 个教程 似乎所有问题都在讨论内部脚本 粉丝页面 应用
  • 将 Javascript 变量转换为 PHP 变量

    我想使用由 videoel getCurrentTime 函数返回给我的 javascript 变量 并将其转换为 php 变量 以便我能够将其添加到我的 SQL 插入查询中 例如 INSERT INTO tblData VALUES ph
  • 使用 swiftmailer 向多个收件人发送电子邮件

    我正在尝试在我的项目中使用 swiftmailer 以便我可以向多个用户发送 html 新闻通讯 我已经彻底搜索过 但我得到的一切从未对我有用 我想在表单输入字段中粘贴多个收件人 以逗号分隔 然后将 html 电子邮件发送给他们 我将收件人
  • Laravel + AngularJS Nginx 路由

    我有以下问题 我需要配置Nginx 这样在任何URL用户访问时 它都会保留uri 例如domain com some url 但仅传递给 laravel 并让 Angular 处理路由 Route get function return v
  • 使用 jquery 和 php 测试表单输入是否为 1 或 2 位整数

    我有一个表单 其中有五个字段全部设置为 maxlength 2 基本上 我希望唯一可以输入的值是一位或两位整数 因为在将值存储在数据库中之前对这些字段执行计算 是否有任何 jquery 不允许用户输入不是整数的值 另外 用 jquery 和
  • 登录页面上出现错误“警告:尝试访问 bool 类型值的数组偏移量”[重复]

    这个问题在这里已经有答案了 我目前正在为一个学校项目制作一个网站 并且正在制作一个用户注册系统 目前 注册部分与进入 MySQL 数据库的用户数据完美配合 但是 我的登录页面似乎已损坏 每次我尝试登录时都会收到以下错误 警告 尝试访问第 2
  • 是否可以从插件扩展 Wordpress XMLRPC 接口?

    是否可以创建一个插件 在激活时向 XMLRPC 接口添加新的 功能 并处理其调用 简而言之 是的 您可以将函数添加为插件或添加到主题的functions php 文件中来处理XMLRPC 调用 您将需要以下部分 function xml a
  • 如何使用 Mockery 在第 N 次调用模拟方法时抛出异常

    我需要测试我编写的某些代码多次调用另一个类上的方法时的行为 其中一次调用将导致抛出异常 我在用Mockery http docs mockery io模拟可能引发异常的类 因此 就我而言 该方法将被调用三次 我需要它在第二次抛出异常 这是我
  • 使用 ActiveRecord 和 Yii2 记录实际的 SQL 查询?

    我正在这样做 students Student find gt all return this gt render process array students gt students 然后在视图中 foreach students as

随机推荐

  • 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 语句来查看代码如何工作