【基础算法】基础算法【转载】

1. 最大公约数

问题:求两个自然数的最大公约数。

分析:这个是基础的数学问题,最大公约数指两个数字公共的约数中最大的,例如数字6的约数有1、2、3、6,数字9的约数有1、3、9,则数字6和数字9的公共约数有1和3,其中3是最大的公约数。

第一种思路:从1开始循环,每次把符合要求(即同时是两个数字的约数)的值都存储起来,那么最后一个存储起来的就是最大的约数。

E:\博客-基础算法-代码\1_a_max_divisor.php:

[php] view plain copy

  1. <?php
  2. /**
  3. * 求两个自然数的最大公约数(暴力枚举法/穷举法)
  4. * @param int $a
  5. * @param int $b
  6. * @return bool|int
  7. */
  8. function get_max_divisor($a, $b)
  9. {
  10. if (!is_numeric($a) || !is_numeric($b)) {
  11. return false;
  12. }
  13. $min = $a > $b ? $b : $a;
  14. $max = $a > $b ? $a : $b;
  15. if ($max % $min === 0) {
  16. return $min;
  17. }
  18. $result = 1;
  19. for ($i = 1; $i <= $min / sqrt(2); $i++) {
  20. if ($min % $i === 0 && $max % $i === 0) {
  21. $result = $i;
  22. }
  23. }
  24. return $result;
  25. }
  26. $a = 36;
  27. $b = 16;
  28. $c = get_max_divisor($a, $b);
  29. var_dump($c);

使用该思路,每次都存储得到的公共约数,那么最后一个存储的就是两个数字的最大公约数。

第二种思路:从两个数字中最小的数字开始循环,每次减1,那么第一次得到的公共约数就是所求的最大公约数。

则实现的代码如下:

E:\博客-基础算法-代码\1_b_max_divisor.php:

[php] view plain copy

  1. <?php
  2. /**
  3. * 求两个自然数的最大公约数(暴力枚举法/穷举法——优化)
  4. * 时间复杂度是O(min(a, b)))
  5. * @param int $a
  6. * @param int $b
  7. * @return bool|int
  8. */
  9. function get_max_divisor($a, $b)
  10. {
  11. if (!is_numeric($a) || !is_numeric($b)) {
  12. return false;
  13. }
  14. $min = $a > $b ? $b : $a;
  15. $result = 1;
  16. for ($i = $min; $i >= 1; $i--) {
  17. if ($a % $i === 0 && $b % $i === 0) {
  18. $result = $i;
  19. break;
  20. }
  21. }
  22. return $result;
  23. }
  24. $a = 55;
  25. $b = 10;
  26. $c = get_max_divisor($a, $b);
  27. var_dump($c);

两者相比之下,第二种的时间复杂度要低,因为一旦找到了之后就会跳出循环了,而第一种则需要完整的遍历一遍循环。

但是,两种方法都是采用了暴力穷举法,时间复杂度都是比较高的。可以采用其他的方法。时间复杂度是O(min(a, b)))

第三种思路:辗转相除法/欧几里德算法

辗转相除法, 又名欧几里德算法(Euclidean algorithm)乃求两个正整数之最大公因子的算法。它是已知最古老的算法, 其可追溯至公元前300年前。

原理:两个整数的最大公约数是能够同时整除它们的最大的正整数。

辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数的相除余数的最大公约数。

设两数为a、b(a>b),求a和b最大公约数(a,b)的步骤如下:用a除以b,得
a÷b=q......r1(0≤r1)。若r1=0,则(a,b)=b;若r1≠0,则再用b除以r1,得b÷r1=q......r2
(0≤r2).若r2=0,则(a,b)=r1,若r2≠0,则继续用r1除以r2,……如此下去,直到能整除为止。其最后一个为被除数的余数的除数即为
(a, b)。

例如:a=25,b=15,a/b=1......10,b/10=1......5,10/5=2.......0,最后一个为被除数余数的除数就是5,5就是所求最大公约数。

E:\博客-基础算法-代码\1_c_max_divisor.php:

