剑指offer 11~15

面试题11:旋转数组的最小元素

#include<iostream>
#include<vector>
using namespace std;

//题目:找出旋转数组的最小元素
//思路:
/*
* 这道题如果直接遍历找最小值就没意思了,题目的本意应该是通过二分查找来解决问题
* 举例分析:假设数组为{3,4,5,0,1,1,2} 可以看到旋转过的数组由两个非递减序列构成,最小数为第二个序列的头
* 且前一个序列的任意数字都大于等于第二个序列的任意数字
* 最小元素的特性为:小于等于它左侧元素,同时小于等于其右侧元素
* 整个查找流程如下:
* 先找到当前数组的中间元素nums[mid],和数组头元素及尾元素进行比较,有三种情况:
* 1.当前的中间元素nums[mid]大于等于数组头元素nums[begin],这表明中间元素位于第一个非递减序列上,此时下标为begin~mid可以被排除,begin更新为mid
* 2.当前中间元素nums[mid]小于数组头元素nums[begin],这表明中间元素位于第二个非递减序列上,此时下标为mid~end可以被排除,end更新为mid,
* 3.还有一种特殊情况,即begin mid end三处的值一样 比如{1 1 1 0 1},此时无法通过前面的思路快速减小范围
* 只能通过遍历begin~end来查找
*/

//leetcode:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/submissions/
//牛客:https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=13&&tqId=11159&rp=1&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking
//leetcode旋转数组相关的题还有:
//33 搜索旋转排序数组1 https://leetcode-cn.com/problems/search-in-rotated-sorted-array/
//81 搜索旋转排序数组2 https://leetcode-cn.com/problems/search-in-rotated-sorted-array-ii/
int minNumberInRotateArray(vector<int> nums) {
	if (nums.size() == 0)
		return 0;
	//设置初始的begin、end及mid
	int begin = 0;
	int end = nums.size() - 1;
	int mid = begin;//mid初始值设为begin的原因在下面给出
	//循环进行的条件是nums[begin]和nums[end]分别位于不同的两个非递减序列上
	//一般我们可能会想到begin<end,但是旋转数组可能会出现旋转完仍有序的情况
	//举个例子{1,2,3}也可以说是旋转数组,按理说应该直接输出1,
	//如果循环条件设为begin<end,最终输出将是3而不是1,而循环条件为nums[begin] >= nums[end]
	//同时设置初始mid为0,则可以不进入循环,直接输出nums[0]
	while (nums[begin] >= nums[end]){
		if (begin + 1 == end){
			mid = end;
			break;
		}
		mid = (end - begin) / 2 + begin;
		//情况3
		if (nums[begin] == nums[mid] && nums[end] == nums[mid]){
			int min = 0x7FFFFFFF;
			for (int i = begin; i <= end; i++){
				if (nums[i]<min)
					min = nums[i];
			}
			mid = min;
			break;
		}
		//情况1
		if (nums[begin] <= nums[mid])
			begin = mid;
		//情况2
		else
			end = mid;
	}
	return nums[mid];
}

面试题12:矩阵中的路径

#include<iostream>
#include<vector>
using namespace std;

//题目12 矩阵中的路径
/*
* 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。
* 路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。
* 如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。
* 例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
* [["a","b","c","e"],
* ["s","f","c","s"],
* ["a","d","e","e"]]
* 但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,
* 路径不能再次进入这个格子。
*/

//思路
/*
* 此类问题的解法相对比较单一,主要依赖于DFS,也就是递归来实现
* 首先主函数中进行遍历,如果当前下标处矩阵元素等于字符串首字母,则进入dfs,进行进一步的查找
* 假设矩阵元素board[i][j]等于word[0],则从下标(i,j)开始,向上下左右移动,观察是否与word[1]一致
* 如果一致则继续,进入下一层递归,否则退回来,注意由于不能重复走,所以需要额外的数组用来保存是否已经visited的信息
* 这类题的解法其实都类似,学会总结,熟能生巧
*/

//leetcode:https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof/
//牛客:https://www.nowcoder.com/practice/c61c6999eecb4b8f88a98f66b273a3cc?tpId=13&tqId=11218&rp=1&ru=%2Factivity%2Foj&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking&tPage=4

