《剑指offer》算法题第三天

今日题目:

  1. 斐波那契数列
  2. 青蛙跳台阶问题(及其变种:变态跳台阶)
  3. 矩形覆盖
  4. 旋转数组的最小数字
  5. 矩阵中的路径
  6. 机器人的运动范围

细心的同学会发现,第1,2,3题其实对应的是《剑指》书上的同一道题目,即第10题斐波那契数列,这类问题属于递归问题,虽然思路比较简单,但却是属于那种不看答案想不出来,看了答案恍然大悟的题目,因此在平时同学们和博主都应该多练练这一类型的题目,培养这种递归的思维。有趣的是,博主在做题的时候发现这三道题目是可以用动态规划的思路来解决的,而且往往动态规划的所用的时间是要低于递归的。在后文中会以矩形覆盖为例子来说明。

第5,6两题是回溯法的题目,在leetcode上有不少这一类型的题,只要多做练习,这一类型的题目还算是比较容易上手的。本文只说明第6题。

3.矩行覆盖

题目描述:我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

思路:以n=8为例,我们先把2*8矩形的覆盖方法记为f(8)。当用一个2*1的小矩形取覆盖大矩形的最左边时有两种选择:竖着放或横着放。如果选择竖着放,那么右边还剩下2*7的区域,也就是f(7);如果横着放在左上角,左下角必须也放置一个矩形,那么右边还剩下2*6的区域,也就是f(6)。所以我们有f(8)=f(7)+f(6)。从这边我们可以看出来,其实这就是一个斐波那契数列。

理清思路后,接下来我们分别用递归以及动态规划的方法来实现。

递归代码如下:

1 public class Solution {
2     public int RectCover(int target) {
3         if(target == 0) return 0;
4         if(target == 1) return 1;
5         if(target == 2) return 2;
6         return RectCover(target-1)+RectCover(target-2);
7     }
8 }

代码比较简单、清晰,但是效率非常的低,他的时间复杂度不难推出来是成指数的。在牛客网上的运行时间为447ms。

接下来我们看一下动态规划是如何解决的,代码如下:

 1 public class Solution {
 2     public int RectCover(int target) {
 3         if(target == 0) return 0;
 4         int[] dp = new int[target+1];
 5         dp[0] = 1;
 6         dp[1] = 1;
 7         for(int i = 2; i <= target; i++){
 8             dp[i] = dp[i-1] + dp[i-2];
 9         }
10         return dp[target];
11     }
12 }

很明显,这段代码的时间复杂度仅为O(n),当然空间复杂度也为O(n),所以,你也可以这么做:

 1 public class Solution {
 2     public int RectCover(int target) {
 3         if(target == 0) return 0;
 4         int num1 = 1;
 5         int num2 = 1;
 6         int res = 1;
 7         for(int i = 2; i <= target; i++){
 8             res = num1 + num2;
 9             num1 = num2;
10             num2 = res;
11         }
12         return res;
13     }
14 }

这样空间复杂度也就仅为常数啦。动态规划在同样的条件下运行时间仅为12ms。

4.旋转数组的最小数字

题目描述:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

这题在leetcode上面是有原题的,当时博主在做的时候并没有太多的想法,直接从后往前遍历数组,如果某个数比它前面的数字要小的话,那么这个数就是最小的,时间复杂度为O(n)。代码如下:

 1 public class Solution {
 2     public int minNumberInRotateArray(int [] array) {
 3         if(array.length ==0) return 0;
 4         for(int i = array.length-1; i >0; i--){
 5             if(array[i-1] > array[i])
 6                 return array[i];
 7         }
 8         return array[0];
 9     }
10 }