[php] view plain copy

  1. <?php
  2. /**
  3. * 求两个自然数的最大公约数(辗转相除法/欧几里德算法)
  4. * 时间复杂度不太好计算,可以近似为O(log(min(a, b))),但是取模运算性能较差。
  5. * @param int $a
  6. * @param int $b
  7. * @return bool|int
  8. */
  9. $a = 550;
  10. $b = 700;
  11. $c = gcd($a, $b);
  12. var_dump($c);
  13. /**
  14. * 递归计算最大的公约数
  15. * @param int $a
  16. * @param int $b
  17. * @return mixed
  18. */
  19. function gcd($a, $b)
  20. {
  21. if (!is_numeric($a) || !is_numeric($b)) {
  22. return false;
  23. }
  24. $min = $a > $b ? $b : $a;
  25. $max = $a > $b ? $a : $b;
  26. if ($max % $min === 0) {
  27. return $min;
  28. } else {
  29. return gcd($min, $max % $min);
  30. }
  31. }

辗转相除法相比起穷举法效率快很多,不用遍历每个数字。

时间复杂度不太好计算,可以近似为O(log(min(a, b))),但是取模运算性能较差。

第四种思路:另一种方法是采用中国国人发明的更相减损法,更相减损术是出自《九章算术》的一种求最大公约数的算法,它原本是
为约分而设计的,但它适用于任何需要求最大公约数的场合。(如果需要对分数进行约分,那么)可以折半的话,就折半(也就是用2来约分)。如果不可以折半的
话,那么就比较分母和分子的大小,用大数减去小数,互相减来减去,一直到减数与差相等为止,用这个相等的数字来约分。

步骤:

第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。

第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。

则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。

其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。

比如:用更相减损术求260和104的最大公约数。由于260和104均为偶数,首先用2约简得到130和52,再用2约简得到65和26。

此时65是奇数而26不是奇数,故把65和26辗转相减:

65-26=39

39-26=13

26-13=13

所以,260与104的最大公约数等于13乘以第一步中约掉的两个2,即13*2*2=52。

E:\博客-基础算法-代码\1_d_max_divisor.php:

[php] view plain copy

  1. <?php
  2. /**
  3. * 求两个自然数的最大公约数(更相减损法)
  4. * 避免了取模运算,但是算法性能不稳定,最坏时间复杂度为O(max(a, b)))
  5. * @param int $a
  6. * @param int $b
  7. * @return bool|int
  8. */
  9. $a = 44;
  10. $b = 88;
  11. $c = gcd($a, $b);
  12. var_dump($c);
  13. /**
  14. * 递归计算最大的公约数
  15. * @param int $a
  16. * @param int $b
  17. * @return mixed
  18. */
  19. function gcd($a, $b)
  20. {
  21. if (!is_numeric($a) || !is_numeric($b)) {
  22. return false;
  23. }
  24. if ($a === $b) {
  25. return $a;
  26. }
  27. $min = $a > $b ? $b : $a;
  28. $max = $a > $b ? $a : $b;
  29. return gcd($max - $min, $min);
  30. }

更相减损术和辗转相除法的主要区别在于前者所使用的运算是“减”,后者是“除”。从算法思想上看,两者
并没有本质上的区别,但是在计算过程中,如果遇到一个数很大,另一个数比较小的情况,可能要进行很多次减法才能达到一次除法的效果,从而使得算法的时间复
杂度退化为O(N),其中N是原先的两个数中较大的一个。相比之下,辗转相除法的时间复杂度稳定于O(logN)。但是缺点也很明显,如果两个数相差很
大,比如10000和59,做$a % $b取模运算的性能会比较低。

第五种思路:结合辗转相除法和更相减损法,在更相减损法的基础上使用移位运算