//pos矩阵为方向矩阵,pos[0] pos[1] pos[2] pos[3]分别表示右 左 上 下
vector<int> pos[4] = { { 0,1 },{ 0,-1 },{ 1,0 },{ -1,0 } };
bool dfs(vector<vector<char>>& board, string word, int i, int j, int idx, vector<vector<bool>>& visited) {
	//如果当前idx等于字符串长 说明已经匹配完毕 直接返回true
	if (idx == word.size())
		return true;
	//分别向上下左右移动
	for (int k = 0; k<4; k++) {
		int x = i + pos[k][0];
		int y = j + pos[k][1];
		//如果移动后坐标符合实际情况 对应值也与字符串下一个字符相匹配,且当前坐标未被访问过
		if (x >= 0 && x<board.size() && y >= 0 && y<board[0].size() && !visited[x][y] && word[idx] == board[x][y]) {
			//说明这一步匹配成功,修改visited矩阵 并进入下一层
			visited[x][y] = true;
			if (dfs(board, word, x, y, idx + 1, visited))
				return true;
			//到达这一步说明前面的匹配没有成功,将visited数组对应位置的值再改回false
			visited[x][y] = false;
		}
	}
	return false;

}
bool exist(vector<vector<char>>& board, string word) {
	if (word.length() == 0)
		return false;
	if (board.size() == 0 && board[0].size() == 0)
		return false;
	int rows = board.size();
	int cols = board[0].size();
	vector<vector<bool>> visited(rows, vector<bool>(cols, false));
	//遍历矩阵,如果当前值等于word[0],进入dfs
	for (int i = 0; i<rows; i++) {
		for (int j = 0; j<cols; j++) {
			if (word[0] == board[i][j]) {
				visited[i][j] = true;
				if (dfs(board, word, i, j, 1, visited))
					return true;
				visited[i][j] = false;
			}
		}
	}
	return false;
}

面试题13:机器人的运动范围

#include<iostream>
#include<vector>
using namespace std;

//题目13 机器人运动范围
/*
* 地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,
* 它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。
* 例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],
* 因为3+5+3+8=19。请问该机器人能够到达多少个格子?
*/

//思路
/*
* 和12题一样 此类问题的解法相对比较单一,主要依赖于DFS,也就是递归来实现
* 这个题和12题的区别在于 12题返回的是能否匹配,这道题要返回机器人的运动范围 也就是坐标的个数
* 为了使整个结构更清晰,“行坐标和列坐标数位之和”专门设计函数getSum来实现
* 和上题一样,这里也需要构造visited数组 因为最终要记录坐标个数 visited数组用于保证不重复计算
* 运动范围的统计操作由counting函数实现
* 如果当前坐标(i,j)满足实际条件且未被访问和统计 同时满足“行坐标和列坐标数位之和大于k”
* 则count++,同时再去检查(i,j)的上下左右 即(i+1,j) (i-1,j) (i,j-1) (i,j+1) 再检查意味着进入深一层的递归
* 直到不再满足条件,再带着当前统计的count一层层返回和叠加
* 最终统计出所有结果
*/

//leetcode:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/
//牛客:https://www.nowcoder.com/practice/6e5207314b5241fb83f2329e89fdecc8?tpId=13&tqId=11219&rp=1&ru=%2Factivity%2Foj&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking&tPage=4
int movingCount(int m, int n, int k) {
	if (m<0 || n<0 || k<0)
		return 0;
	vector<vector<bool>> visited(m, vector<bool>(n, false));
	return counting(m, n, k, visited, 0, 0);
}

int counting(int rows, int cols, int limit, vector<vector<bool>>& visited, int i, int j) {
	int count = 0;
	if (i<rows&&i >= 0 && j<cols&&j >= 0 && !visited[i][j] && getSum(i) + getSum(j) <= limit) {
		visited[i][j] = true;
		count = 1 + counting(rows, cols, limit, visited, i, j + 1) + counting(rows, cols, limit, visited, i, j - 1) + counting(rows, cols, limit, visited, i + 1, j) + counting(rows, cols, limit, visited, i - 1, j);
	}
	return count;
}

int getSum(int n) {
	int sum = 0;
	while (n) {
		sum += n % 10;
		n /= 10;
	}
	return sum;
}

面试题14:剪绳子

#include<iostream>
#include<vector>
using namespace std;
//题目:
//给你一根长度为n的绳子,请把绳子剪成m段,每段绳子长度记为k[1]、k[2]、k[3]...请问可能的最大乘积是多少?


