十大基础算法
参考文档:菜鸟教程
文章图片引用的是菜鸟教程的图片
下面是十大基本算法的一个比较如下图:
冒泡排序法
冒泡排序法是一种直观的排序算法,它主要的本质就是不断地比较数组相邻的两个元素,交换元素的位置,从而达到排序的效果,整个过程中,小的元素会慢慢的浮
到数组的顶端,这也是 冒泡排序
的由来
话不多说,直接上动图:
经典案例
小k很喜欢到小饰品店里买东西,他想花100块,买最多的不同种类的商品,于是她找她的好朋友小M帮忙
public function sort($array)
{
if (count($array) < 2) {
return $array;
}
// 循环到数组的倒数第二个数据排序完的时候,最后一个数据也就已经排序好了
for ($i = 0; $i < count($array) - 1; $i++) {
for ($j = 0; $j < count($array) - $i - 1; $j++) {
if ($array[$j] > $array[$j + 1]) {
$temp = $array[$j];
$array[$j] = $array[$j + 1];
$array[$j + 1] = $temp;
}
}
}
return $array;
}
选择排序
选择排序法也是一种比较直观简单的算法, 它也是一个比较有趣的算法,它就是再数组中找到最小的元素放在数组的顶端,然后再剩余的数据中再找到最小的数据,放到已经排序了元素的后面,直到数组排序完成
选择排序动图
案例
对数组 [3,1,4,2,7] 使用选择排序排序
public function selectionSort($array)
{
if (count($array) < 2) {
return $array;
}
for ($i = 0; $i < count($array) - 1; $i++) {
$index = $i;
for ($j = $i + 1; $j < count($array); $j++) {
if ($array[$index] > $array[$j]) {
$index = $j;
}
}
$temp = $array[$i];
$array[$i] = $array[$index];
$array[$index] = $temp;
}
return $array;
}
插入排序法
插入排序法的原理是最容易理解的,它的原理就和打扑克牌一样,取未排序的数据向已排序的数据扫描,找到位置并插入。
直接上动图,一看就懂了
案例
public function insertSort($array)
{
if (count($array) < 2) {
return $array;
}
for ($i = 1; $i < count($array); $i++) {
$temp = $array[$i];
$preIndex = $i - 1;
while ($preIndex >= 0 && $temp < $array[$preIndex]) {
$array[$preIndex + 1] = $array[$preIndex];
$preIndex--;
}
$array[$preIndex + 1] = $temp;
}
return $array;
}
希尔排序法
希尔排序,也称递减增量排序算法,希尔排序的本质是将相隔某个“增量”的记录组成一个字序列,实现跳跃式的移动,使得排序的效率提高。个人觉得相当于优化的选择排序
代码
public function shellSort($array)
{
$len = count($array);
if ($len < 2) {
return $array;
}
$temp = null;
$gap = 1;
while ($gap < $len / 3) {
$gap = $gap * 3 + 1;
}
for ($gap; $gap > 0; $gap = floor($gap / 3)) {
for ($i = $gap; $i < $len; $i++) {
$temp = $array[$i];
for ($j = $i - $gap; $j >= 0 && $array[$j] > $temp; $j -= $gap) {
$array[$j + $gap] = $array[$j];
}
$array[$j + $gap] = $temp;
}
}
return $array;
}
归并排序法
归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。其本质就是将数组不断地颗粒化,直到达到最小颗粒1,当最小颗粒的排序完成后,再由里到外的完成整个数组的排序。主要运用了递归
。
原理动态图
案例
public function mergeSort($array)
{
$len = count($array);
if ($len < 2) {
return $array;
}
$middle = $len / 2;
$leftArray = array_slice($array, 0, $middle);
$rightArray = array_slice($array, $middle);
return $this->merge($this->mergeSort($leftArray), $this->mergeSort($rightArray));
}
private function merge($leftArray, $rightArray)
{
$result = [];
while (count($leftArray) > 0 && count($rightArray) > 0) {
if ($leftArray[0] <= $rightArray[0]) {
$result[] = array_shift($leftArray);
} else {
$result[] = array_shift($rightArray);
}
}
while (count($rightArray)) {
$result[] = array_shift($rightArray);
}
while (count($leftArray)) {
$result[] = array_shift($leftArray);
}
return $result;
}
快速排序法
快速排序是由东尼·霍尔所发展的一种排序算法。它的本质相当于优化了的归并排序法,也主要使用了递归,它也是将数组不断颗粒化(通过与自己定义的指标比较), 而它与归并的区别在于,它在不断颗粒化的过程中,相对于归并是有序的。
上原理动图
案例
public function quicklySort($array)
{
if (count($array) < 2) {
return $array;
}
$middle = $array[0];
$leftArray = [];
$rightArray = [];
for ($i = 1; $i < count($array); $i++) {
if ($array[$i] < $middle) {
$leftArray[] = $array[$i];
} else {
$rightArray[] = $array[$i];
}
}
$leftArray = $this->quicklySort($leftArray);
$leftArray[] = $middle;
$rightArray = $this->quicklySort($rightArray);
return array_merge($leftArray, $rightArray);
}
堆排序法
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:
- 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
- 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
堆排序的平均时间复杂度为 Ο(nlogn)。
堆排序的本质就是利用二ca
原理动图
案例
function buildMaxHeap(&$arr)
{
global $len;
for ($i = floor($len/2); $i >= 0; $i--) {
heapify($arr, $i);
}
}
function heapify(&$arr, $i)
{
global $len;
$left = 2 * $i + 1;
$right = 2 * $i + 2;
$largest = $i;
if ($left < $len && $arr[$left] > $arr[$largest]) {
$largest = $left;
}
if ($right < $len && $arr[$right] > $arr[$largest]) {
$largest = $right;
}
if ($largest != $i) {
swap($arr, $i, $largest);
heapify($arr, $largest);
}
}
function swap(&$arr, $i, $j)
{
$temp = $arr[$i];
$arr[$i] = $arr[$j];
$arr[$j] = $temp;
}
function heapSort($arr) {
global $len;
$len = count($arr);
buildMaxHeap($arr);
for ($i = count($arr) - 1; $i > 0; $i--) {
swap($arr, 0, $i);
$len--;
heapify($arr, 0);
}
return $arr;
}
计数排序法
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
这种算法的本质就是将原数组的值作为新数组(新数组的长度为max(旧数组) + 1
)的 key,元素重复的个数作为新数组的 value, 这样就通过新数组的 key 完成了排序,然后再取出 value 为 null 的元素,这样就达到了排序。这种方法有个天然的弊端,就是耗内存
原理动图
案例
public function countSort($array)
{
if (count($array) < 2) {
return $array;
}
$len = max($array);
$newArray = [];
for ($i = 0; $i < $len; $i++) {
$newArray[] = null;
}
for ($j = 0; $j < count($array); $j++) {
if (!array_key_exists($array[$j], $newArray)) {
$newArray[$array[$j]] = 0;
}
$newArray[$array[$j]]++;
}
$sortedIndex = 0;
foreach ($newArray as $key => $val) {
if ($val !== null) {
for ($j = 0; $j < $val; $j++) {
$array[$sortedIndex++] = $key;
}
}
}
return $array;
}
桶排序法
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。它需要自己自定义一个桶的大小,然后根据数组的最大值和最小值来算出桶的个数,然后确定每个元素再哪个一个桶,对每个桶排序,再将桶中的元素放入数组中,排序就完成了
1. 什么时候最快
当输入的数据可以均匀的分配到每一个桶中。
2. 什么时候最慢
当输入的数据被分配到了同一个桶中。
原理动图
元素分布在桶中:
然后,元素在每个桶中排序:
案例
public function bucketSort($array, $bucketSize)
{
if (count($array) < 2) {
return $array;
}
$minValue = min($array);
$maxValue = max($array);
$bucketCount = floor(($maxValue - $minValue) / $bucketSize) + 1;
$buckets = array();
for ($i = 0; $i < $bucketCount; $i++) {
$buckets[$i] = [];
}
for ($i = 0; $i < count($array); $i++) {
$buckets[floor(($array[$i] - $minValue) / $bucketSize)][] = $array[$i];
}
$arr = array();
for ($i = 0; $i < count($buckets); $i++) {
$bucketTmp = $buckets[$i];
if (empty($bucketTmp)) {
continue;
}
sort($bucketTmp);
for ($j = 0; $j < count($bucketTmp); $j++) {
$arr[] = $bucketTmp[$j];
}
}
return $arr;
}
基数排序法
基数排序是一种非比较型整数排序算法,其原理就是我们常用的基数0~9,我们以这些基数创建对应的数组,例如[0 => [], 1 => []… 9=>[]], 然后找出数组的最大值,获取最大值的位数,也是整个大循环的次数,从个位开始,各位是哪个基数,就放到哪个基数的桶里面,整个大循环完成后,再遍历将数剧依次放回原数组。
2. LSD 基数排序动图演示
案例
function baseSort($arr)
{
if (count($arr) < 2) {
return $arr;
}
$baseBickets = [];
for ($i = 0; $i < 10; $i++) {
$baseBickets[$i] = [];
}
for ($i = 0; $i < strlen((string) max($arr)); $i++) {
for ($j = 0; $j < count($arr); $j++) {
preg_match_all('/\d/', $arr[$j], $matches);
$numArr = $matches[0];
$len = count($numArr);
$baseBicketIndex = array_key_exists($len - $i -1, $numArr) ? $numArr[$len - $i -1] : 0;
if (array_key_exists($baseBicketIndex, $baseBickets)) {
$baseBickets[$baseBicketIndex][] = $arr[$j];
}
}
$arr = [];
for ($j = 0; $j < count($baseBickets); $j++) {
if (!empty($baseBickets[$j])) {
while (($val = array_shift($baseBickets[$j])) !== null) {
array_push($arr, $val);
}
}
}
}
return $arr;
}