模拟专题
1,丑数
基础版本
题目链接: 丑数 刷题链接.
编写一个程序判断给定的数是否为丑数。丑数就是只包含质因数 2, 3, 5 的正整数。
示例 1:
输入: 6
输出: true
解释: 6 = 2 × 3
示例 2:
输入: 8
输出: true
解释: 8 = 2 × 2 × 2
示例 3:
输入: 14
输出: false
解释: 14 不是丑数,因为它包含了另外一个质因数 7。
说明:
1 是丑数。
输入不会超过 32 位有符号整数的范围: [−231, 231 − 1]。
class Solution {
public:
bool isUgly(int num) {
int a[]={2, 3, 5};
for(auto prime : a)
while(num > 0 && num % prime == 0)//判断 num还能不能继续整除当前 prime
num /= prime;
return num == 1;
}
};
今天也是跟着y总刷题的一天,这个程序写的太简结了。厉害!
升级版本
题目链接: 丑数升级版 刷题链接.
题目:
我们把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。求第n个丑数的值。
样例
输入:5
输出:5
注意: 习惯上我们把1当做第一个丑数。
解题思路:
这道题的解法十分精妙,一般人想不出来那种。。。。当然大佬们可以~
这里简单介绍一下思路:
首先设三个指针,分别是 i, j, k。这三个指针赋值为2,3,5
先把第一个丑数 1 放入答案数组 ans[] 中,i, j, k 均指向 ans[1] 的位置,之后,ans[1] 分别与三个指针指向的数相乘,取最小的那个数 2添加到数组里面,即为第二个丑数(即 i * ans[1]),之后 i
指针向后移动一位,每个指针与其指向的数字相乘,得到 3,4,5,将 3 添加到数组里面,j 指针向后移动一位,指向 ans[2] ,再次相乘,比较,得到4,5,6,将 4 放到数组里面, i 继续向后移动一位,指向 ans[3],之后将5加到数组中,k 向后移动一位,此时ans[] = [1,2,3,4,5],j 指针指向的数是 2, i 指向 3 ,二者得到的乘积都是 6,为了避免丑数重复,i, j 指针同时后移一位,将 6 添加到数组中,之后就按这个顺序来,直到找到第 N 位丑数。
这个题的思路很像归并排序(y大佬这么说的,感觉我根本想不到。。),每次选 i, j, k 三个指针指向的数组中比较的那个拿出来放在答案数组中,就像三路归并那样去做(参考归并排序算法),现在,令 i 指向的数组里面的数是丑数里面满足是 2 的倍数的数的集合,包括 [2,4,8,6,8,10,…],令 j 指向的数组里面包含丑数里面所有满足是 3 的倍数的数,即 [3,6,9,…],k 指向的数组包含丑数里面所有满足是 5 的倍数的数
思路证明:
i ->[2,4,6,8,10,...] 该数组除以 2 得到 [1,2,3,4,5,...]
j ->[3,6,9,12,15,...] 该数组除以 3 得到 [1,2,3,4,5,...]
k ->[5,10,15,20,...] 该数组除以 2 得到 [1,2,3,4,5,...]
所以从上面发现,三个数组各自除以 指针 所表示的数,得到的就是
丑数序列,我们可以每次取 i 指向的数组里面数除以2,j 的除以 3
k 的除以 5 ,然后比较取最小数,相当于对三个数组去重合并,再加上
第一个数 1,就能得到我们的丑数序列。
又因为,除以相应数后,三个数组其实都是丑数序列,所以我们没必要
设三个数组,只需一个即可,i,j,k先都指向开始,之后依次后移即可。
这样我们思路又回到了最初,接下来看一下代码怎么写吧!
听视频讲解指南: 丑数讲解.
代码:
class Solution {
public:
int getUglyNumber(int n) {
vector<int> uglynum(1, 1);
int i = 0, j = 0, k = 0;
while(--n)//循环 n - 1 次,因为第一个数 1 已知(n-- 循环 n 次)
{
int t = min(uglynum[i] * 2, min(uglynum[j] * 3, uglynum[k] * 5));
uglynum.push_back(t);
if(uglynum[i] * 2 == t) i ++;
if(uglynum[j] * 3 == t) j ++;
if(uglynum[k] * 5 == t) k ++;
}
return uglynum.back();
}
};
没想到程序如此简单!
相似题目
思路跳跃相似题目链接: 把数组排成最小的数 刷题链接.
2,二进制求和
题目链接: 二进制求和 刷题链接.
给你两个二进制字符串,返回它们的和(用二进制表示)。输入为 非空 字符串且只包含数字 1 和 0。
示例 1:
输入: a = “11”, b = “1”
输出: “100”
示例 2:
输入: a = “1010”, b = “1011”
输出: “10101”
提示:
每个字符串仅由字符 ‘0’ 或 ‘1’ 组成。
1 <= a.length, b.length <= 10^4
字符串如果不是 “0” ,就都不含前导零。
class Solution {
public:
string addBinary(string a, string b) {
//思路:模拟列竖式二进制加法的步骤,从最后一位开始一个一个加,如果大于 2 就进一位
reverse(a.begin(),a.end());//高位在前低位在后不方便加和进位,所以先翻转字符串
reverse(b.begin(),b.end());
int t = 0;//表示进位
string ans;
for(int i = 0;i < a.size()||i < b.size(); i ++)
{
int va = i >= a.size()? 0 : a[i] - '0';//字符串转换,要是当前a[i]还有字符,转换为数字0/1,如果只剩b还有位数,那么取0,这样与b相应位相加还是b
int vb = i >= b.size()? 0 : b[i] - '0';
int n = va + vb + t;//当前位的和,加上进位
t = n/2, n %= 2;//每一位数不能大于2
ans += to_string(n);
}
if(t) ans += '1';
return string(ans.rbegin(), ans.rend());//翻转最终答案对应的字符串~
}
};
下面这个好像时间快一些~
class Solution {
public:
string addBinary(string a, string b) {
if (a.size() < b.size()) swap(a, b);
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
int t = 0;
string res;
int k = 0;
while (k < b.size())
{
t += a[k] - '0' + b[k] - '0';
res += to_string(t&1);//t=1或3时,t&1=1,t=2,t&1=0
t /= 2;//求进位
k ++ ;
}
while (k < a.size()) //处理a超出b的部分
{
t += a[k] - '0';
res += to_string(t&1);
t /= 2;
k ++ ;
}
if (t) res += '1';
reverse(res.begin(), res.end());
return res;
}
};
作者:yxc
链接:https://www.acwing.com/solution/content/151/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3,七进制数
刷题链接: leetcode 504-七进制数.
题目:
给定一个整数,将其转化为7进制,并以字符串形式输出。
示例 1:
输入: 100
输出: “202”
示例 2:
输入: -7
输出: “-10”
注意: 输入范围是 [-1e7, 1e7] 。
思路:模拟数学中进制转换
代码:
class Solution {
public:
string convertToBase7(int num) {
string ans,minus;
if(num < 0)
{
minus = '-';
num = - num;
}
if(!num) ans = '0';
while(num)
{
ans += to_string(num % 7);
num /= 7;
}
reverse(ans.begin(),ans.end());
return minus + ans;
}
};
4,Spiral Matrix(*)
题目链接: leetcode 54-Spiral Matrix.
Description:
Given a matrix of m * n elements (m rows, n columns), return all elements of the matrix in spiral order.
Example 1:
Input:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
Output: [1,2,3,6,9,8,7,4,5]
Example 2:
Input:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
Output: [1,2,3,4,8,12,11,10,9,5,6,7]
解析:
本题要求顺时针螺旋输出矩阵中的元素,假设 x 为行坐标,y 为列坐标。从(0,0)元素开始,先右移,直到超出矩阵范围,向下移动,之后向左,接着走到最左端,开始向上移动,遇到已经标记过的元素,则继续按照规律右移,下移,。。。。
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(matrix.empty()) return vector<int>();
int m = matrix.size(), n = matrix[0].size();//行, 列值
vector<vector<bool>> index(m, vector<bool>(n, false));//记录初始每个元素标志,为访问都为 false
int dx[4] = {0, 1, ,0, -1}, dy[4] = {1, 0, -1, 0};//按照右下左上的顺序定义行列值变化量
int x = 0, y = 0, d = 0;//定义位置 matrix[x][y] 与 移动距离 d
vector<int> ans;
for(int i = 0; i < m * n; i ++)
{
int a = x + dx[d], b = y + dy[d];//要移动到的下一个位置
if(a < 0 || a >= m || b < 0 || b >= n || index[a][b])//如果这个位置超出范围或者已访问
{ // 过,则更换移动方向,重定义ab
d = (d + 1) % 4;
a = x + dx[d], b = y + dy[d];
}
ans.push_back(matrix[x][y]);
index[x][y] = true;
x = a, y = b;
}
return ans;
}
};
5,两两交换链表中的节点
题目链接: leetcode 24 - 两两交换链表中的节点.
题目:
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.
题目分析:
设置一个虚拟头结点 tmp
tmp -> 1 -> 2 -> 3 -> 4
*p *a *b
p,a,b三个指针初始化分别指向虚拟头结点,第一个与第二个结点
1, p->next = b;//得到如下链表
tmp -> 2 -> 3 ->4
^
|
1
2, a->next = b->next;//得到如下链表
tmp -> 2 -> 3 ->4
^
|
1
3,b->next = a;
tmp -> 2 -> 1 -> 3 ->4
4,最后,令 p = a;//得到如下链表
tmp -> 2 -> 1 -> 3 -> 4
*p *a *b
之后交换下一对结点
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode *tmp = new ListNode(0);//新生成一个头结点
tmp -> next = head;
ListNode *p = tmp;
while(p && p -> next && p -> next -> next)
{
ListNode *a = p->next;
ListNode *b = a->next;
p->next = b;
a->next = b->next;
b->next = a;
p = a;
}
return tmp->next;
}
};
6,猜数字游戏
题目链接: leetcode - 299 猜数字游戏.
题目:
你在和朋友一起玩 猜数字(Bulls and Cows)游戏,该游戏规则如下:
你写出一个秘密数字,并请朋友猜这个数字是多少。
朋友每猜测一次,你就会给他一个提示,告诉他的猜测数字中有多少位属于数字和确切位置都猜对了(称为“Bulls”, 公牛),有多少位属于数字猜对了但是位置不对(称为“Cows”, 奶牛)。
朋友根据提示继续猜,直到猜出秘密数字。
请写出一个根据秘密数字和朋友的猜测数返回提示的函数,返回字符串的格式为 xAyB ,x 和 y 都是数字,A 表示公牛,用 B 表示奶牛。
xA 表示有 x 位数字出现在秘密数字中,且位置都与秘密数字一致。
yB 表示有 y 位数字出现在秘密数字中,但位置与秘密数字不一致。
请注意秘密数字和朋友的猜测数都可能含有重复数字,每位数字只能统计一次。
示例 1:
输入: secret = “1807”, guess = “7810”
输出: “1A3B”
解释: 1 公牛和 3 奶牛。公牛是 8,奶牛是 0, 1 和 7。
示例 2:
输入: secret = “1123”, guess = “0111”
输出: “1A1B”
解释: 朋友猜测数中的第一个 1 是公牛,第二个或第三个 1 可被视为奶牛。
说明: 你可以假设秘密数字和朋友的猜测数都只包含数字,并且它们的长度永远相等。
题解:
本题需要统计相同位置相同字符的个数,以及不同位置相同数字的个数(注意,这里两个字符串中位于不同位置但数字相同的每个数字的个数一样才能匹配。比如说,secret = ‘1240’,guess = ‘3211’,虽然guess中有两个1,但是只能有一个1 可以匹配,因为secret中只有一个1 )
具体做法看程序吧~
代码:
class Solution {
public:
string getHint(string secret, string guess) {
string ans;
int n = guess.size();
int a = 0, b = 0;
int ds[10] = {0}, dg[10] = {0};
for(int i = 0; i < n; i ++)
{
int s = secret[i] - '0', g = guess[i] - '0';
if(s == g) a ++;//计算 x 值
ds[s] ++, dg[g] ++;//记录secret 和 guess 中每个数字出现次数
}
for(int i = 0; i < 10; i ++) b += min(ds[i], dg[i]);//此时的b为两串中可以匹配的字符数的总和(位置可能不同)
b -= a;//减去相同位置相同字符的个数,得到不相同位置但是数字一样的个数
ans += to_string(a) + 'A' + to_string(b) + 'B';
return ans;
}
};
7, 神奇字符串
题目链接: leetcode-481 神奇字符串.
题目:
神奇的字符串 S 只包含 ‘1’ 和 ‘2’,并遵守以下规则:
字符串 S 是神奇的,因为串联字符 ‘1’ 和 ‘2’ 的连续出现次数会生成字符串 S 本身。
字符串 S 的前几个元素如下:S = “1221121221221121122 …”
如果我们将 S 中连续的 1 和 2 进行分组,它将变成:
1 22 11 2 1 22 1 22 11 2 11 22 …
并且每个组中 ‘1’ 或 ‘2’ 的出现次数分别是:
1 2 2 1 1 2 1 2 2 1 2 2 …
你可以看到上面的出现次数就是 S 本身。
给定一个整数 N 作为输入,返回神奇字符串 S 中前 N 个数字中的 ‘1’ 的数目。
**注意:**N 不会超过 100,000。
示例
输入:6
输出:3
解释:神奇字符串 S 的前 6 个元素是 “12211”,它包含三个 1,因此返回 3。
这道题虽然很绕,但是明白数字如何生成以后,就很简单了,直接模拟就行。从第一位开始,1生成一个 1,2生成两个2,即 122,之后,第三位2生成两个1,得到 1 22 11,继续,第四位是1,生成一个2,得到 1 22 11 2。。。。
代码如下:
class Solution {
public:
int magicalString(int n) {
string s = "122";
for(int i = 2, j = 1; s.size() <= n; i ++, j = 3 - j)
{
for(int k = 0; k < s[i] - '0'; k ++) s += to_string(j);
}
int ans = 0;
for(int i = 0; i < n; i ++)
{
if(s[i] == '1')
ans ++;
}
return ans;
}
};
8,简化路径
题目链接: leetcode-71 简化路径.
题目:
以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。
在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (…) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径
请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。
示例 1:
输入:"/home/"
输出:"/home"
解释:注意,最后一个目录名后面没有斜杠。
示例 2:
输入:"/…/"
输出:"/"
解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。
示例 3:
输入:"/home//foo/"
输出:"/home/foo"
解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。
示例 4:
输入:"/a/./b/…/…/c/"
输出:"/c"
示例 5:
输入:"/a/…/…/b/…/c//.//"
输出:"/c"
示例 6:
输入:"/a//bc/d//././/…"
输出:"/a/b/c"
题解:
首先推荐一个知识点链接,便于详细理解题意
链接: Linux / Unix中的绝对路径 vs 相对路径.
之后推荐 y 总题解
链接: yxc 题解.
代码(附注释)
class Solution {
public:
string simplifyPath(string path) {
path += '/';//为了后面便于处理结尾情况(结尾无 / 时),可以加上判断条件 if(path.back() != '/')
string s, ans;
for(auto &c : path)//这里 c 前可以不加 & ,但是加上速度更快了
{
if(ans.empty()) ans += '/';//输出答案先加上绝对路径标志
else if(c != '/') s += c;//若非 /,则为两 / 之间的字符,暂时保存在 s 中
else{
if(s == "..")
{
if(ans.size() > 1)//排除/../的情况,当为/../时,ans不做处理
{
ans.pop_back();//先去掉..前的/,之后返回上一级
while(ans.back() != '/') ans.pop_back();
}
}
else if(s != "." && s != "")//当s字符非 . 也不是空串(空串说明上一个字符可能也是/)
{ //时,将 s 加到答案路径中
ans += s + '/';
}
s = "";//s置空,保存下一次两 / 之间字符
}
}
if(ans.size() > 1) ans.pop_back();//当ans非 / 时,弹出最后的 /
return ans;
}
};
9,整数转罗马数字
题目链接: leetcode-12 整数转罗马数字.
题目:
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。
示例 1:
输入: 3
输出: “III”
示例 2:
输入: 4
输出: “IV”
示例 3:
输入: 9
输出: “IX”
示例 4:
输入: 58
输出: “LVIII”
解释: L = 50, V = 5, III = 3.
示例 5:
输入: 1994
输出: “MCMXCIV”
解释: M = 1000, CM = 900, XC = 90, IV = 4.
题解:
链接: y总题解分享.
本题只要列出全部可能情况即可,之后按从大到小的顺序去一个个减。具体看题解~
代码:
class Solution {
public:
string intToRoman(int num) {
int values[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
string roms[] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
string ans;
for(int i = 0; i < 13; i ++)
{
while(num >= values[i])
{
num -= values[i];
ans += roms[i];
}
}
return ans;
}
};
10,文本左右对齐
题目链接: leetcode-68 文本左右对齐.
题目:
给定一个单词数组和一个长度 maxWidth,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。
你应该使用“贪心算法”来放置给定的单词;也就是说,尽可能多地往每行中放置单词。必要时可用空格 ’ ’ 填充,使得每行恰好有 maxWidth 个字符。
要求尽可能均匀分配单词间的空格数量。如果某一行单词间的空格不能均匀分配,则左侧放置的空格数要多于右侧的空格数。
文本的最后一行应为左对齐,且单词之间不插入额外的空格。
说明:
单词是指由非空格字符组成的字符序列。
每个单词的长度大于 0,小于等于 maxWidth。
输入单词数组 words 至少包含一个单词。
示例1:
输入:
words = [“This”, “is”, “an”, “example”, “of”, “text”, “justification.”]
maxWidth = 16
输出:
[
“This is an”,
“example of text”,
"justification. "
]
示例 2:
输入:
words = [“What”,“must”,“be”,“acknowledgment”,“shall”,“be”]
maxWidth = 16
输出:
[
“What must be”,
"acknowledgment ",
"shall be "
]
解释: 注意最后一行的格式应为 "shall be " 而不是 “shall be”,
因为最后一行应为左对齐,而不是左右两端对齐。
第二行同样为左对齐,这是因为这行只包含一个单词。
示例 3:
输入:
words = [“Science”,“is”,“what”,“we”,“understand”,“well”,“enough”,“to”,“explain”,
“to”,“a”,“computer.”,“Art”,“is”,“everything”,“else”,“we”,“do”]
maxWidth = 20
输出:
[
“Science is what we”,
“understand well”,
“enough to explain to”,
“a computer. Art is”,
“everything else we”,
"do "
]
题目解析:
本题细究不难,但是很繁琐,需要考虑各种情况。还是放y总题解在此~
链接: 文本左右对齐题解.
代码及注释:
class Solution {
public:
string space(int x)
{
string spa;
while(x--) spa += ' ';
return spa;
}
vector<string> fullJustify(vector<string>& words, int maxWidth) {
vector<string> ans;
for(int i = 0; i < words.size();)
{
int j = i + 1, s = words[i].size(), rs = words[i].size();
//先求出每一行最多能放几个单词,以及能放的单词的总长度
while(j < words.size() && s + 1 + words[j].size() <= maxWidth)
{
s += 1 + words[j].size();
rs += words[j].size();
j ++;
}
rs = maxWidth - rs;//该行空格总数
string tmp = words[i];
//分情况处理,首先考虑最后一行的情况(第一行也可能是最后一行)
if(j == words.size())
{
for(i ++; i < j; i ++)
tmp += ' ' + words[i];
while(tmp.size() < maxWidth) tmp += ' ';
}
else if(j - i == 1) //该行只有一个单词的情况
{
tmp += space(rs);
}
else{ //该行有多个单词,需要均分空格,若不能均分,左边的要多于右边的
int m = rs / (j - i - 1);//j 为下一行首单词的序号,j-i-1为该行单词间空隙数
int n = rs % (j - i - 1);//均分后多出的单词数
i ++;
for(int k = 0; i < j; i ++, k ++)
{
//不能均分时,左边单词间空格每个比右边多一个
tmp += space(m + (k < n)) + words[i];
}
}
i = j;
ans.push_back(tmp);
}
return ans;
}
};
模拟专题一共十道题,终于刷完了。要开下一个专题啦 ~ 撒花