//牛客:https://www.nowcoder.com/practice/57d85990ba5b440ab888fc72b0751bf8?tpId=13&tqId=33257&rp=1&ru=%2Factivity%2Foj&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking&tPage=4
//leetcode:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/



//思路1:动态规划 从上至下递归,从下到上计算
int maxProductAfterCutting_solution1(int length)
{
	if (length < 2)
		return 0;
	if (length == 2)
		return 1;
	if (length = 3)
		return 2;
	//第i个元素表示把长度为i的绳子剪成若干段之后各段长度乘积的最大值
	vector<int> products(length,0);
	//长度为1 2 3的绳子不剪的时候乘积最大
	products[1] = 1;
	products[2] = 2;
	products[3] = 3;
	int max = 0;
	//对于长度为i(i>3)的绳子,由于长度1~i-1绳子剪下来的乘积最大值已知,保存在products[1]~products[i-1]中
	//可以分别计算所有的可能性并保存最大的值 保存在products[i]中
	for (int i = 4; i <= length; ++i){
		max = 0;
		for (int j = 1; j <= i / 2; ++j){
			int product = products[j] * products[i - j];
			if (max < product)
				max = product;

			products[i] = max;
		}
	}
	//最后返回products[length]
	return products[length];
}

//思路2:
/*
* 贪婪算法 在写代码之前找出对应子问题的最优解决方法,再体现在代码中,而不需要遍历去找最优
* 首先可以证明,当n大于5时,2*(n-2)>1*(n-1)并且3(n-3)>1*(n-1),
* 即绳子长度大于5时,剪去长度2或3 得到的乘积要大于剪去1
* 另外3(n-3)>=2(n-2),说明剪去长度3得到的乘积大于剪去长度2 所以尽可能地多剪长度为3的绳子段
* 而当绳子长度为4时,应该剪成2和2,而不是1和3
*/
int maxProductAfterCutting_solution2(int length)
{
	if (length < 2)
		return 0;
	if (length == 2)
		return 1;
	if (length = 3)
		return 2;
	//尽可能多地去剪去长度为3的绳子段
	int timesOf3 = length / 3;
	//当绳子最后剩下的长度为4的时候,不能再剪去长度为3的绳子段
	//此时更好的方法是把绳子剪成长度为2的两段
	if (length - timesOf3 * 3 == 1)
		timesOf3 -= 1;
	int timesOf2 = (length - timesOf3 * 3) / 2;
	return (int)(pow(3, timesOf3))*(int)(pow(2, timesOf2));
}

面试题15:二进制中1的个数

#include<iostream>
using namespace std;

//面试题15 二进制中1的个数
/*
请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如9表示为1001,有2位是1,结果为2
*/

//思路:
//把一个整数减去1,再和原整数做按位与运算,会把该整数最右边的1变成0,而高位不变
//一个数在变为0之前可以进行多少次这样的操作 就表明该数的二进制表示中有多少个1
//对第一句话举例说明:如果数字n二进制的最后一位为1,前面m-1位分别为1 1 1 0 ... 0
//n与n-1相与即为 1 1 1 0 ... 0 0
//如果数字n二进制最后一位为0,前面m-1位分别为1 1 1 0 ... 0
//则n-1的二进制表示为 1 1 0 1 ... 1 1
//n和n-1相与1 1 0 0 ... 0

//leetcode:https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/
//牛客:https://www.nowcoder.com/practice/8ee967e43c2c4ec193b040ea7fbb10b8?tpId=13&tqId=11164&rp=1&ru=%2Factivity%2Foj&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking&tPage=1
int NumberOf1(int n){
	int count = 0;
	while (n){
		count++;
		n = n & (n - 1);
	}
	return count;
}

//另外给出两道位运算的相关题目:
//判断一个整数是不是2的整数次方,若是2的整数次方,则二进制中1的个数为1
int isSQ(int num){
	if (NumberOf1(num) == 1)
		return 0;
	else return 1;
}

//输入两个整数m和n,计算需要改变m的二进制中的多少位才能得到n
//先求异或,异或中1的个数就是需要改变的位数个数
int NumberOfChange(int m, int n){
	int temp = m ^ n;
	return NumberOf1(temp);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值