数据结构与算法学习笔记4

  • 递归
    • 斐波那契数列
    • 青蛙跳台阶问题
    • 链表倒序打印
  • 分治法
    • 二分查找/折半查找 Binary Search
    • 题目1:快速幂
    • 题目2:如何判断一个数是否为2的次幂

递归

指在函数的定义中使用函数自身的方法。(在栈区消耗空间)

  • 特点:

    • ① 必须可以分解成若干个规模较小,但是与原问题形式相同的子问题。并且这些子问题可以用完全相同的解决思路来进行解决。
    • ② 递归问题的演化过程是一个把原问题从大到小进行拆解的过程,并且会有一个明确的终点。最后从这个临界点开始,把小问题的答案原路返回,原问题便得以解决。
  • 步骤:
    • ① 确定函数功能
    • ② 找出递归结束的条件
    • ③ 函数的主体(函数等价关系式)

斐波那契数列

f(1) = 1; f(2) = 1; f(n) = f(n-1) + f(n-2)

#include <stdio.h>unsigned int Fibonacci(unsigned int n){if (n == 2 || n == 1) return 1;if (n == 0)return 0;return Fibonacci(n-1) + Fibonacci(n-2);
}int main()
{unsigned int nResult = Fibonacci(10);   printf("%u\n",nResult); // %u是十进制无符号整数 return 0;
}
  • 经调试观察,50以上就巨慢了 因为函数调用的次数以等比数列疯狂增长

    • 优化思路:保存已经计算过的结果,下次可以直接进行调用,加快计算。

    • 继续优化:

      ​ 实际上,递归并不是唯一的解决方式,甚至也不是最好的方式,原因也正在于递归的特性:递归问题的演化过程是一个把原问题从大到小进行拆解的过程,并且会有一个明确的终点。最后从这个临界点开始,把小问题的答案原路返回,原问题便得以解决。

      ​ 基于以上,这是一个从大到小又从小返回到大得到结果的过程,那直接从小到大得到结果不是更快?同时,递归基于栈,实际上是消耗空间的,而从小到大利用循环的思路进行计算,已经计算过的小的都没有意义了,所以实际上所需要的空间也并不多,时间消耗为O(n),遍历一次,空间消耗为O(1),三个变量(i-2,i-1,i)即可。

      #include <stdio.h>unsigned int Fibonacci2(unsigned int n){if (n == 2 || n == 1) return 1;if (n == 0)return 0;unsigned int fn;unsigned int fn_1 = 1;unsigned int fn_2 = 1;unsigned int i;for (i = 3; i <= n; i++) {fn = fn_1 + fn_2;fn_2 = fn_1;fn_1 = fn;}return fn;
      }int main()
      {unsigned int nResult1 = Fibonacci2(50);printf("%u\n",nResult1);return 0;
      }
      

青蛙跳台阶问题

问题:一次性可以跳1个台阶,也可以一次性跳2个台阶,那么n级台阶一共有多少种跳法?

  • 规律类似于Fibonacci数列,代码如下:

        递归做法:public static int jump(int n){if (n==0)return 0;if (n==1)return 1;if (n==2)return 2;return jump(n-1)+jump(n-2);}非递归做法:public static int jump2(int n){if (n==0)return 0;if (n==1)return 1;if (n==2)return 2;int n1=1;int n2=2;int count=2;while (count++<=n){int tmp=n1;n1=n2;n2=tmp+n2;}return n2;}
    

升级版问题:一只青蛙一次可以跳上1级台阶,也可以跳上2 级……它也可以跳上n 级,此时该青蛙跳上一个n级的台阶总共有多少种跳法?

  • 当n = 1 时, 只有一种跳法,即1阶跳:Fib(1) = 1;
    当n = 2 时, 有两种跳的方式,一阶跳和二阶跳:Fib(2) = Fib(1) + Fib(0) = 2;
    当n = 3 时,有三种跳的方式,第一次跳出一阶后,后面还有Fib(3-1)种跳法;
    第一次跳出二阶后,后面还有Fib(3-2)中跳法;第一次跳出三阶后,后面还有Fib(3-3)种跳法,
    因此Fib(3)=Fib(2)+Fib(1)+Fib(0)=4;

    当n = n 时,共有n种跳的方式,第一次跳出一阶后,后面还有Fib(n-1)中跳法;
    第一次跳出二阶后,后面还有Fib(n-2)中跳法

    第一次跳出n阶后, 后面还有Fib(n-n)中跳法.

    Fib(n) = Fib(n-1)+Fib(n-2)+Fib(n-3)+…+Fib(n-n)=Fib(0)+Fib(1)+Fib(2)+…+Fib(n-1)
    又因为Fib(n-1)=Fib(0)+Fib(1)+Fib(2)+…+Fib(n-2)
    两式相减得:Fib(n)-Fib(n-1)=Fib(n-1) =====》 Fib(n) = 2*Fib(n-1) n >= 2

    public int Jump3(int n) {if (n <= 1) {return 1;} else {return 2 * Jump3(n - 1);}}