[php] view plain copy

  1. <?php
  2. /**
  3. * 求两个自然数的最大公约数(结合辗转相除法和更相减损法,在更相减损法的基础上使用移位运算)
  4. * 不但避免了取模运算,而且算法性能稳定,时间复杂度是a和b中较大数的二进制位数,即O(log(max(a, b)))
  5. * @param int $a
  6. * @param int $b
  7. * @return bool|int
  8. */
  9. $a = 50;
  10. $b = 88;
  11. $c = gcd($a, $b);
  12. var_dump($c);
  13. /**
  14. * 递归计算最大的公约数
  15. * @param int $a
  16. * @param int $b
  17. * @return mixed
  18. */
  19. function gcd($a, $b)
  20. {
  21. if (!is_numeric($a) || !is_numeric($b)) {
  22. return false;
  23. }
  24. if ($a == $b) {
  25. return $a;
  26. }
  27. //保证参数$a永远大于等于参数$b,为减少代码量
  28. if ($a < $b) {
  29. return gcd($b, $a);
  30. } else {
  31. //和1做按位与运算,判断奇偶
  32. if (is_even($a) && is_even($b)) {
  33. return (gcd($a >> 1, $b >> 1) << 1);
  34. } elseif (is_even($a) && !is_even($b)) {
  35. return gcd($a >> 1, $b);
  36. } elseif (!is_even($a) && is_even($b)) {
  37. return gcd($a, $b >> 1);
  38. } else {
  39. return gcd($b, $a - $b);
  40. }
  41. }
  42. }
  43. /**
  44. * 判断奇偶数,减少迭代次数,奇数为false,偶数为true
  45. * @param int $a
  46. * @return bool
  47. */
  48. function is_even($a) {
  49. return !($a & 1);
  50. }

众所周知,移位运算的性能非常快。对于给定的正整数a和b,不难得到如下的结论。其中gcb(a,b)的意思是a,b的最大公约数函数:

当a和b均为偶数,gcb(a,b) = 2*gcb(a/2, b/2) = 2*gcb(a>>1, b>>1)

当a为偶数,b为奇数,gcb(a,b) = gcb(a/2, b) = gcb(a>>1, b)

当a为奇数,b为偶数,gcb(a,b) = gcb(a, b/2) = gcb(a, b>>1)

当a和b均为奇数,利用更相减损术运算一次,gcb(a,b) = gcb(b, a-b), 此时a-b必然是偶数,又可以继续进行移位运算。

比如计算10和25的最大公约数的步骤如下:

整数10通过移位,可以转换成求5和25的最大公约数

利用更相减损法,计算出25-5=20,转换成求5和20的最大公约数

整数20通过移位,可以转换成求5和10的最大公约数

整数10通过移位,可以转换成求5和5的最大公约数

利用更相减损法,因为两数相等,所以最大公约数是5

在两数比较小的时候,暂时看不出计算次数的优势,当两数越大,计算次数的节省就越明显。

这种方法:不但避免了取模运算,而且算法性能稳定,时间复杂度为O(log(max(a, b)))

2. 百元百鸡问题

问题:每只母鸡3元,每只公鸡4元,每只小鸡0.5元,如果花100元钱买100只鸡,请问有哪些可能?说明:每种鸡的数量都可以为零。

分析:其实这个问题是数学上的组合问题,只需要把所有的情况列举出来,然后来判断是否符合要求即可。这样的重复列举的问题,在程序上可以使用循环进行解决。

第一种思路:当母鸡的数量为0时,公鸡的数量从0-100,当公鸡的数量每变化一次,小鸡的数量就从0变化到100,使用如下数值组合来描述这个思路:

母鸡数量                                   公鸡数量                                 小鸡数量

0                                               0                                   从0变化到100

0                                               1                                   从0变化到100

0                                               2                                   从0变化到100

……

1                                               0                                   从0变化到100

1                                               1                                   从0变化到100

……

100                                          100                                         100

上面列举出了所有公鸡、母鸡和小鸡的数量都是0-100时的所有组合,总计是101的三次方种,这样的穷举结构直接存在嵌套,在程序实际实现时,通过循环之间的嵌套就可以实现,则实现的代码如下:

E:\博客-基础算法-代码\2_a_hundred_hen.php:

  • <?php
  • for ($i = 0; $i <= 100; $i++) {
  • for ($j = 0; $j <= 100; $j++) {
  • for ($k = 0; $k <= 100; $k++) {
  • if (3 * $i + 4 * $j + 0.5 * $k == 100 && $i + $j + $k == 100) {
  • echo $i . ‘<br>‘ . $j . ‘<br>‘ . $k;
  • echo ‘<hr>‘;
  • }
  • }
  • }
  • }

