编程思想
编程思想:如何利用数学模式,来解决对应的需求问题,然后利用代码实现对应的数据模型(逻辑)
算法:使用代码实现对应的数学模型,从而解决对应的业务问题
一.递推算法
递推算法是一种简单的算法:通过已知条件,利用特定关系得出中间理论,直到得到结果的算法,递推算法分为顺推和逆推两种
顺推:
通过最简单的条件(已知),然后逐步推演结果
逆推:
通过结果找到规律,然后推到已知条件
斐波那契数列: 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 个元素开始,每个轮到它的时候,就准备把它插到左边去)
插入排序的算法思路:
- 从数组第二个元素开始处理(因为第一个元素天然有序)
- 每次拿出这个元素(key)
- 从前一位开始向左扫描
- 只要发现比 key 大的,就把它们统统往右挪
- 直到找到不比 key 大的位置
- 在这个位置把 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));