剑指 Offer 11. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。
n == numbers.length
1 <= n <= 5000
-5000 <= numbers[i] <= 5000
numbers 原来是一个升序排序的数组,并进行了 1 至 n 次旋转
题解:
原数组是一个升序的数组,并进行了 1 至 n 次旋转。意味着除了n次旋转,旋转后的数组必然存在两个升序的子数组,且右边的子数组是“小”的那部分,也是我们最终要定位的数组,最小值必然在里面。
如上图所示,我们先设置两个指针指向数组首尾,采用二分查找,计算出两个点的中点,我们需要知道中点是在第一个子数组还是第二个子数组里。
如果在第一个中,那么中点元素大于等于p1所指的元素,将p1移到中点的位置,如图a。
如果在第二个中,那么中点元素小于等于p2所指的元素,将p2移到中点的位置,如图b。
当处于图c的情况后,说明已经找到最小值了。
注意!注意!注意!
1.上面所讲的情况只不包含1,2,3,4,5这种无逆转数组或者叫n次逆转数组。
2.不包含以下的情况
所以需要特别处理,情况1的处理方式是,得出的结果和数组第一个数再进行比较。
情况2的处理方式是,加入判断,当怕p1,p2,mid三个索引指向的数都相等时,用遍历的方法查找最小值。
代码:
class Solution {
public:
int minArray(vector<int>& numbers) {
int index1=0,indexMid=0,index2=numbers.size()-1;
while(index1<index2){
if(index2-index1<=1) //已经找到最小值,跳出循环
break;
indexMid=(index1+index2)/2;
//特殊情况,1,0,1,1,1,这种情况下只能循环判断了
if(numbers[indexMid]==numbers[index1] && numbers[indexMid]==numbers[index2]){
for(int i=index1+1;i<=index2;++i)
if(numbers[index2]>numbers[i])
index2=i;
break;
}
if(numbers[indexMid]>=numbers[index1]) index1=indexMid;
else if(numbers[indexMid]<=numbers[index2]) index2=indexMid;
}
return min(numbers[index2],numbers[0]); //防止n次旋转,如1,3,5
}
};
结果:
剑指 Offer 12. 矩阵中的路径
题解:
首先,遍历矩阵,如果遇到与字符串第一个字符相同的元素,就开始递归。
递归,选择pos==word.size()作为终止条件,走到这一步返回true
然后,设置边界,防止越界溢出。
注意,如果board[i][j]这个点走过,就把它置为*,防止形成回路又走到自己了。在下一次递归完成之后恢复原来的数值。
代码:
class Solution {
public:
bool dfs(vector<vector<char>>& board, string word,int i,int j,int pos){
if(pos==word.size())
return true;
if(i<0||j<0||i>=board.size()||j>=board[0].size()||board[i][j]!=word[pos])
return false;
char c=board[i][j];//防止又找到自己
board[i][j]='*';
bool res=dfs(board,word,i+1,j,pos+1)||dfs(board,word,i,j+1,pos+1)||dfs(board,word,i-1,j,pos+1)||dfs(board,word,i,j-1,pos+1);
board[i][j]=c;
return res;
}
bool exist(vector<vector<char>>& board, string word) {
for(int i=0;i<board.size();++i)
for(int j=0;j<board[0].size();++j)
if(dfs(board,word,i,j,0))
return true;
return false;
}
};
结果:
剑指 Offer 13. 机器人的运动范围
题解:
方法一:递归
思想和题12一样,只不过缺了一个矩阵,所以需要自己构建一个。范围是0~100,所以用i%10+i/10+j%10+j/10判断即可
首先,从(0,0)开始递归,设置终止条件,越界,已走过的节点,数位之和小于k三者作为终止条件。
然后,把当前节点设为true,res值+1,开始从上,下,左,右继续递归。
代码:
class Solution {
public:
void dfs(vector<vector<bool>>&dp,int i, int j, int k,int &res){
if(i<0||j<0||i>=dp.size()||j>=dp[0].size()||dp[i][j]==true||i%10+i/10+j%10+j/10>k)
return;
++res;
dp[i][j]=true;
dfs(dp,i-1, j, k,res);
dfs(dp,i+1, j, k,res);
dfs(dp,i, j-1, k,res);
dfs(dp,i, j+1, k,res);
}
int movingCount(int m, int n, int k) {
vector<vector<bool>>dp(m, vector<bool>(n, false));
int res=0;
dfs(dp,0,0,k,res);
return res;
}
};
结果:
方法二:动态规划
我们只需要判断当前位置的上面的点和左边的点有没有走过,再判断当前的点的数位的和是否小于k即可。
代码:
class Solution {
public:
int sum(int num1,int num2){
int sum=0;
while(num1||num2){
sum+=num1%10+num2%10;
num1=num1/10;
num2=num2/10;
}
return sum;
}
int movingCount(int m, int n, int k) {
vector<vector<bool>>dp(m+1, vector<bool>(n+1, false));
dp[0][1]=true;
int res;
for(int i=0;i<m;++i)
for(int j=0;j<n;++j)
if((dp[i][j+1]||dp[i+1][j])&&sum(i,j)<=k){
dp[i+1][j+1]=true;
++res;
}
return res;
}
};
结果:
剑指 Offer 14- I. 剪绳子
题解:
方法一:3的个数
尽量多找3,因为像5,2*3最大;6,3*3最大;8,3*3*2最大
如果是4的话,让它变成2*2,不是1*3,也就是说,n>4才起步计算
首先,排除2,3的情况,如果大于4则进行循环找3的工作
最后剩下的,必然小于4,这时候就不需要再拆分了,直接做乘法操作即可
代码:
class Solution {
public:
int cuttingRope(int n) {
if(n==2)return 1;
if(n==3)return 2;
int res=1;
while(n>4){
n=n-3;
res*=3;
}
return res*n;
}
};
结果:
方法二:
首先,排除特殊情况,n=2或者3的时候。
从4到n开始遍历,
代码:
class Solution {
public:
int cuttingRope(int n) {
if(n<4) return n-1;
vector<int>dp(n+1,0);
dp[1]=1;dp[2]=2;dp[3]=3;
for(int i=4;i<=n;++i)
for(int j=1;j<=i/2;++j)
dp[i]=max(dp[j]*dp[i-j],dp[i]);
return dp[n];
}
};
结果:
剑指 Offer 14- II. 剪绳子 II
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
题解:
和上一题一模一样,只是增加n的长度,结果需要取模.
考虑到n 的最大长度会达到1000,用动态规划的话需要建立n+1个数组,空间复杂度会很高。而且不容易判断大小,所以采用上题的方法一
代码:
class Solution {
public:
int cuttingRope(int n) {
if(n < 4){
return n - 1;
}
long res = 1;
while(n > 4){
res = res * 3 % 1000000007;
n -= 3;
}
return (int) (res * n % 1000000007);
}
};
结果:
题解:
代码:
class Solution {
public:
int hammingWeight(uint32_t n) {
int res=0;
while(n){
++res;
n=n&n-1;
}
return res;
}
};