输出:

按照for语句的执行流程,循环变量变化1,则内部的循环执行一次,而在循环嵌套时,循环体又是一个新的循环,则该循环执行完成的一组循环。这里通过循环的嵌套实现了所有数值的穷举。在循环的内部,只需要按照题目要求判断一下数量和金额是否符合要求即可。

但是这样的代码效率比较差,可以通过简单的优化来提高程序的执行效率。

第二种思路:由于母鸡每只的金额是3元,所以100元最多购买的母鸡数量是100/3=33只,同理100元最多购买的公鸡数量是25只,而按照100元100只的要求,小鸡的数量应该为100减去公鸡和母鸡的数量,这样代码就可以简化为如下的结构:

E:\博客-基础算法-代码\2_b_hundred_hen.php:

  • <?php
  • for ($i = 0; $i <= 33; $i++) {
  • for ($j = 0; $j <= 25; $j++) {
  • $k = 100 - $i - $j;
  • if (3 * $i + 4 * $j + 0.5 * $k == 100) {
  • echo $i . ‘<br>‘ . $j . ‘<br>‘ . $k;
  • echo ‘<hr>‘;
  • }
  • }
  • }

输出也是一样,但是时间复杂度比第一种好得多。

3. 喝汽水问题

问题:共有1000瓶汽水,每喝完后一瓶得到的一个空瓶子,每3个空瓶子又能换1瓶汽水,喝掉以后又得到一个空瓶子,问总共能喝多少瓶汽水,最后还剩余多少个空瓶子?

分析:这个问题其实是个比较典型的递推问题,每3个空瓶都可以再换1瓶新的汽水,这样一直递推下去,直到最后不能换到汽水为止。

第一种思路:每次喝一瓶,每有三个空瓶子就去换一瓶新的汽水,直到最后没有汽水可以喝为止。在程序中记忆汽水的数量和空瓶子的数量即可。

则实现的代码如下:

E:\博客-基础算法-代码\3_a_drink_bottle.php:

  • <?php
  • $num = 1000; //汽水数量
  • $drink_num = 0; //喝掉的汽水数量
  • $empty_num = 0; //空瓶子的数量
  • while ($num > 0) { //有汽水可以喝
  • $num--; //喝掉一瓶
  • $empty_num++; //空瓶子数量加1
  • $drink_num++; //喝掉的汽水数量加1
  • if ($empty_num === 3) { //有3个空瓶子,就去换一瓶汽水
  • $num++; //汽水数量加1
  • $empty_num = 0; //空瓶子数量清0
  • }
  • }
  • echo "total drink: " . $drink_num;
  • echo "left empty bottle: " . $empty_num;

输出:

total drink: 1499left empty bottle: 2

在该代码中,每次循环喝掉一瓶汽水,则汽水数量减少1,空瓶子数增加1,喝掉的总汽水瓶数增加1,每次判断空瓶子的数量是否达到3,如果达到3则换1瓶汽水,同时空瓶子的数量变为零。这种思路比较直观,但是循环的次数比较多,所以就有了下面的逻辑实现。

第二种思路:一次把所有的汽水喝完,获得所有的空瓶子,再全部换成汽水,然后再一次全部喝完,再获得所有的空瓶子,依次类推,直到没有汽水可喝为止。

则实现的代码如下:

E:\博客-基础算法-代码\3_b_drink_bottle.php:

  • <?php
  • $num = 1000; //汽水数量
  • $drink_num = 0; //喝掉的汽水数量
  • $empty_num = 0; //空瓶子的数量
  • while ($num > 0) { //有汽水可以喝
  • $drink_num += $num; //喝掉所有的汽水
  • $empty_num += $num; //空瓶子数量等于上次剩余的加上这次喝掉的数量
  • $num = intval(floor($empty_num / 3)); //兑换的汽水数量
  • $empty_num -= $num * 3; //本次兑换剩余的空瓶子数量
  • }
  • echo "total drink: " . $drink_num;
  • echo "left empty bottle: " . $empty_num;

