常用算法
一、冒泡算法
冒泡排序(Bubble sort)是一种简单的排序算法,它重复的走访过要排序的数列,依次比较两个元素,如果他们的顺序错误就把他们交换过来,走访数列的的工作是重复的进行直到没有再需要的交换,也就是说该数列已经完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
步骤:
- 比较相邻的元素,如果第一个比第二个大,就交换他们两个
- 对每一对相邻元素做相同的操作,从开始第一对到最后一对,再这一点,最后的元素应该是最大的数
- 针对所有的元素重复以上的步骤,除了最后一个
- 持续每次对越来越少的元素重复以上的步骤,直到没有任何一对数字需要比较
//冒泡排序 $arr = [4,6,3,8,1,10,5]; function mysort($arr){ $count = count($arr); for($i=0; $i<$count; $i++){ for($j=0; $j<$count-$i-1; $j++){ if($arr[$j]>$arr[$j+1]){ $tmp = $arr[$j+1]; $arr[$j+1] = $arr[$j]; $arr[$j] = $tmp; } } } return $arr; } print_r(mysort($arr));
2.快速排序
在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。
步骤:
- 从数列中挑出一个元素,成为“基准”(pivot)
- 重新排列数列,所有元素比基准值小的放在基准前面,所有元素比基准值大的放在基准后面(相同的数可以到任一边),在这个分区退出后,该基准就处于数列的中间位置,这成为分区(partition)操作
- 递归的(recursive)把小于基准值的子数列和大于基准值元素的子数列排序
//快速排序 $arr = [1,5,3,2,10,7,4]; function quick_sort($arr){ $count = count($arr); if($count<=1){ return $arr; } $left = $right = []; for($i=1; $i<$count; $i++){ if($arr[$i] < $arr[0]){ $left[]=$arr[$i]; }else{ $right[]=$arr[$i]; } } //递归调用 $left=quick_sort($left); $right=quick_sort($right); return array_merge($left, array($arr[0]),$right); } print_r(quick_sort($arr));
3.二叉树查找
假如数据必须是按照升序排序的,对于给定值x,从序列的中间值开始比较,如果当前位置等于x则查找成功;若x小于当前位置值则在数列的前半段查找;若x大于当前位置值则再数列的后半段查找,直到找到为止。(数据量大的时候使用)
//二分查找 $arr = [1,2,3,4,5,6,7,8,9,10]; function bin_search($arr, $low, $high, $k){ if($low<=$high){ $mid = intval(($low+$high)/2); if($arr[$mid]==$k){ return $mid; }elseif( $k<$arr[$mid] ){ return bin_search($arr, $low, $mid-1, $k ); }else{ return bin_search($arr, $mid+1, $high, $k); } } return -1; } echo bin_search($arr, 0, count($arr), 8);
4.选择排序
工作原理:首先再未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从未排序序列中继续寻找最小元素存放到排序序列末尾,以此类推,直到所有元素都排序完毕
//选择排序 $arr = [1,3,20,9,5,4,8]; function selectSort($arr){ $len = count($arr); for($i=0; $i<$len-1; $i++){ $p = $i; for($j=$i+1; $j<$len; $j++){ $p = $arr[$p]<=$arr[$j] ? $p : $j; } if($p!=$i){ $tmp = $arr[$p]; $arr[$p] = $arr[$i]; $arr[$i] = $tmp; } } return $arr; } print_r(selectSort($arr));
4.雪花算法
SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法
其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛,且ID 引入了时间戳,保持自增性且不重复。
雪花算法的结构:
主要分为5个部分:
- 是 1 个 bit:0,这个是无意义的。
- 是 41 个 bit:表示的是时间戳。
- 是 10 个 bit:表示的是机房 id,0000000000,因为我传进去的就是0。
- 是 12 个 bit:表示的序号,就是某个机房某台机器上这一毫秒内同时生成的 id 的序号,0000 0000 0000。
这四个部分的解释:
1 bit,是无意义的:
- 因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。
41 bit:表示的是时间戳,单位是毫秒。
- 41 bit 可以表示的数字多达 2^41 - 1,也就是可以标识 2 ^ 41 - 1 个毫秒值,换算成年就是表示 69 年的时间。
10 bit:记录工作机器 id,代表的是这个服务最多可以部署在 2^10 台机器上,也就是 1024 台机器。
- 但是 10 bit 里 5 个 bit 代表机房 id,5 个 bit 代表机器 id。意思就是最多代表 2 ^ 5 个机房(32 个机房),每个机房里可以代表 2 ^ 5 个机器(32 台机器),这里可以随意拆分,比如拿出4位标识业务号,其他6位作为机器号。可以随意组合。
12 bit:这个是用来记录同一个毫秒内产生的不同 id。
- 12 bit 可以代表的最大正整数是 2 ^ 12 - 1 = 4096,也就是说可以用这个 12 bit 代表的数字来区分同一个毫秒内的 4096 个不同的 id。也就是同一毫秒内同一台机器所生成的最大ID数量为4096
简单来说,你的某个服务假设要生成一个全局唯一 id,那么就可以发送一个请求给部署了 SnowFlake 算法的系统,由这个 SnowFlake 算法系统来生成唯一 id。这个 SnowFlake 算法系统首先肯定是知道自己所在的机器号,(这里姑且讲10bit全部作为工作机器ID)接着 SnowFlake 算法系统接收到这个请求之后,首先就会用二进制位运算的方式生成一个 64 bit 的 long 型 id,64 个 bit 中的第一个 bit 是无意义的。接着用当前时间戳(单位到毫秒)占用41 个 bit,然后接着 10 个 bit 设置机器 id。最后再判断一下,当前这台机房的这台机器上这一毫秒内,这是第几个请求,给这次生成 id 的请求累加一个序号,作为最后的 12 个 bit。
SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。但是依赖与系统时间的一致性,如果系统时间被回调,或者改变,可能会造成id冲突或者重复。实际中我们的机房并没有那么多,我们可以改进改算法,将10bit的机器id优化,成业务表或者和我们系统相关的业务。
其实对于分布式ID的生成策略。无论是我们上述提到的哪一种。无非需要具有以下两种特点。 自增的、不重复的。而对于不重复且是自增的,那么我们是很容易想到的是时间,而雪花算法就是基于时间戳。但是毫秒级的并发下如果直接拿来用,显然是不合理的。那么我们就要在这个时间戳上面做一些文章。至于怎么能让这个东西保持唯一且自增。就要打开自己的脑洞了。可以看到雪花算法中是基于 synchronized 锁进行实现的。
PHP实现雪花算法:https://www.cnblogs.com/lz0925/p/11288058.html
更多排序算法:
https://www.cnblogs.com/mzhaox/p/11218297.html
https://www.cnblogs.com/xiaobingch/p/12464045.html