目录
11 二进制中的1的个数3/11
参考:https://www.cnblogs.com/iwiniwin/p/11058255.html
【位运算】
1 与1相与完,1左移一位
class Solution {
public:
int NumberOf1(int n) {
int unit = 1, count = 0;//初始化unit为000(中间省略32-4个0)1
while(unit != 0){//退出循环条件:unit左移到33位溢出,32位数扫描了一遍
if((unit & n) != 0)//【易错:&优先级低于!=】相与不等于0,说明对应位置为1
count++;//计数加一
unit <<= 1;//将unit左移
}
return count;
}
};
2 与n-1相与,相当于把最右边的1变成0,有多少个1,就能变多少次【优秀】
class Solution {
public:
int NumberOf1(int n) {
//对于数值n,将n - 1后再和n相与,
//得到的值相当于将n从右边数的第一个1变成0。
//n的二进制表示中有多少个1,就能变多少次。
//实现代码如下,时间复杂度优化为O(n中1的个数)
int cnt = 0;
while( n != 0){
cnt++;
n = (n - 1) & n;
}
return cnt;//【易错,竟然把这里写成了n】
}
};
12 数值的整数次方
参考:https://www.nowcoder.com/questionTerminal/1a834e5e3e1a4b7ba251417554e07c00?f=discussion
class Solution {
public:
double Power(double base, int exponent) {
if(base == 0.0)//基数为0,结果为0
return 0.0;
double res = 1.0;
int e = exponent > 0 ? exponent: -exponent;//次方为正还是负
for(int i = 1; i <= e; i++){
res *= base;//base相乘e次
}
return exponent > 0? res: 1/res;//根据次方正负输出结果
}
};
19 顺时针打印矩阵
参考:https://www.cnblogs.com/silentteller/p/11918473.html
参考:https://blog.csdn.net/qq_43461641/article/details/90199866
思路:求矩阵行数row,列数col
从左到右,从上到下,从右到左,从下到上
走完一圈之后行和列-1,到达对角线的下一个,
关键在于判断边界:
从左到右,列数<=col
从上到下,行数<=row
从右往左,列数<=col,行数<=row
从下往上,列数<=col,行数<=row
27 字符串的排列【暂时有疑问】
【全排列】【递归】
参考:https://blog.csdn.net/lr604844811/article/details/99710445
参考:https://blog.csdn.net/lr604844811/article/details/99710445
最小的k个数
参考:https://blog.csdn.net/daaikuaichuan/article/details/85239149
参考:https://www.cnblogs.com/silentteller/p/11959368.html
维护一个最大堆;
利用优先级队列实现最大堆;
【最大堆】【优先级队列】
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
if(input.size() == 0 || k == 0 || k > input.size())
return res; //输入数组为空,或k==0 或k超出数组范围
for(int num:input){
if(q.size() < k)
q.push(num);//将前k个数存入最大堆
else{//k之后元素到来时
if(num < q.top()){//将它与堆顶(最大值)做比较
q.pop();//小于栈顶元素,删除栈顶元素
q.push(num);//将新元素压入堆中
}
}
}
while(!q.empty()){//循环终止条件:队列取出了所有元素
res.push_back(q.top());//将队列里元素保存到vector中
q.pop();//删除堆顶元素
}
//reverse(res.begin(), res.end());
return res;
}
private:
vector<int> res;
priority_queue<int> q;//优先级队列模拟堆来处理。
};
整数中1出现的次数
参考:https://www.cnblogs.com/aimi-cn/p/11510770.html
参考:https://www.cnblogs.com/wmx24/p/8901808.html
引用:
总结一下以上的算法,可以看到,当计算右数第 i 位包含的 1 的个数时:
- 取第 i 位左边(高位)的数字x,乘以 10的i−1次方,得到基础值 a。
- 取第 i 位数字,计算修正值:
- 如果大于 1,则结果为 a+10i−1,(x+1)*10^(i-1)。
- 如果小于 1,则结果为 a。
- 如果等于 1,则取第 i 位右边(低位)数字,设为 b,最后结果为 a+b+1。
关于a+8的解释:https://www.nowcoder.com/questionTerminal/bd7f978302044eee894445e244c7eee6?f=discussion
+8是在当前位>=2的时候,当前位出现1的次数与高位数+1相关。相反,当前位<=2时,当前位出现1的次数与高位数和余数相关。
class Solution {
public:
int NumberOf1Between1AndN_Solution(int n)
{
int count = 0;
for (int i = 1; i <= n; i *= 10) {//i每次*10
int a = n / i,b = n % i;//
count += (a + 8) / 10 * i + ((a % 10 == 1) ? b + 1 : 0);
}
return count;
}
};
33 丑数3/15
参考:https://blog.csdn.net/qq_38790716/article/details/89052721
【穷举】
思路:
维护三个队列,分别为2的倍数,3的倍数,5的倍数,然后从三个队列中选择最小值;
如果从2的队列选择最小值,则指针向下一个移动(t++),模拟出队的操作
class Solution {
public://
int GetUglyNumber_Solution(int index) {
if (index < 7)//丑数序列为1234567
return index;
vector<int> res(index);//【易错】必须初始化数组大小index个,否则返回时候会出现数组越界问题
res[0] = 1;
int t2 = 0, t3 = 0, t5 = 0, i;
for (i = 1; i < index; ++i)//【易错】i从1开始
{
//从三个队列中选择最小值
res[i] = min(res[t2] * 2, min(res[t3] * 3, res[t5] * 5));
//判断哪一个队列的元素出队,然后t++实现出队操作
if (res[i] == res[t2] * 2)t2++;
if (res[i] == res[t3] * 3)t3++;
if (res[i] == res[t5] * 5)t5++;
}
return res[index - 1];
}
};
34 第一个只出现一次的字符
参考:https://www.nowcoder.com/questionTerminal/1c82e8cf713b4bbeb2a5b31cf5b0417c?f=discussion
思路:利用哈希表。
字母a-z链接:对应的ASCII码值为97-122,A-Z对应的ASCII码为65-90,每个字母的key=int(word)-65,
数组中具体记录的内容是该字母出现的次数,最终遍历一遍字符串,找出第一个数组内容为1的字母就可以了,时间复杂度为O(n)
class Solution {
public:
int FirstNotRepeatingChar(string str) {
for(int i = 0; i < str.size(); ++i){
m[str[i]]++;//遍历字符串,在hash表中统计各字母出现次数
}
for(int i = 0; i < str.size(); ++i){
if(m[str[i]] == 1)
return i;//扫描直接访问hash表获得次数
}
return -1;
}
private:
map<char, int> m;//哈希表,key为字母,value为字母出现次数
};
41 和为S的正数序列
参考:https://www.nowcoder.com/questionTerminal/c451a3fd84b64cb19485dad758a55ebe?f=discussion
参考:https://www.cnblogs.com/silentteller/p/12061071.html
思路:左右两端点left、right,由于序列连续且等差,则求和公式为sum =(right +left)*(left right之间个数)/2
如果sum= 目标值,将当前序列保存到临时数组,临时数组保存到二维向量结果集res中;right++
如果sum >目标值,left++
如果sum < 目标值,right++
直到所有的都求出来
还有种思路是求中间值和序列长度,也是根据等差数列求和的性质,有点麻烦
【滑动窗口】【穷举】【求和】
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
if(sum == 0)
return res;//【易错】一定要注意返回值类型
int left = 1;//左端起点
int right = 2;//右端起点,相当于动态窗口的两边,根据其窗口内的值的和来确定窗口的位置和大小
while(left < right){
//由于序列是连续的,差为1,求和公式是(a0+an)*n/2
if((left + right) *(right - left + 1) / 2 == sum){
//和与目标值相等,那么就将窗口范围的所有数添加进临时一维向量
vector<int> temp;//保存满足条件的一个序列
for(int i = left; i <= right; ++i)
temp.push_back(i);
res.push_back(temp);//保存到结果集
right++;//右侧端点往右挪
}
else if((left + right) *(right - left + 1) / 2 > sum){
//如果当前窗口内的值之和大于sum,那么左边窗口右移一下
left++;
}
else{
//如果当前窗口内的值之和小于sum,那么右边窗口右移一下
right++;
}
}
return res;
}
private:
vector<vector<int> > res;//保存结果的二维向量
};
42 和为S的两个数字
参考:https://www.cnblogs.com/silentteller/p/12061216.html
思路:左指针left,指向第一个元素;右指针right,指向末尾元素;和sum= left + right ;
如果sum ==目标值,输出left right
如果sum < 目标值,left++
如果sum > 目标值, right--
当left right距离越远的时候乘积越小。联想1*15 和8*8,联想小学时候,边长L相等面积S最大的时候边=根号S
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
if(array.size() == 0)
return res;
int left = 0;//左指针初始化指向第一个元素
int right = array.size()-1;//右指针初始化指向最后一个元素
while(left < right){//循环终止条件:左右指针相遇
if(array[left] + array[right] == sum){
//和为目标值,将left元素值和right元素值加入结果数组
res.push_back(array[left]);
res.push_back(array[right]);
break;//出口
}
else if(array[left] + array[right] > sum){
//和大于目标值,右指针左移
right--;
}
else{//和小于目标值,左指针右移
left++;
}
}
return res;
}
private:
vector<int> res;//保存结果的数组
};
43 左旋转字符串
参考:https://www.cnblogs.com/silentteller/p/12069702.html
参考:https://www.nowcoder.com/questionTerminal/12d959b108cb42b1ab72cef4d36af5ec?f=discussion
思路:
str: abc12345 循环左移3位后输出12345abc
先整体翻转变成54321cba
在找到len - n(8-3 = 5)的地方分割成两部分,分别翻转这两部分,即分别翻转54321和cba变成12345和abc
【reverse】
节选一下《STL源码剖析一书》关于reverse的概念:将序列[first,last)的元素在原容器中颠倒重排。
左开右闭区间,拿54321cba举例子,str.begin()指向第一个元素5, str.size() - n指向c,但是右半边闭区间,所以旋转的是54321。str.end()指向a后面的空字符,根据左闭右开区间,旋转cba。
class Solution {
public:
string LeftRotateString(string str, int n) {
reverse(str.begin(), str.end());
reverse(str.begin(), str.begin() + str.size() - n);
reverse(str.begin() + str.size() - n, str.end());
return str;
}
};
或者自己写reverse
链接:https://www.nowcoder.com/questionTerminal/12d959b108cb42b1ab72cef4d36af5ec?f=discussion
来源:牛客网
class Solution {
public:
void fun(string &s,int start,int end)
{
char temp;
while(start<end)
{
temp=s[start];
s[start]=s[end];
s[end]=temp;
start++;
end--;
}
}
string LeftRotateString(string str, int n) {
int len=str.length();
if(0==len || 0==n)
return str;
string &temp=str;
fun(temp,0,n-1);
fun(temp,n,len-1);
fun(temp,0,len-1);
return str;
}
};
字符串拼接
链接:https://www.nowcoder.com/questionTerminal/12d959b108cb42b1ab72cef4d36af5ec?f=discussion
来源:牛客网
class Solution {
public:
string LeftRotateString(string str, int n) {
int len = str.length();
if(len == 0) return "";
n = n % len;//移动n位对字符串长度取余数
str += str;//str扩大到原来两倍
return str.substr(n, len);//从取余的地方去len(字符串长度)的子字符串
}
};
44 翻转单词顺序列
参考:https://www.cnblogs.com/silentteller/p/12069918.html
思路:
翻转整个句子,然后,依次翻转每个单词。
依据空格来确定单词的起始和终止位置
【注意】结尾单词单独处理是因为结尾没有空字符,不能再根据str[index] == ' '判断最后一个单词的结尾,而且最后一个单词翻转的范围也因此改变。这里就体现了STL左开右闭区间的优越性。
【reverse】
class Solution {
public:
string ReverseSentence(string str) {
myReverse(str, 0, str.size()-1);//将整个个字符串翻转一次
int index = 0;//单词长度
int l = 0;//单词左端开始的地方
while(index < str.size()){
if(str[index] == ' '){//根据空格位置划分单词,将单词翻转
myReverse(str, l, index-1);
l = index + 1;
}
else if(index == str.size()-1){最后一个单词单独进行反转
myReverse(str, l, index);
break;//【出口】最后一个单词翻转完毕
}
index++;//不是空格,index继续右移
}
return str;
}
void myReverse(string &str, int l, int r){
if(l > str.size()-1 || r < 0)
return;
while(l < r){
swap(str[l], str[r]);
l++;
r--;
}
}
};
45 扑克牌顺子
参考:https://www.cnblogs.com/silentteller/p/12073082.html
必须满足两个条件
1. 除0外没有重复的数
2. max - min < 5
思路:
1、排序
2、计算所有相邻数字间隔总数
3、计算0的个数
4、如果2、3相等,就是顺子
5、如果出现对子,则不是顺子
class Solution {
public:
bool IsContinuous( vector<int> numbers ) {
if(numbers.size() == 0)
return false;
sort(numbers.begin(), numbers.end());//排序
int countZero = 0;
for(auto i:numbers){
if(i == 0)
countZero++;//找出数组中0的个数
}
int left = countZero;//左指针指向0之后的第一个元素
int right = left+1;//右指针指向左指针之后的那个元素
int countGap = 0;//gap保存左右两个元素之间的差值
while(right < numbers.size()){//循环终止条件:到达数组结尾
if(numbers[left] == numbers[right])
return false;//出口A,出现相同数字,对子
countGap += (numbers[right] - numbers[left] - 1);//【易错】求差值
left = right;//左右指针各自往右挪一位
right++;
}
return countGap <= countZero ? true : false;//出口B,如果gap<=0的个数,可以成为顺子,否则不行
}
};
另一种思路:
链接:https://www.nowcoder.com/questionTerminal/762836f4d43d43ca9deb273b3de8e1f4?f=discussion
来源:牛客网
//考略到顺子的特性,最大值和最小值之差绝对为4,
//然而又有大小王的存在,所以a[4]-a[jokers] <=4
class Solution {
public:
bool IsContinuous( vector<int> numbers ) {
int len=numbers.size();
if(len!=5) return false;
sort(numbers.begin(),numbers.end());
int jokers=0;//计算王的数目
for(int i=0;i<5&&numbers[i]==0;i++){
jokers++;
}
if(jokers>4) return false;
for(int i=jokers+1;i<5;i++){
if(numbers[i]==numbers[i-1])//判断对子的存在
return false;
}
int dis=numbers[4]-numbers[jokers];
if(dis<=4) return true;
return false;
}
};