在该代码中,每次喝掉所有的汽水,也就是num瓶,则喝掉的总瓶数每次增加num,因为每次都可能剩 余空瓶子(不足3个的),则总的空瓶子数量是上次空瓶子数量加上本次喝掉的num瓶。接着是兑换汽水,则每次可以兑换的汽水数量是空瓶子的数量的1/3, 注意这里是整数除法,而本次兑换剩余的空瓶子数量是原来的空瓶子数量减去兑换得到汽水数量的3倍,这就是一次循环所完成的功能,依次类推即可解决该问题。

第三种思路:3个空瓶子=1瓶饮料=>3个空瓶子=1瓶饮料(不带瓶)+1个空瓶子=>2个空瓶子=1瓶饮料
(不带瓶),那么这样1000个空瓶可以兑换500瓶饮料(不带瓶),但是最后1瓶饮料你是喝不到的,因为你最后剩下2个空瓶,喝的饮料
数=1000+500-1。

E:\博客-基础算法-代码\3_c_drink_bottle.php:

  • <?php
  • $num = 1000; //汽水数量
  • $drink_num = 0; //喝掉的汽水数量
  • $empty_num = 2; //空瓶子的数量
  • $drink_num = $num + $num / 2 - 1;
  • echo "total drink: " . $drink_num;
  • echo "left empty bottle: " . $empty_num;

4. 水仙花数

问题:水仙花数指三位数中,每个数字的立方和和自身相等的数字,例如370,3 × 3 × 3 + 7 × 7 × 7 + 0 × 0 × 0 =370,请输出所有的水仙花数。

分析:该问题中体现了一个基本的算法——数字拆分,需要把一个数中每位的数字拆分出来,然后才可以实现该逻辑。

实现思路:循环所有的三位数,拆分出三位数字的个位、十位和百位数字,判断3个数字的立方和是否等于自身。

则实现的代码如下所示:

E:\博客-基础算法-代码\4_narcissus_number.php:

  • <?php
  • for ($i = 100; $i < 1000; $i++) {
  • $a = $i % 10; //个位数字
  • $b = intval(floor($i / 10)) % 10; //十位数字
  • $c = intval(floor($i / 100)); //百位数字
  • if ($a * $a * $a + $b * $b * $b + $c * $c * $c === $i) {
  • echo ‘<br>‘ . $i . ‘<br>‘;
  • }
  • }

在该代码中,拆分个位数字使用i和10取余即可,拆分十位数字时首先用i除以十,去掉个位数字,并使原 来的十位数字变成个位,然后和10取余即可,因为i是一个三位数,所以i除以100即可得百位数字,因为这里都是整数除法,不存在小数的问题。然后只需要 判断立方和是否等于自身即可。

注意:因为i是循环变量,这里不能改变i的值,不然可能造成死循环。

5. 求素数问题

问题:求出1000以内的所有素数,素数即质数,只能被1和本身整除的数,最小的质数是2。

实现思路:通过嵌套循环找出2到1000内所有的符合条件的数。

则实现的代码如下所示:

E:\博客-基础算法-代码\5_a_prime_number.php:

  • <?php
  • for ($i = 2; $i <= 997; $i++) {
  • $is_prime = true;
  • for ($j = 2; $j < $i; $j++) {
  • if ($i !== $j && $i % $j === 0) {
  • $is_prime = false;
  • break;
  • }
  • }
  • if ($is_prime === true) {
  • echo ‘<br>‘ . $i . ‘<br>‘;
  • }
  • }

这样算是想到的最直接的方式,当然也是最笨的方式,因为每次判断的时候都会从2检查到i,聪明一点的方式是把i变成i/2,因为2/i以上的数肯定不会被i整除。

则实现的代码如下所示:

E:\博客-基础算法-代码\5_b_prime_number.php:

  • <?php
  • for ($i = 2; $i <= 1000; $i++) {
  • $is_prime = true;
  • for ($j = 2; $j <= $i / 2; $j++) {
  • if ($i !== $j && $i % $j === 0) {
  • $is_prime = false;
  • break;
  • }
  • }
  • if ($is_prime === true) {
  • echo ‘<br>‘ . $i . ‘<br>‘;
  • }
  • }

那么再聪明的方式就是把i/2变成根号i,因为根号i以上的数肯定不会被i整除。

