目录
面试题 08.11. 硬币
硬币。给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
题解:动态规划
这是一道升级版的青蛙跳台阶的题。注意不能将内外两个循环交换,否则和dfs一样会有重复的情况
代码:
class Solution {
public:
int waysToChange(int n) {
vector<int>arr{1,5,10,25};
vector<int>dp(n+1,1);
dp[0]=1;
for(int k=1;k<4;++k){
for(int i=1;i<=n;++i){
if (i>=arr[k])
dp[i]=(dp[i]+dp[i-arr[k]])%1000000007;
}
}
return dp[n];
}
};
面试题 08.12. 八皇后
设计一种算法,打印 N 皇后在 N × N 棋盘上的各种摆法,其中每个皇后都不同行、不同列,也不在对角线上。这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。
注意:本题相对原题做了扩展
题解:递归
因为题目要求,每一列,对角线和反对角线上不能有重复的‘Q'。因此,我们需要建立三个bool数组用于记录是否出现。列很简单,建立一个1*n的数组即可;对角线上的元素行和列相加是相等的;反对角线上的元素n-i+j的结果是相等的。根据上述规则来建立三个bool数组。
在递归过程中,对每一行每一列都需要遍历,满足三个数组对应的位都是false的时候,便可放Q。开始下一次的递归,值得注意的是,再下一次的循环前需要把放Q的地方变为.。
代码:
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
vector<bool>col(n); //记录每一列
vector<bool>diagonal(n+n-1); //记录对角线
vector<bool>back_diagonal(n*2); //记录反对角线
vector<vector<string>>res;
vector<string>strs(n,string(n,'.'));
dfs(0,n,res,col,diagonal,back_diagonal,strs);
return res;
}
void dfs(int i,int n,vector<vector<string>>&res,vector<bool>&col,vector<bool>&diagonal,vector<bool>&back_diagonal,vector<string>&strs){
if(i==n){
res.push_back(strs);
return;
}
for(int j=0;j<n;++j){
if(col[j]||diagonal[i+j]||back_diagonal[n-i+j]) continue;
strs[i][j]='Q';
col[j]=diagonal[i+j]=back_diagonal[n-i+j]=true;
dfs(i+1,n,res,col,diagonal,back_diagonal,strs);
strs[i][j]='.';
col[j]=diagonal[i+j]=back_diagonal[n-i+j]=false;
}
}
};
面试题 08.13. 堆箱子
堆箱子。给你一堆n个箱子,箱子宽 wi、深 di、高 hi。箱子不能翻转,将箱子堆起来时,下面箱子的宽度、高度和深度必须大于上面的箱子。实现一种方法,搭出最高的一堆箱子。箱堆的高度为每个箱子高度的总和。
输入使用数组[wi, di, hi]表示每个箱子。
题解:
代码:
面试题 08.14. 布尔运算
给定一个布尔表达式和一个期望的布尔结果 result,布尔表达式由 0 (false)、1 (true)、& (AND)、 | (OR) 和 ^ (XOR) 符号组成。实现一个函数,算出有几种可使该表达式得出 result 值的括号方法。
题解:合并之后排序
代码:
class Solution {
public:
void merge(vector<int>& A, int m, vector<int>& B, int n) {
for(int i=m;i<A.size();++i){
A[i]=B[i-m];
}
sort(A.begin(),A.end());
}
};
面试题 10.01. 合并排序的数组
给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法,将 B 合并入 A 并排序。
初始化 A 和 B 的元素数量分别为 m 和 n。
题解:
代码:
面试题 10.02. 变位词组
编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。变位词是指字母相同,但排列不同的字符串。
题解:哈希+排序
遍历字符串数组,对每一个字符串进行排序,在哈希表中,对排序过的字符串进行查找。如果能找到,将原字符串添加到数组中。如果没有,重新开辟一个数组用于添加字符串。
代码:
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>>res;
unordered_map<string,int>map;
string temp;
for(string i:strs){
temp=i;
sort(temp.begin(),temp.end());
if(map.find(temp)!=map.end()){
res[map[temp]].push_back(i);
}else{
map[temp]=res.size();
res.push_back({i});
}
}
return res;
}
};
面试题 10.03. 搜索旋转数组
搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个。
题解:二分法
数组至少一半有序,一半无序。
如果target在有序的数组里,就在里面查找。
如果target在无序的数组里,就在里面查找。
如果像 11111211这种,就把前面重复的都消掉。
代码:
class Solution {
public:
int search(vector<int>& arr, int target) {
int l=0,r=arr.size()-1;
int mid=0;
if(target==arr[0]) return 0;
while(l<=r){
mid=(l+r)/2;
if(arr[mid]==target){
while(mid>0&&arr[mid]==arr[mid-1]) --mid;
return mid;
}
if(arr[mid]>arr[l]){//l~mid是递增序列
if(target>=arr[l]&&target<arr[mid])
r=mid-1;
else
l=mid+1;
}
else if(arr[mid]<arr[r]){//mid~r是递增序列
if(target>arr[mid]&&target<=arr[r])
l=mid+1;
else
r=mid-1;
}
else{//这步很关键
while (l <= r && arr[mid] == arr[l])l++;
while (l <= r && arr[r] == arr[mid])r--;
}
}
return -1;
}
};
面试题 10.05. 稀疏数组搜索
稀疏数组搜索。有个排好序的字符串数组,其中散布着一些空字符串,编写一种方法,找出给定字符串的位置。
题解:二分法
因为是有序排列,用普通的二分法即可,但是需要注意的一点是,如果遇到”“,那么需要一直消除直到遇到一个正常的字符串。否则二分法便会失效
代码:
class Solution {
public:
int findString(vector<string>& words, string s) {
int l=0,r=words.size()-1,mid=0;
while(l<=r){
mid=(l+r)/2;
while(mid>l&&words[mid]=="") --mid;//把""去掉
if(words[mid]==s)
return mid;
else if(s<words[mid])
r=mid-1;
else if(s>words[mid])
l=mid+1;
}
return -1;
}
};
面试题 10.09. 排序矩阵查找
给定M×N矩阵,每一行、每一列都按升序排列,请编写代码找出某元素。
题解:z字查找
首先,从(0,n-1)开始,也就是矩阵的右上角。
如果遇到
1.target=matrix[i][j],返回true;
2.target>matrix[i][j],i++,因为每一行有序,前面不会有更大的了;
3.target<matrix[i][j],j--,因为每一行有序,在这行中查找是否有等于target的值即可。
代码:
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.size()==0||matrix[0].size()==0) return false;
int i=0,j=matrix[0].size()-1;
while(i<matrix.size()&&j>=0){
if(target==matrix[i][j])
return true;
else if(target>matrix[i][j]){//因为有序,所以前面的不会比它大
++i;
// j=matrix[0].size()-1;
}
else //这一行查找即可
--j;
}
return false;
}
};
面试题 10.10. 数字流的秩
假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。请实现数据结构和算法来支持这些操作,也就是说:
实现 track(int x) 方法,每读入一个数字都会调用该方法;
实现 getRankOfNumber(int x) 方法,返回小于或等于 x 的值的个数。
题解:
代码:
面试题 10.11. 峰与谷
在一个整数数组中,“峰”是大于或等于相邻整数的元素,相应地,“谷”是小于或等于相邻整数的元素。例如,在数组{5, 8, 4, 2, 3, 4, 6}中,{8, 6}是峰, {5, 2}是谷。现在给定一个整数数组,将该数组按峰与谷的交替顺序排序。
题解1:排序+交换
先排序,再交换奇偶位上的数即可。
代码:
class Solution {
public:
void wiggleSort(vector<int>& nums) {
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i=i+2){
if(i+1<nums.size()){
swap(nums[i],nums[i+1]);
}
}
}
};
题解2:遍历+交换
遍历数组,有两种情况:
1.峰处i遇到nums[i]>nums[i-1],交换两者位置;
2.谷处i遇到nums[i]<nums[i-1],交换两者位置。
代码:
class Solution {
public:
void wiggleSort(vector<int>& nums) {
for(int i=1;i<nums.size();++i){
if(i%2==0){
if(nums[i]>nums[i-1]) swap(nums,i,i-1);
}else{
if(nums[i]<nums[i-1]) swap(nums,i,i-1);
}
}
}
void swap(vector<int>& nums,int i,int j){
nums[i]^=nums[j];
nums[j]^=nums[i];
nums[i]^=nums[j];
}
};
面试题 16.01. 交换数字
编写一个函数,不用临时变量,直接交换numbers = [a, b]
中a
与b
的值。
题解:按位异或
例如3(011)与5(101),两者按位异或把3变成6(110),再次异或把5变成3(011),再次异或把6变成5(101)
代码:
class Solution {
public:
vector<int> swapNumbers(vector<int>& numbers) {
numbers[0]^=numbers[1];
numbers[1]^=numbers[0];
numbers[0]^=numbers[1];
return numbers;
}
};
面试题 16.02. 单词频率
设计一个方法,找出任意指定单词在一本书中的出现频率。
你的实现应该支持如下操作:
WordsFrequency(book)构造函数,参数为字符串数组构成的一本书
get(word)查询指定单词在书中出现的频率
题解:哈希
这题应该是考前缀树,但是哈希更快,也更简单。
代码:
class WordsFrequency {
public:
vector<string>books;
unordered_map<string,int>map;
WordsFrequency(vector<string>& book) {
for(auto s:book){
books.push_back(s);
++map[s];
}
}
int get(string word) {
return map[word];
}
};
/**
* Your WordsFrequency object will be instantiated and called as such:
* WordsFrequency* obj = new WordsFrequency(book);
* int param_1 = obj->get(word);
*/
面试题 16.03. 交点
给定两条线段(表示为起点start = {X1, Y1}和终点end = {X2, Y2}),如果它们有交点,请计算其交点,没有交点则返回空值。
要求浮点型误差不超过10^-6。若有多个交点(线段重叠)则返回 X 值最小的点,X 坐标相同则返回 Y 值最小的点。
题解:
代码:
面试题 16.04. 井字游戏
设计一个算法,判断玩家是否赢了井字游戏。输入是一个 N x N 的数组棋盘,由字符" ","X"和"O"组成,其中字符" "代表一个空位。
以下是井字游戏的规则:
玩家轮流将字符放入空位(" ")中。
第一个玩家总是放字符"O",且第二个玩家总是放字符"X"。
"X"和"O"只允许放置在空位中,不允许对已放有字符的位置进行填充。
当有N个相同(且非空)的字符填充任何行、列或对角线时,游戏结束,对应该字符的玩家获胜。
当所有位置非空时,也算为游戏结束。
如果游戏结束,玩家不允许再放置字符。
如果游戏存在获胜者,就返回该游戏的获胜者使用的字符("X"或"O");如果游戏以平局结束,则返回 "Draw";如果仍会有行动(游戏未结束),则返回 "Pending"。
题解:数组记录
开辟大小为n的两个数组row,col记录行和列,两个变量diagonal,anti_diagonal记录对角线和反对角线,变量pending用于记录是否有空位。
然后,开始遍历矩阵,会遇到三种情况:
1.'O',对应的row,col,diagonal,anti_diagonal做++操作;
2.'X',对应的row,col,diagonal,anti_diagonal做--操作;
3.' ',pending置为true。
在循环体内判断row,col,diagonal,anti_diagonal是否等于n或者-n,若有返回'O'或'X'。
循环结束,代表无人胜利,那么判断pending是否为true。
代码:
class Solution {
public:
string tictactoe(vector<string>& board) {
int n=board.size();
vector<int>row(n,0);
vector<int>col(n,0);
int diagonal=0;
int anti_diagonal=0;
int flag=0;
bool pending=false;
for(int i=0;i<n;++i)
for(int j=0;j<n;++j){
if(board[i][j]=='O') flag=1;
else if(board[i][j]=='X') flag=-1;
else {
pending=true;//有空位
continue;
}
row[i]+=flag;
col[j]+=flag;
if(i==j) diagonal+=flag;
if(i+j==n-1) anti_diagonal+=flag;
if(row[i]==n||col[j]==n||diagonal==n||anti_diagonal==n)
return "O";
else if(row[i]==-n||col[j]==-n||diagonal==-n||anti_diagonal==-n)
return "X";
}
if(pending)
return "Pending";
else
return "Draw";
}
};
面试题 16.05. 阶乘尾数
设计一个算法,算出 n 阶乘有多少个尾随零。
题解:数学公式
其实是一道数学题,因为阶乘里面的2的个数远大于5的个数,2*5=10。有几个5就有几个0,我们只需要知道有几个5就可以了,15有3个5,25有5个5,但是!25本身就是有两个5组成,所以25有6个5。那怎么表示呢?对n/5操作,一直到n=0为止。
代码:
class Solution {
public:
int trailingZeroes(int n) {
int res=0;
while(n){
res+=n/5;
n/=5;
}
return res;
}
};
面试题 16.06. 最小差
给定两个整数数组a
和b
,计算具有最小差绝对值的一对数值(每个数组中取一个值),并返回该对数值的差
题解:
代码:
class Solution {
public:
int smallestDifference(vector<int>& a, vector<int>& b) {
sort(a.begin(),a.end());
sort(b.begin(),b.end());
int res=INT_MAX;
int i=0,j=0;
while(i<a.size()&&j<b.size()){
long diff=a[i]-b[j];
if(abs(diff)<res)
res=abs(diff);
if(diff<0){
++i;
}else
++j;
}
return res;
}
};
面试题 16.07. 最大数值
编写一个方法,找出两个数字a
和b
中最大的那一个。不得使用if-else或其他比较运算符。
题解:
用数学公式 :
代码:
class Solution {
public:
int maximum(int a, int b) {
return (fabs((long)a-(long)b)+(long)a+(long)b)/2;
}
};
面试题 16.08. 整数的英语表示
题解:
代码:
面试题 16.09. 运算
题解:
代码:
面试题 16.10. 生存人数
题解:
代码:
class Solution {
public:
int maxAliveYear(vector<int>& birth, vector<int>& death) {
vector<int>res(102,0);
int maxval=0;
int idx=0;
int temp=0;
for(int i=0;i<birth.size();++i){
++res[birth[i]-1900];
--res[death[i]-1900+1];
}
for(int i=0;i<=100;++i){
temp+=res[i];
if(temp>maxval){
maxval=temp;
idx=i;
}
}
return idx+1900;
}
};
面试题 16.11. 跳水板
题解:数学公式
首先,排除特殊情况,
k=0时,返回[];
shorter=longer时,返回shorter*k;
设结果F={k-i)*shorter+i*longer=k*shorter+i*(longer-shorter);
因为(longer-shorter)>0,所以结果F是随着i增大的数,所以不会重复的k+1个数。
代码:
class Solution {
public:
vector<int> divingBoard(int shorter, int longer, int k) {
if(k==0) return{};
if(shorter==longer) return {shorter*k};
vector<int>res;
for(int i=0;i<=k;++i){
res.push_back(k*shorter+i*(longer-shorter));
}
return res;
}
};
面试题 16.13. 平分正方形
题解:
代码:
面试题 16.14. 最佳直线
题解:
代码:
面试题 16.15. 珠玑妙算
题解:
代码:
面试题 16.16. 部分排序
题解:
代码:
面试题 16.17. 连续数列
给定一个整数数组,找出总和最大的连续数列,并返回总和。
题解:动态规划
利用动态规划的思想,遍历数组x1,x2,...,xn。
当遍历到x(k)时,遇到两种情况:
1.x(k-1)的最大累加和是正数,那么加上这个值,最后x(k)的最大连续数组和是x(k)+x(k-1);
2.x(k-1)的最大累加和是非正数,那么x(k)的最大连续数组和是x(k)。
循环直至遍历完数组,最后返回最大的那个数maxval。
代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int maxval=INT_MIN;
int dp=0;
for(auto n:nums){
if(dp>0)
dp+=n;
else
dp=n;
if(dp>maxval)
maxval=dp;
}
return maxval;
}
};
面试题 16.18. 模式匹配
题解:
代码:
面试题 16.19. 水域大小
题解:
代码:
面试题 16.20. T9键盘
题解:
代码:
面试题 16.21. 交换和
题解:
代码:
面试题 16.22. 兰顿蚂蚁
题解:
代码: