18.PHP基础-递归递推算法

编程思想

编程思想:如何利用数学模式,来解决对应的需求问题,然后利用代码实现对应的数据模型(逻辑)

算法:使用代码实现对应的数学模型,从而解决对应的业务问题

一.递推算法

递推算法是一种简单的算法:通过已知条件,利用特定关系得出中间理论,直到得到结果的算法,递推算法分为顺推和逆推两种

顺推:

通过最简单的条件(已知),然后逐步推演结果

逆推:

通过结果找到规律,然后推到已知条件

斐波那契数列: 1 1 2 3 5 8 13...,通常需求:请求得到指定位置N所对应的值是多少

找规律:

第一个数是1 if n==1 则 n=1;

第二个数 是 1 0+1=1 if n == 2 则n=1;

第三个数是2 1+1 =2 从第三个数开始 结果= 前两个数之和 = (n-2)+(n-1)

第三个数是3 2+1=3

第N个数是前两个数之和

php 复制代码
$num = 15;
$f[1] = 1; //已知条件
$f[2] = 1; //已知条件
for ($i = 3; $i <= $num; $i++) {
  $f[$i] = $f[$i - 1] + $f[$i - 2];
  echo $i . '=' . $f[$i] . PHP_EOL;
}
echo $f[$num];

//封装
function  test2($des)
{
  if ($des == 1 || $des == 2) {
    return 1;
  }
  $f[1] = 1; //已知条件
  $f[2] = 1; //已知条件
  for ($i = 3; $i <= $des; $i++) {
    $f[$i] = $f[$i - 1] + $f[$i - 2];
  }
  return $f[$des];
}
echo test2(15);

二.递归算法

递归算法是把问题转换为规模缩小为的同类问题的子问题。

然后递归调用函数(或过程)来表示问题的解

1.简化问题:找到最优子问题(不能再小)

2.函数自己调用自己

斐波那契数列 :1 1 23 5 8 14

需求:求指定位置的数列的值

规律:第一个和第二个为1.从第三个开始为缉拿连个之和

F(N)=F(N-1)+F(N-2)

F(N-1)=F(N-2)+F(N-3)

...

F(2)=F(1)=1;

递归思想中:有两个非常重要的点

递归点:发现当前问题可以有解决当前问题的函数,去解决规模比当前小一点的问题来解决

F(N) =F(N-1)+F(N-2);

递归出口:当前问题解决的时候。已经到达(必须有)最优子问题,不能再次调用函数

如果一个函数递归调用自己而没有递归出口:就是死循环

递归的本质是函数调用函数:一个函数需要开辟一块内存空间,递归会出现同时调用N多个函数(自己):

递归的本质就是利用空间换时间

php 复制代码
function test($n)
{
  if ($n <= 2) {
    return 1;
  }
  return test($n - 2) + test($n - 1);
};
echo test(15) . PHP_EOL;

三.数组排序

3.1 冒泡排序

冒泡排序(bubble sort)是一种计算机科学领域的较简单的排序算法

它重复地走过要排序的数列,依此比较两个元素,如果他们的书序错误就把他们交换过来。

走访数列的工作是重复地进行知道没有在需要交换,也就是说改数列已经排序完成

冒泡排序的算法思路:

1 比较相邻的元素,如果第一个比第二个大,就交换他们两个。

2、对每一对相邻元素做同样的工作,从开始第一对到结尾最后一对,在这一点,最后的元素应该是最大的书

3、针对所有的元素重复以上的步骤,除了最后一个

4、持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较

php 复制代码
<?php
echo "<pre>";

$arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
shuffle($arr);
echo "排序前:" . PHP_EOL;
print_r($arr);
//冒泡排序
//最后一个数不用排序外循环n次 内循环次数为  3+2+1
//则内循环为(n-1)+(n-2)+(n-3)+...1 = (n-1)*n/2
$n = 0;

for ($i = 0, $len = count($arr) - 1; $i < $len; $i++) {

  //减少内循环次数
  for ($j = 0, $len = count($arr) - 1; $j < $len - $i; $j++) {
    if ($arr[$j] > $arr[$j + 1]) {
      $temp = $arr[$j];
      $arr[$j] = $arr[$j + 1];
      $arr[$j + 1] = $temp;
    }
    $n++;
    echo  $n . "内循环" . PHP_EOL;
    print_r($arr);
  }
  echo $i . "外循环" . PHP_EOL;
  print_r($arr);
}

/* 
等差数列之和
(n - 1) + (n - 2) + ... + 1 
1 + 2 + 3 + ... + (n-1)//倒着写
头尾相加 (n-1)+1 +(n-2)+2+(n-3)+3+... =
那一共多少对呢? 
1+2+3
3+2+1 

n*(n - 1)/2 用 "(首项 + 末项) × 项数÷ 2" 

//项数(n-1) 
//头+尾巴 (n-1)+1 =n
//结果就是(n-1)*n/2

所以如果n = 3那么内循环次数是 (3-1) 3/2 = 3*2/2=3
如果n=10 就是9*10/2=45

*/
echo "</pre>";