则实现的代码如下所示:

E:\博客-基础算法-代码\5_c_prime_number.php:

  • <?php
  • for ($i = 2; $i <= 15; $i++) {
  • $is_prime = true;
  • for ($j = 2; $j <= sqrt($i); $j++) {
  • if ($i !== $j && $i % $j === 0) {
  • $is_prime = false;
  • break;
  • }
  • }
  • if ($is_prime === true) {
  • echo ‘<br>‘ . $i . ‘<br>‘;
  • }
  • }

6. 输出数列

问题:输出1 1 2 3 5 8 13……这样的数列,输出该数列的前20个数字。

分析:该题是一个基本的数字逻辑,在实际解决该问题时,首先要发现该数字的规律,然后按照该规律来设计数组即可。

实现思路:数字的规律是除了数列里的前两个数字以外,其它的数字都满足该数字等于前两个数字的和,由于题目要求输出前20个数字,所以需要一个长度为20的数组,第一个和第二个数字直接赋值,后续的数字通过前两个数字元素得到。

则实现的代码如下:

E:\博客-基础算法-代码\6_get_series.php:

  • <?php
  • $res[0] = 1;
  • $res[1] = 1;
  • for ($i = 2; $i < 20; $i++) {
  • $res[$i] = $res[$i - 1] + $res[$i - 2];
  • }
  • var_dump($res);

在该代码中,初始化一个长度为20的数组,首先将数组中的前两个元素赋值成1,然后循环对后续的元素的赋值,如果当前元素的下标是i,则它前一个元素的下标是i-1,再前面一个元素的下标是i-2,只需要将这2个元素的值相加,然后赋值给当前元素即可。

7. 歌手打分

问题:在歌唱比赛中,共有10位评委进行打分,在计算歌手得分时,去掉一个最高分,去掉一个最低分,然后剩余的8位评委的分数进行平均,就是该选手的最终得分。如果已知每个评委的评分,求该选手的得分。

分析:该题实际上涉及到求数组的最大值、最小值,以及求数组中所有元素的和,也是数组方便统计的用途体现。

实现思路:求出数组元素的最大值、最小值以及和,然后使用和减去最大值和最小值,然后除以8获得得分。

则实现的代码如下:

E:\博客-基础算法-代码\7_get_avt_score.php:

  • <?php
  • $score = array(90,78,90,96,67,86,78,92,79,85);
  • $avg = (array_sum($score) - max($score) - min($score)) / 8;
  • echo $avg;

8. 冒泡排序

原理:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,
将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。至此第一趟结束,将最
大的数放到了最后。在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一
直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。如此下
去,重复以上过程,直至最终完成排序。

由于在排序过程中总是小数往前放,大数往后放,相当于气泡往上升,所以称作冒泡排序。

用二重循环实现,外循环变量设为i,内循环变量设为j。外循环重复9次,内循环依次重复
9,8,...,1次。每次进行比较的两个元素都是与内循环j有关的,它们可以分别用a[j]和a[j+1]标识,i的值依次为1,2,...,9,对于
每一个i,j的值依次为1,2,...10-i。

实现的代码如下:

E:\博客-基础算法-代码\8_maopao.php:

  • <?php
  • /**
  • * 冒泡排序
  • * @param $array
  • * @return mixed
  • */
  • function bubble_sort($array)
  • {
  • for ($i = 1; $i < count($array); $i++) {
  • for ($j = 0; $j < count($array) - $i; $j++) {
  • if ($array[$j] > $array[$j + 1]) {
  • $temp = $array[$j + 1];
  • $array[$j + 1] = $array[$j];
  • $array[$j] = $temp;
  • }
  • }
  • }
  • return $array;
  • }
  • $array = array(5, 8, 9, 3, 4, 7, 2, 4, 1);
  • var_dump(bubble_sort($array));

9. 选择排序

原理:n个数的直接选择排序可经过n-1趟直接选择排序得到有序结果:

①初始状态:无序区为R[1..n],有序区为空。