进阶版问题:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个m级的台阶总共有多少种跳法?

  • 先列多项式:
    f(n) = f(n-1) + f(n-2) + f(n-3) + … + f(n-m)
    f(n-1) = f(n-2) + f(n-3) + … + f(n-m) + f(n-m-1)
    化简得:f(n) = 2f(n-1) - f(n-m-1)
  • 代码如下:
 public static int Jump4(int n,int m ) {//当大于m的时候是上面的公式if(n > m){return 2*Jump4(n-1, m)-Jump4(n-1-m, m);}//当小于等于m的时候就是和n级的相同了if (n <= 1) {return 1;} else {return 2 * Jump4(n - 1,n);}}

链表倒序打印

PS:倒序打印和倒置是不一样的,前者链表结构不变,只是倒序打印,后者链表结构发生改变,方向进行变化了。

  • 方案:

    • 入栈出栈

      • 时间O(n);空间O(n)
    • 递归(下一个≠NULL,打印下一个,再打印本节点)

      • 时间O(n);空间O(n)

      • 代码如下:

        void ReversePrint(List *pHead){if (pHead == NULL) return;ReversePrint(pHead->pNext);printf("%d\t",pHead->nValue);
        }
        
    • 利用数组(链表节点值加入数组,倒序遍历数组)

      • 时间O(n);空间O(n)
    • 头插法倒置链表,然后遍历,完成后再倒置复原链表就行

      • 三次遍历 时间O(n) ;无空间消耗 空间O(1)

分治法

  • 定义:

    把一个大规模、高难度的问题分解为若干个小规模、低难度的小问题,再将多个小问题的解合并成大问题的解。(大数据集上优势明显)

  • 需要用分治法进行解决的问题的特性:

    1. 问题难度随着数据规模的缩小而降低
    2. 能够分解成小规模的问题
    3. 解可以合并
    4. 子问题的解相互独立
  • 二分是典型的分治法其中的一个处理方法,快排和归并也都是分治法的运用。

二分查找/折半查找 Binary Search

  • 二分查找的前提:必须有序

  • 代码:

    //二分查找代码实现
    #include <stdio.h>//递归方式
    int BinarySearch(int arr[],int nBegin,int nEnd,int nNum){if(arr == NULL || nBegin > nEnd) return -1;//int nMid = (nBegin + nEnd)/2;//不知道给定的数据是多大,如果很大的话,相加可能会超出int的上限出现问题,所以要进行优化int nMid = nBegin + (nEnd - nBegin)/2;if (arr[nMid] == nNum) {return nMid; }if (arr[nMid] > nNum) { //左侧return BinarySearch(arr, nBegin, nMid-1, nNum);}else {                 //右侧return BinarySearch(arr, nMid+1, nEnd, nNum);}
    }//循环方式
    int BinarySearch1(int arr[],int nBegin,int nEnd,int nNum){if(arr == NULL || nBegin > nEnd) return -1;int nMid;//找到中间位置,比较while (nBegin <= nEnd) {nMid = nBegin + (nEnd-nBegin)/2;if (arr[nMid] == nNum) {return nMid;}else if (arr[nMid] > nNum) {   //左侧nEnd = nMid - 1;}else {                         //右侧nBegin = nMid + 1;}}return -1;
    }int main()
    {int arr[] = {1,2,3,4,5,6,7,8};int nIndex = BinarySearch(arr,0,sizeof(arr)/sizeof(arr[0])-1,4);int nIndex2 = BinarySearch1(arr,0,sizeof(arr)/sizeof(arr[0])-1,5);printf("%d\n%d\n",nIndex,nIndex2);return 0;
    }
    

题目1:快速幂

快速计算一个数字的n次方

  • 确定奇偶(n%2 == 1 为奇数)

    • 奇: x ∗ x ( n − 1 ) / 2 ∗ x ( n − 1 ) / 2 x* x^{(n-1)/2} * x^{(n-1)/2} x∗x(n−1)/2∗x(n−1)/2
    • 偶: x n / 2 ∗ x n / 2 x^{n/2} * x^{n/2} xn/2∗xn/2
  • 代码:

    #include <stdio.h>int FastPower(int nNum,int n){if (n == 0) return 1;if (n == 1) return nNum;if (n%2 == 1) {     //奇数return nNum*FastPower(nNum,n-1);}else {             //偶数int nValue = FastPower(nNum, n/2);return nValue*nValue;}
    }int main(){int nResult = FastPower(2, 7);printf("%d\n",nResult);return 0;
    }
    
    • 2的50次方用上述的代码运行后得到0,因为int存不下这么大的数发生了截断。

题目2:如何判断一个数是否为2的次幂

  • 取余+除法:先取余,如果==0,再使用除法
  • int类型的数字最大也就32次方,就直接乘法然后判断每次乘完和这个数比较就行
  • 二进制里是否只有一个1(原二进制数和原二进制数减1进行&运算是否得到0,即 n&(n-1) == 0)