但是在《剑指》上,这题有一个时间复杂度为O(logn)的思路:利用二分查找的思路来寻找最小数字,具体的过程阐述起来过于复杂,感兴趣的同学可以阅读书上第83页,这边只贴出代码:

 1 public class Solution {
 2     public int minNumberInRotateArray(int [] array) {
 3         if(array.length ==0) return 0;
 4         int s = 0;
 5         int e = array.length - 1;
 6         if(array[s] < array[e])//表示旋转了0个数字到数组后面
 7             return array[s];
 8         while(s != e){
 9             if(e - s == 1)
10                 return array[e];
11             int mid = (s+e)/2;
12             if(array[mid] == array[s] && array[mid] == array[e])//处理特殊情况
13                 return orderFind(array,s,e);
14             if(array[mid] >= array[s])
15                 s = mid;
16             else if(array[mid] <= array[e])
17                 e = mid;
18         }
19         return array[s];
20     }
21     public int orderFind(int[] array,int start,int end){
22         int res = array[start];
23         for(int n:array){
24             res = (n < res)?n:res;
25         }
26         return res;
27     }
28 }

6. 机器人的运动范围

题目描述:
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

思路:这是比较直接的回溯法的题目,首先使用一个数组来标记机器人已经走过的放歌,防止重复,然后使用回溯法让机器人运动,每次运动到一个方格都判断一下是否合法,如果合法,计数器加一接着往下走;否则返回0;

代码如下:

 1 public class Solution {
 2     public int movingCount(int threshold, int rows, int cols)
 3     {
 4         boolean[][] visited = new boolean[rows][cols];
 5         return movingCount(threshold,visited,rows,cols,0,0);
 6     }
 7
 8     public int movingCount(int k,boolean[][] visited,int rows,int cols,
 9                           int row,int col)
10     {
11         if(row < 0 || row >= rows||
12           col < 0 || col >= cols || visited[row][col])
13             return 0;
14         visited[row][col] = true;
15         int sum = 0;
16         int row_tmp = row;
17         int col_tmp = col;
18         do{
19             sum += (row%10);
20             row /= 10;
21         }while(row > 0);
22
23         do{
24             sum += (col%10);
25             col /= 10;
26         }while(col > 0);
27         row = row_tmp;
28         col = col_tmp;
29         if(sum > k)
30             return 0;
31         else{
32             return movingCount(k,visited,rows,cols,row+1,col)+
33                 movingCount(k,visited,rows,cols,row-1,col)+
34                 movingCount(k,visited,rows,cols,row,col+1)+
35                 movingCount(k,visited,rows,cols,row,col-1)+1;
36         }
37     }
38 }

原文地址:https://www.cnblogs.com/wezheng/p/8361520.html

时间: 2024-10-11 15:58:51

《剑指offer》算法题第三天的相关文章

剑指offer算法总结

剑指offer算法学习总结 节选剑指offer比较经典和巧妙的一些题目,以便复习使用.一部分题目给出了完整代码,一部分题目比较简单直接给出思路.但是不保证我说的思路都是正确的,个人对算法也不是特别在行,只不过这本书的算法多看了几遍多做了几遍多了点心得体会.于是想总结一下.如果有错误也希望能指出,谢谢. 具体代码可以参考我的GitHub仓库: https://github.com/h2pl/SwordToOffer 数论和数字规律 从1到n整数中1出现的次数 题目描述 求出1~13的整数中1出现的

剑指 offer 第一题: 二维数组中的查找

打算写 图解剑指 offer 66 题 的系列文章,不知道大家有没有兴趣 ?? 题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 题目分析 图 1 如果没有头绪的话,很显然使用 暴力解法 是完全可以解决该问题的. 即遍历二维数组中的每一个元素,时间复杂度:O(n^2). 其实到这里我们就可以发现,使用这种暴力解法并没有充分利用题目给出的信息.这

剑指offer链表题的双指针法总结

本篇博客旨在总结双指针法在剑指offer链表题中的应用 包括删除链表中重复的节点.链表中倒数第k个节点.链表中环的入口节点.反转链表.合并两个排序的链表.两个链表的第一个公共节点. 根据双指针的类型,可以大致分为三种: 第一种是间隔一定距离的双指针法,包括删除链表中重复的节点.链表中倒数第k个节点两题 删除链表中重复的节点 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4->5

剑指offer编程题Java实现——面试题12相关题大数的加法、减法、乘法问题的实现