②第1趟排序,在无序区R[1..n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1]交换,使R[1..1]和R[2..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。

……

③第i趟排序,第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中选出关键字最小的记录R[k],
将它与无序区的第1个记录R交换,使R[1..i]和R分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。

这样,n个数的直接选择排序可经过n-1趟直接选择排序得到有序结果。

实现的代码如下:

E:\博客-基础算法-代码\9_a_xuanze.php:

  • <?php
  • /**
  • * 选择排序
  • * @param $array
  • * @return mixed
  • */
  • function select_sort($array)
  • {
  • for ($i = 0; $i < count($array); $i++) {
  • $min = $i;
  • for ($j = $i + 1; $j < count($array); $j++) {
  • if ($array[$j] < $array[$min]) {
  • $min = $j;
  • }
  • }
  • if ($min !== $i) {
  • $temp = $array[$min];
  • $array[$min] = $array[$i];
  • $array[$i] = $temp;
  • }
  • }
  • return $array;
  • }
  • $array = array(5, 8, 9, 3, 4, 7, 2, 4, 1);
  • var_dump(select_sort($array));

10. 寻找孤立数字

问题:给定一个数组,数组内的数两两相同,只有一个数是孤立的,用最快的方式找出这个数。

分析:循环数组,判断第i个元素的值和其它位置的值是否相等,如果不存在相等的,那么这个数就是孤立数据。

实现的代码如下:

E:\博客-基础算法-代码\10_a_find_single_num.php:

  • <?php
  • /**
  • * 从数组(数组内的数两两相同,只有一个数是孤立的)中寻找孤立的数字
  • * Created by PhpStorm.
  • * User: Administrator
  • * Date: 2016/11/16
  • * Time: 10:43
  • */
  • $array = [1, 2, 3, 2, 3, 1, 4, 7, 6, 4, 7];
  • $res = find_single_num($array);
  • echo $res;
  • /**
  • * 从数组(数组内的数两两相同,只有一个数是孤立的)中寻找孤立的数字
  • * @param array $array 数组
  • * @return bool|int|mixed
  • */
  • function find_single_num($array)
  • {
  • if (!is_array($array)) {
  • return false;
  • }
  • $single = 0;
  • foreach ($array as $key1=>$value1) {
  • $is_single = true;
  • foreach ($array as $key2=>$value2) {
  • if ($key2 !== $key1 && $value2 === $value1) {
  • $is_single = false;
  • break;
  • }
  • }
  • if ($is_single === true) {
  • $single = $value1;
  • break;
  • }
  • }
  • return $single;
  • }

显然这样的嵌套循环判断复杂度是很高的,达到n的平方,所以使用异或(^),则实现的代码如下:

  • <?php
  • /**
  • * 从数组(数组内的数两两相同,只有一个数是孤立的)中寻找孤立的数字
  • * Created by PhpStorm.
  • * User: Administrator
  • * Date: 2016/11/16
  • * Time: 10:43
  • */
  • $array = [1, 2, 3, 2, 3, 1, 4, 7, 6, 4, 7];
  • $res = find_single_num($array);
  • echo $res;
  • /**
  • * 从数组(数组内的数两两相同,只有一个数是孤立的)中寻找孤立的数字
  • * @param array $array 数组
  • * @return bool|int|mixed
  • */
  • function find_single_num($array)
  • {
  • if (!is_array($array)) {
  • return false;
  • }
  • $single = 0;
  • foreach ($array as $value) {
  • $single = $single ^ $value;
  • }
  • return $single;
  • }
				
时间: 2024-08-03 07:04:35

【基础算法】基础算法【转载】的相关文章

算法基础之排序(2)--选择排序 改进

1 /********************************************************************************************************** 2 * Function : test 3 * Create Date : 2014/03/23 4 * Author : NTSK13 5 * Email : [email protected] 6 * Copyright : 欢迎大家和我一起交流学习,转载请保持源文件的完整性

算法基础之排序(1)--冒泡排序 改进

1 /********************************************************************************************************** 2 * Function : test 3 * Create Date : 2014/03/23 4 * Author : NTSK13 5 * Email : [email protected] 6 * Copyright : 欢迎大家和我一起交流学习,转载请保持源文件的完整性

Levenberg-Marquardt算法基础知识

Levenberg-Marquardt算法基础知识 (2013-01-07 16:56:17) 转载▼ 什么是最优化?Levenberg-Marquardt算法是最优化算法中的一种.最优化是寻找使得函数值最小的参数向量.它的应用领域非常广泛,如:经济学.管理优化.网络分析.最优设计.机械或电子设计等等.根据求导数的方法,可分为2大类.第一类,若f具有解析函数形式,知道x后求导数速度快.第二类,使用数值差分来求导数.根据使用模型不同,分为非约束最优化.约束最优化.最小二乘最优化. 什么是Leven

c++ 提高4 map容器 共性机制 使用时机 比较| STL算法 算法基础仿函数 谓词 函数适配器 遍历算法

[本文谢绝转载] <大纲> STL 容器 map 容器的4中初始化 遍历 map容器 元素的删除观测map.insert返回值,方法123,已存在就报错,初始化方法4会覆盖 map的查找,异常处理 map容器的range返回两个迭代器 multimap案例,按照部门_增删员工信息 容器共性机制 把对象放到容器中,会自动执行拷贝构造函数 各个容器的使用时机 vector与deque的比较: 算法 算法基础 函数对象(仿函数) 函数对象 与普通函数的区别:--  相同之处 函数对象 与普通函数的区

【机器学习基础】机器学习算法的分类——关于如何选择机器学习算法和适用解决的问题

引子 系统的学习机器学习课程让我觉得受益匪浅,有些基础问题的认识我觉得是非常有必要的,比如机器学习算法的类别. 为什么这么说呢?我承认,作为初学者,可能无法在初期对一个学习的对象有全面而清晰的理解和审视,但是,对一些关键概念有一个初步并且较为清晰的认识,有助于让我们把握对问题的认识层次,说白了,就是帮助我们有目的的去学习心得知识,带着问题去学习,充满对解决问题的动力去实验,我觉得这种方式是有益并且良性的. 之前,我遇到过很多这方面的问题,可能出于对问题分析不够,在寻找解决的问题的方法或者模型的时

零基础学贪心算法

本文在写作过程中参考了大量资料,不能一一列举,还请见谅.贪心算法的定义:贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解.贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关.解题的一般步骤是:1.建立数学模型来描述问题:2.把求解的问题分成若干个子问题:3.对每一子问题求解,得到子问题的局部最优解:4.把子问题的局部最优

九章算法 基础算法 强化算法 系统设计 大数据 安卓 leetcode 高清视频

leetcode 直播视频讲座录像 九章算法视频录像,PPT 算法班,算法强化班,Java入门与基础算法班,big data项目实战班,Andriod项目实战班 九章算法下载 九章算法面试 九章算法leetcode 九章算法答案 九章算法mitbbs 九章算法班 九章算法ppt 九章算法录像 九章算法培训 九章算法微博 leetcode 视频 九章算法偷录 算法培训 算法班课程大纲: 1 从strStr谈面试技巧与Coding Style(免费试听) 2 二分搜索与旋转排序数组 Binary S

python小白-day4递归和算法基础

递归&算法基础 一.递归 递归函数的优点是定义简单,逻辑清晰.理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰. 使用递归函数需要注意防止栈溢出.在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧.由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出. 1 2 3 4 5 6 7 8 def calc(n):     print(n)     if n/2>1:         r

算法——基础篇——快速排序

快速排序是一个经常使用的算法,由于每次用的时候,都感觉没有理解清楚,特写一篇文章记录一下. 算法介绍 快速排序有点类似有冒泡排序,冒泡排序从相邻的两个元素比较,小的在左边,大的在右边,这个算法很容易理解.而快速排序它相当于是在一头一尾两边分别排序比较,比较的对象是当前元素值,和一个选定的key值,主题的思想就是通过跟key值比较,把大于key的值放在右边,小于的放在左边这样就完成了一次排序,接着在对key值左边的序列进行同样的操作,右边也是,最后便能将所有的元素给排好序,由于它每次排序,都会分成

算法——基础篇——二分查找

     二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好:其缺点是要求待查表为有序表,且插入删除困难.因此,折半查找方法适用于不经常变动而查找频繁的有序列表.     首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功:否则利用中间位置记录将表分成前.后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表.重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功