简单示意图:例如[3,2,1]

3.2 选择排序

选择排序(selection sort)

是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。选择排序是最不稳定的排序方式(比如[5,5,3]第一次就讲第一个[5]与[3]交换,导致第一个5移动到了第二个5后面

选择排序的算法思路:

1、假设第一个元素是最小元素,记下下标

2、寻找右侧剩余的元素,如果有更小的,重新记下最新的下标

3、一轮结束后,锁定最小的标志位,交换当前外循环位置的下标与最小标志位下标的元素

4、往右重复以上步骤,直到元素本身是最后一个

php 复制代码
$arr = [1, 2, 3, 4, 5, 6];
shuffle($arr);
echo "排序前" . PHP_EOL;
print_r($arr);
$num = 0;

//选择排序 交换次数减少 性能比冒泡排序性能高
for ($i = 0; $i < count($arr) - 1; $i++) {
  $minIndex = $i;
  for ($j = $i + 1; $j < count($arr); $j++) {
    if ($arr[$j] < $arr[$minIndex]) {
      $minIndex = $j;
    }
    $num++;
    echo "第{$num}轮";
  }
  if ($minIndex != $i) {
    $temp = $arr[$i];
    $arr[$i] = $arr[$minIndex];
    $arr[$minIndex] = $temp;
  }
}

3.3 插入排序

插入排序(Insert sort)

插入排序的基本操作是将一个数据插入到已经排好序的有序数据中,从而得到一个新的,个数加1的有序数据,算法适用于少量数据的排序,是稳定的排序方法。

插入算法把要排序的数组分成两部分:

第一部分:左边是已经排好序的部分(从第 0 个元素开始,一开始它只有 1 个元素,所以天然有序)

第二部分:右边是还没插进去的部分(从第 1 个元素开始,每个轮到它的时候,就准备把它插到左边去)

插入排序的算法思路:

  1. 从数组第二个元素开始处理(因为第一个元素天然有序)
  2. 每次拿出这个元素(key)
  3. 从前一位开始向左扫描
  4. 只要发现比 key 大的,就把它们统统往右挪
  5. 直到找到不比 key 大的位置
  6. 在这个位置把 key 插进去
php 复制代码
function test($arr)
{
  $len = count($arr);
  for ($i = 1; $i < $len; $i++) {
    $key = $arr[$i]; //当前要插入的值
    $j = $i - 1;

    while ($j >= 0 && $arr[$j] > $key) {
      $arr[$j + 1] = $arr[$j]; //向后挪动一位
      $j--; //再判断前列的元素
    }
    $arr[$j + 1] = $key; //如果进入的循环插入到列的空出来的位置
    //如果没有进入循环,则插入到末尾 
  }
  return $arr;
}
// 测试
$nums = [5, 2, 8, 3, 1];
$result = test($nums);
print_r($result);

也可以倒过来

第一部分是无序区从第一个元素到倒数第二个

第二部分是有序区 最后一个元素

php 复制代码
function reverseInsertSort($arr)
{
    $len = count($arr);
    // 最后一个元素作为初始有序区
    for ($i = $len - 2; $i >= 0; $i--) { // 从倒数第二个开始
        $key = $arr[$i];
        $j = $i + 1;
        // 向后移动有序区元素
        while ($j < $len && $arr[$j] < $key) {
            $arr[$j - 1] = $arr[$j];
            $j++;
        }
        $arr[$j - 1] = $key;
    }
    return $arr;
}

// 测试
$nums = [5, 2, 8, 3, 1];
print_r(reverseInsertSort($nums));

有个网站可以帮助动画理解:

https://visualgo.net/zh/sorting

如下:

3.4 快速排序

快速排序(quick sort) 是对冒泡排序的一种改进。

通过一趟排序将要排序的数据分科成独立的两部分。

其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按照此方法对着两部分数据分别进行快速排序,整个排序过程可以递归进行,一次达到整个数据变成有序序列

设 要排序的数组是A[0]...A[N-1] ,首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比他小的数都放到它前面,所有比它大的数据都放到它后面,这个过程称为一趟快速排序。

值得注意的是,快速排序不是一种稳定点的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动

快速排序的算法是:

1、从数组中选出一个元素(通常是第一个),作为惨超对象。

2、定义两个数组,将目标数组中剩余的元素与参照元素挨个比较:

小的放到一个数组,大的放到另外一个数组

3、第二步执行完以后,前后的数组顺序不确定,但确定了自己的位置

4、将得到的小数组按照第1到第3部重复操作(子问题)

5、回溯最小数组(一个元素)。

实现代码

php 复制代码
<?php
echo "<pre>";
function test($arr)
{
  $len = count($arr);
  //如果数组元素为1 结束快排
  if ($len <= 1) return $arr; //返回数组本身 可能有一边是空数组

  //快速排序
  $leftArr =  $rightArr = [];
  for ($i = 1; $i < $len; $i++) {
    $value = $arr[$i]; //比较的值
    $middleValue = $arr[0]; //基准
    if ($middleValue > $value) {
      array_push($leftArr, $value);
      // $leftArr[] = $value
      //小于等于的情况划分到另一个组
    } else {
      array_push($rightArr, $value);
      // $rightArr[] = $value
    }
  }

  $res =  [...test($leftArr), $arr[0], ...test($rightArr)];
  //array_merge(test($leftArr),(array)$arr[0],test($rightArr));
  print_r($res);
  return $res;
}
$arr = [1, 2, 3, 3, 4, 5];
shuffle($arr);
print_r($arr);
$res = test($arr);
print_r($res);
echo "</pre>";

3.5 归并排序

conquer-征服,治服,战胜,胜利

归并排序(merge-sort)是建立在归并操作上的一种有效的排序算法,该算法采用分治法(divide and conquer)

的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列,即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

归并排序的算法是:

1、将数组拆分成两个数组

2、重复步骤1将数组拆分成最小单元

3、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

4、设定两个指针,最初位置分别为两个已经排序序列的起始位置

5、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位

6、重复步骤三知道某指针拆除序列尾

7、将另一序列剩下的所有元素直接复制到合并序列尾

可参考https://cs50.harvard.edu/x/2022/notes/3/#merge-sort

代码实现:

php 复制代码
function merge_sort($arr)
{
  $len = count($arr);
  //递归出口
  if ($len <= 1) return $arr;
  //拆分 
  $middle = floor($len / 2);
  $left = array_slice($arr, 0, $middle);
  $right = array_slice($arr, $middle);
  //递归点:$left和$right都没有排好序:而且可能是多个元素的数组
  $left = merge_sort($left);
  $right = merge_sort($right);
  //假设左边和右边都已经排好序:二路归并
  $m = array();
  while (count($left) && count($right)) {
    //只要$arr1和$arr2里面还有元素,就进行循环
    //取出每个数组的第一个元素:进行比较
    $m[] = $left[0] < $right[0] ? array_shift($left) : array_shift($right);
  }
  //返回结果 
  return array_merge($m, $left, $right);
};

四.查找算法

4.1 概念

查找是在大量的信息中寻找一个特定的信息元素,在计算机应用中,查找是常用的基本运算,查找算法是指实现查找过程对应的代码结

4.2 顺序查找算法

顺序查找算法也称为线性查找,从数据结构线性表的一段开始,顺序扫描,依此将扫描到的结点关键字与给定值k相比较,如果相等则表示查找成功,若扫描结束仍没有找到关键字等于k的结点,表示查找失败

代码实现:

php 复制代码
$arr = array(1, 3, 4);
//顺序查找
function  test($arr, $num)
{
  foreach ($arr as $key => $value) {
    if ($value == $num) {
      return $key;
    }
  }
  return false;
}
var_dump(test($arr, 3));

4.3 二分查找算法

二分查找要求线性表中的结点按照关键字升序或降序排列,用给定值k与中间结点的关键字相比较,总监结点吧线性表分成两个子表,若相等则查找成功,若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,这样递归进行,直到查询到查找结束发现表中没有这样的结点。

代码实现

php 复制代码
//二分查找算法
//1.得到数组边界
$arr = [1, 3, 6, 8, 23, 68, 100];
$res  = 68;
function test2($arr, $res)
{
  $right = count($arr) - 1;
  $left = 0;

  //2.循环匹配
  while ($left <= $right) {
    //3.得到中间位置
    $middle = floor(($right + $left) / 2);
    //4.匹配数据
    if ($arr[$middle] == $res) {
      return $middle;
    }
    //5.没有找到
    if ($arr[$middle] < $res) {
      //值在右边
      $left = $middle + 1;
    } else {
      //值在左边
      $right = $middle - 1;
    }
  }
  return false;
}
var_dump(test2($arr, $res));
相关推荐
海域云赵从友2 小时前
破解跨境数据传输瓶颈:中国德国高速跨境组网专线与本地化 IP 的协同策略
开发语言·php
pilgrim532 小时前
结合 Leetcode 题探究KMP算法
算法·leetcode
罗义凯2 小时前
其中包含了三种排序算法的注释版本(冒泡排序、选择排序、插入排序),但当前只实现了数组的输入和输出功能。
数据结构·c++·算法
kevien_G13 小时前
JAVA之二叉树
数据结构·算法
syt_biancheng3 小时前
Day3算法训练(简写单词,dd爱框框,3-除2!)
开发语言·c++·算法·贪心算法
二进制的Liao4 小时前
【编程】脚本编写入门:从零到一的自动化之旅
数据库·python·算法·自动化·bash
自然数e4 小时前
C++多线程【线程管控】之线程转移以及线程数量和ID
开发语言·c++·算法·多线程
云在Steven5 小时前
在线确定性算法与自适应启发式在虚拟机动态整合中的竞争分析与性能优化
人工智能·算法·性能优化
前进的李工5 小时前
LeetCode hot100:234 回文链表:快慢指针巧判回文链表
python·算法·leetcode·链表·快慢指针·回文链表