用字符串或者数组表示大数是一种很简单有效的表示方式.在打印1到最大的n为数的问题上采用的是使用数组表示大数的方式.在相关题实现任意两个整数的加法.减法.乘法的实现中,采用字符串对大数进行表示,不过在具体的计算中,还是要将字符串转化成字符数组来进行计算. 实现两个大数的加法,要考虑到两个问题,两个数的和的位数问题,以及如何处理两个数按位相加产生的进位问题.首先两个整数相加,两个数的和的位数最多比最大的整数的位数多1:这样和的位数就确定了.对于进位问题,我的做法是先进行按位相加,相加操作完成后再按照

剑指offer编程题Java实现——面试题9斐波那契数列

题目:写一个函数,输入n,求斐波那契数列的第n项. 1 package Solution; 2 3 /** 4 * 剑指offer面试题9:斐波那契数列 5 * 题目:写一个函数,输入n,求斐波那契数列的第n项. 6 * 0, n=1 7 * 斐波那契数列定义如下:f(n)= 1, n=2 8 * f(n-1)+f(n-2), n>2 9 * @author GL 10 * 11 */ 12 public class No9Fibonacci { 13 14 public static void

剑指offer编程题Java实现——面试题7相关题用两个队列实现一个栈

剑指offer面试题7相关题目:用两个队列实现一个栈 解题思路:根据栈的先入后出和队列的先入先出的特点1.在push的时候,把元素向非空的队列内添加2.在pop的时候,把不为空的队列中的size()-1份元素poll出来,添加到另为一个为空的队列中,再把队列中最后的元素poll出来两个队列在栈不为空的情况下始终是有一个为空,另一个不为空的.push添加元素到非空的队列中,pop把非空队列的元素转移到另一个空的队列中,直到剩下最后一个元素,这个元素就是要出栈的元素(最后添加到队列中的元素). 1

剑指offer编程题Java实现——面试题14调整数组顺序使奇数位于偶数之前

题目: 输入一个整数数组,实现一个函数来调整该数组中数组的顺序,使得所有的奇数位于数组的前半部分,偶数位于数组的后半部分. 解题思路:数组中维护两个指针,第一个指针初始化时候指向数组头部,第二个指针初始化时候指向数组尾部,第一个指针指向的数字总是偶数,第二个指针指向的数字总是奇数,如果第一个指针在第二个指针之前,则交换两指针指向的元素. 1 package Solution; 2 3 /** 4 * 剑指offer面试题14:调整数组顺序是奇数位于偶数前面 5 * 题目:输入一个整数数组,实现一

剑指offer编程题Java实现——面试题10二进制中1的个数

题目: 请实现一个函数,输入一个整数,输出该整数二进制表示中1的个数.例如,把9表示成二进制是1001,有2位是1,该函数输出2解法:把整数减一和原来的数做与运算,会把该整数二进制表示中的最低位的1变成0,与运算进行多少次就有多少个1. 1 package Solution; 2 /** 3 * 剑指offer面试题10:二进制中1的个数 4 * 题目:请实现一个函数,输入一个整数,输出该整数二进制表示中1的个数. 5 * 例如,把9表示成二进制是1001,有2位是1,该函数输出2 6 * 解法

剑指offer刷题—二维数组的查找

最近接触到一本书叫做剑指offer,在这里准备在这个2个月左右将这本书刷完,当然,不需要每天多少道什么的,不在多,一天理解一道就好了,希望能成为一种习惯,另外,准备在github上也进行同步分享. 今天第一道题: 面试题3:二位数组中的查找 当我们需要解决一个复杂问题时,一个很有效的方法就是从具体的问题出手,通过分析具体的例子,得到规律. 再一个二维数组中,每一行都要按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排列.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含

《剑指Offer》题三十一~题四十

三十一.栈的压入.弹出序列 题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的数字均不相等.例如,序列{1, 2, 3, 4 ,5}是某栈的压栈序列,序列{4, 5, 3, 2, 1}是该压栈序列对应的一个弹出序列,但{4, 3, 5, 1, 2}就不可能是该压栈序列的弹出序列. 分析:本题中的压栈序列并非是一次全部压入堆栈!如果没有思路,可以举一两个例子,一步步分析压栈.弹出的过程,从中找出规律. 三十二.从上到下打印二叉树 题目一:不分行从