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

时间:2017-09-18 08:24:29

标签: php quicksort usort

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

$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. 

代码的输出是:

li a:hover {
  background-color: #c0b283;
  color: white;
}

.dropdown:hover .dropbtn {
  background-color: #c0b283;
}

li.dropdown {
  display: inline-block;
  text-align: left;
  position: relative;
}

.dropdown-content {
  display: none;
  position: absolute;
  top: 100%;
  left: 0px;
  background-color: #f9f9f9;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 10;
}

.dropdown-content a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
  text-align: left;
}

.dropdown-content:hover {
  background-color: #f1f1f1
}

.dropdown:hover .dropdown-content {
  display: block;
}

.content{
  margin-top:75px;
}

创建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()机制是如何工作的。

感谢。

1 个答案:

答案 0 :(得分:3)

  1. 比较器如何工作?
  2. 我不确定这是问题的一部分,但要清楚比较器是如何工作的: 您有一个由有序列表$order指定的订单和一个特殊的比较器回调 list_cmp,它应该(返回)参数

    • $a小于$breturn -1或价值< 0
    • $a大于$breturn 1或价值> 0
    • $a等于$breturn 0

    list_cmp通过查找订单表而不是检查是否

    来完成此操作
    • $a的顺序较小(或相等),在这种情况下,循环会在return 0
    • 之前退出
    • $b的顺序较小,在这种情况下,循环会以return 1提前退出。

    请注意,根据PHP文档,这是错误的,它声明它需要正/负/ 0作为返回值。只有当您知道内部仅检查comparator($a,$b) > 0时,这才是正确的,这意味着它只会检查$b是否小于且不等于$a,这使其成为比较{{1} }。如果代码开始检查order of $a <= order of $b是否小于且不等于$a,则很容易中断。

    1. quicksort 排序如何运作?
    2. 对于初学者,我假设您使用的是PHP 7或更高版本。在这种情况下,你遇到了一个特殊情况,有6-15个元素大小的数组。 PHP 7+似乎没有使用quicksort作为短列表,而是使用了一个Insertion Sort Variant(由于硬件相关的东西,比如缓存/代码局部性,它很可能更快)。您可以检查排序源代码f.e.在Github PHP Mirror (as an example: PHP 7.0 Zend/Zend_sort.c Line 177-198)上。

      代码分为3个步骤:

      1. 比较:比较邻居元素$barray[j],如果array[j+1]继续,则转到2。
      2. 查找插入点:现在如果array[j] <= array[j+1],请向后扫描以查找array[j] > array[j+1] array[x] < array[j+1] <= array[x+1]的点(显然只有x < j点击开始)
      3. 插入:将元素x向上移动,使其变为x+1 ... j并将前元素插入位置x+2 ... j+1
      4. 如果您将该代码应用于配对(2-1,2-3,1-3,2-4,3-4,2-2,2-1,2-1,4-1,3- 1,1-1,2-2)代码的作用很明显。

        x+1

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

        大多数情况下,最好忽略深度实现细节,只将其减少到需要的东西。排序算法的典型问题是,如果它是稳定的/不稳定的,并且在-- [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 中执行O(log n)内存消耗。有更简单的方法来学习这些优化实现背后的核心算法,例如 Quicksort Dance 或任何其他可视化或旧的(e)书籍或带有示例的网页。

        - 已编辑

        为插入排序添加了一个(糟糕的,未经优化的,不安全的)php实现,以实现其工作原理的另一个可视化:

        O(n)

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

        <?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");