数据结构与算法学习笔记4:递归+分治法相关推荐

  1. 数据结构与算法学习笔记——链栈

    数据结构与算法学习笔记(C语言) 链栈 在开始链栈的学习之前,我们先实现一下上一篇文章中提到的检查括号匹配的小程序,鉴于水平有限,本人就随便写一下代码好了,目标仅限于对功能的实现. /*用顺序栈这种数 ...

  2. 数据结构与算法学习笔记——图 C++实现

    数据结构与算法学习笔记--图 C++实现 1 概念 2 图的表示方法 3 算法 3.1 拓扑排序 3.2 图的搜索算法 3.2.1 广度优先搜索(BFS) 3.2.2 深度优先搜索(DFS) 3.3 ...

  3. 数据结构与算法 学习笔记(8):字典、集合、哈希表

    数据结构与算法 学习笔记(8):字典.集合.哈希表 本次文章记录的是和字典.集合.哈希表等数据结构相关的LeetCode算法题(题号与LeetCode对应),包括其构造和使用,针对每一题或一类题给出了 ...

  4. 数据结构与算法学习笔记之 从0编号的数组

    数据结构与算法学习笔记之 从0编号的数组 前言 数组看似简单,但掌握精髓的却没有多少:他既是编程语言中的数据类型,又是最基础的数据结构: 一个小问题: 为什么数据要从0开始编号,而不是 从1开始呢? ...

  5. 数据结构与算法学习笔记之 提高读取性能的链表(上)

    数据结构与算法学习笔记之 提高读取性能的链表(上) 前言 链表(Linked list)比数组稍微复杂一点,在我们生活中用到最常见的应该是缓存,它是一种提高数据读取性能的技术,常见的如cpu缓存,浏览 ...

  6. 数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配

    数据结构与算法学习笔记15:最大流问题 / 二分图 / 有权无权二分图的匹配 / 匈牙利算法 / 银行家算法 / 稳定婚配 引入小题:最短路径 最大流问题(maximum flow problem) ...

  7. 数据结构与算法 学习笔记(5):字符串

    数据结构与算法 学习笔记(5)- 字符串 本次笔记记录了LeetCode中关于字符串的一些问题,并给出了相应的思路说明和代码.题目编号与LeetCode对应,方便查找. 题目1:LeetCode 13 ...

  8. 数据结构与算法学习笔记之先进先出的队列

    前言 队列是一种非常实用的数据结构,类似于生活中发排队,可应用于生活,开发中各个方面,比如共享打印机(先请求先打印),消息队列.你想知道他们是怎么工作的么.那就来一起学习一下队列吧 正文 一.队列的定 ...

  9. 【数据结构与算法学习笔记】

    文章目录 前言 0 Preview与算法复杂度分析简述 1 线性数据结构 1.1 Stack 1.2 Queue 1.3 Deque 1.4 UnorderedList 1.5 OrderedList ...

最新文章

  1. Java 领域offer收割:程序员黄金 5 年进阶心得!
  2. mysql二进制还原表_MYSQL 二进制还原
  3. Linux如何生成列表
  4. ffmpeg 2.6.3在Windows系统MinGW的编译
  5. 2021-06-09反射的类型方法
  6. windows7 纯净版各个版本下载
  7. LQR控制基本原理(包括Riccati方程具体推导过程)
  8. Katana程序集列表
  9. PyQt5渐变圆环水波进度条+透明淡入(多线程信号)
  10. 老男孩MySqI DBA周末班(三期)
  11. 在Gilt将微服务部署到AWS:ION-Roller简介
  12. springboot 结合 ice(飞冰) 实现上传功能
  13. 【微信小程序控制硬件④】 深度剖析微信公众号配网 Airkiss 原理与过程,esp8266如何自定义回调参数给微信,实现绑定设备第一步!(附带源码)
  14. java 翻译 go语言_[翻译]Go语言1.4版本将支持面向Android开发
  15. 表情设计都有哪些技巧,分享做好表情设计的几个细节
  16. PANTONE 色号
  17. iOS静态库和动态库
  18. 再一次感受爱的力量!
  19. 强大的UI组件集Telerik R3 2022支持.NET 7、全新的主题等
  20. 微信小程序--四色花瓣

热门文章

  1. Java byte[]与short[]之间转换
  2. LC 电路串联谐振与并联谐振
  3. Android开发平台
  4. 【超全】Go语言超详细学习知识体系
  5. CUDA10.2+cuDNN8+tensorflow-gpu2.3.1安装
  6. C语言数据结构一元多项式
  7. realsense中IMU的简单使用与学习
  8. 数学3大分支:代数、几何、分析 //转载
  9. java生成word带多级标题,word2007中多级标题的设置和目录的自动生成 | 学步园
  10. Java中的byte[]/char[]/int/String数据类型转换