文章目录
- 1. JZ74 和为S的连续正数序列
- 暴力解法
- 滑动窗口(双指针)
- 2. JZ57 和为S的两个数字
- 3. JZ58 左旋转字符串
- 4. JZ73 翻转单词序列
- 5. JZ61 扑克牌顺子
- 6. JZ62 孩子们的游戏(圆圈中最后剩下的数)
- 迭代 模拟
- 递归 约瑟夫环问题 找规律
- 7. JZ64 求1+2+3+...+n
- 8. JZ65 不用加减乘除做加法
- 9. 把字符串转换成整数
- 10. 数组中重复的数字
- 哈希表
- 原地解法
1. JZ74 和为S的连续正数序列
暴力解法
class Solution {
public:vector<vector<int> > FindContinuousSequence(int sum) {if(sum == 0) return vector<vector<int>>{};for(int i=1; i<sum; i++){int temp = 0;path.clear();for(int j=i; j<sum; j++){path.push_back(j);temp += j;if(temp == sum) result.push_back(path);if(temp > sum) break;}//result.push_back(path);}return result;}vector<int> path;vector<vector<int> > result;
};
至少是两个数,优化遍历的次数,数学公式计算
class Solution {
public:vector<vector<int> > FindContinuousSequence(int sum) {//暴力解法 写法2for (int n = sqrt(2 * sum); n >= 2; --n) {if (((n & 1) == 1 && sum % n == 0) || (sum % n * 2 == n)) {vector<int> res;//j用于计数,k用于遍历求值for (int j = 0, k = sum / n - (n - 1) / 2; j < n; j++, k++)//注意看k的求法res.push_back(k);result.push_back(res);}}return result;}vector<int> path;vector<vector<int> > result;
};
滑动窗口(双指针)
class Solution {
public:vector<vector<int> > FindContinuousSequence(int sum) {//滑动窗口int left = 1, right = 2, tempsum = 3;while(left < right){if(tempsum == sum)//保存结果{path.clear();for(int i=left; i<=right; i++)path.push_back(i);result.push_back(path);}if(tempsum < sum)//窗口右边界外扩 右移{right++;tempsum += right;}else //tempsum > sum 窗口左边界右移;tempsum = sum 窗口左边界右移 开始下一轮子结果{tempsum -= left;//新的边界值left++;}}return result;}vector<int> path;vector<vector<int> > result;
};
2. JZ57 和为S的两个数字
class Solution {
public:vector<int> FindNumbersWithSum(vector<int> array,int sum) {if(array.size() == 0) return vector<int>{};// 滑动窗口 双指针 写法1/*int left = 0, right = array.size()-1;while(left < right){if(sum - array[left] == array[right]){result.push_back(array[left]);result.push_back(array[right]);break;}if(sum - array[left] < array[right]) right--;else left++;}*/// 滑动窗口 双指针 写法2int left = 0, right = array.size()-1, tempsum = 0;while(left < right){tempsum = array[left] + array[right];if(tempsum == sum){result.push_back(array[left]);result.push_back(array[right]);return result;}else if(tempsum > sum) right--;else left++;}return result;}vector<int> result;
};
3. JZ58 左旋转字符串
先局部反转 cbafedZYX,然后整体反转XYZdefabc
#include <algorithm>
#include <type_traits>
class Solution {
public:string LeftRotateString(string str, int n) {int len = str.size();if(len <= 1) return str;else if(len < n) n %= len;reverse(str, 0, n-1);reverse(str, n, len-1);reverse(str, 0, len-1);return str;}void reverse(string& str, int start, int end){for(int i=start, j=end; i<j; i++, j--){swap(str[i], str[j]);}}
};
4. JZ73 翻转单词序列
class Solution {
public:string ReverseSentence(string str) {if(str.size() <= 1) return str;string result = "", temp = "";for(int i=0; i<str.size(); ++i){if(str[i] == ' ')//遇到空格 把temp和之前的结果拼接{result = ' ' + temp + result;//倒序拼接temp = "";//更新 存下一个单词}else temp += str[i];//没有遇到空格 把整个字符串都存在temp中}if(temp.size()) // 如果temp还剩有单词 没有这一步的话示例1会返回" am a nowcoder."result = temp + result;return result;}
};
5. JZ61 扑克牌顺子
- 排序,统计0的个数,统计所有相邻数字间隔总数。
- 如果0的个数 = 相邻数字间隔总数,就是顺子;如果出现对子,则不是顺子。
- 统计所有相邻数字间隔总数时,如果两个数字间隔为1,不计数。
class Solution {
public:bool IsContinuous(vector<int>& numbers) {if(numbers.size() < 5) return false;sort(numbers.begin(), numbers.end());int numOfZero = 0, numOfInner = 0;//如果是连续的数 排序后 两两之间间隔为1//比较0的个数 是否等于 统计的间隔长度 若相邻/间隔为1 视间隔长度为0for(int i=0; i<numbers.size() - 1; i++){if(numbers[i] == 0) numOfZero++;else if(numbers[i] == numbers[i+1]) return false;//对子 不可能是顺子else{numOfInner += numbers[i+1] - numbers[i] - 1;//相邻数的间隔长度为0 间隔长度累加//numOfInner += numbers[i+1] - numbers[i] -1;//这里千万注意要减去1}}if(numOfZero >= numOfInner) return true;return false;}
};
6. JZ62 孩子们的游戏(圆圈中最后剩下的数)
迭代 模拟
class Solution {
public:int LastRemaining_Solution(int n, int m) {if(n < 1 || m < 1) return -1;vector<int> numbers(n,0);//标记哪个小朋友拿过礼物int index = -1,step = 0, count = n;while (count > 0) {index++;if(index >= n) index = 0;//模拟环 从第一个小朋友开始if(numbers[index] == -1) continue;step++;//报数if(step == m){numbers[index] = -1;step = 0;//下一个小朋友从头开始报数count--;//已经有一个小朋友拿过礼物了 环中小朋友数量-1}}return index;}
};
递归 约瑟夫环问题 找规律
也就是说,从f(n-1, m)当前位置来看,f(n, m)在f(n-1, m)的后m个位置。n个数字中最后剩下的数字,即去掉的数字为(m-1)%n,下一次报数是从第 m%n 个数字开始。因此,f(n-1) 和 f(n)的关系如下:
f(n-1) | f(n) |
---|---|
0 | m%n |
1 | (m+1)%n |
… | … |
n-2 | (m-2)%n |
两种写法
#include <any>
class Solution {
public:int LastRemaining_Solution(int n, int m) {//递归 约瑟夫环问题 写法1if(n <= 0) return -1;return (LastRemaining_Solution(n-1, m) + m) % n;//递归 约瑟夫环问题 写法2/*if(n <= 0 || m < 0) return -1;int result = 0;for(int i=2; i<=n; ++i){result = (result + m) % i;}return result;*/}
};
7. JZ64 求1+2+3+…+n
不能用if、switch、?:等操作时,利用逻辑与的短路特性实现递归终止,也就是通过判断n是否为0来控制递归是否终止。
n递减,并且通过递归实现倒序累加和。当n = 0时,递归结束;n > 0时,递归,倒序累加求和。因此判断条件是 (n > 0) && ((result += Sum_Solution(n-1)) > 0);
class Solution {
public:int Sum_Solution(int n) {int result = n;(n > 0) && ((result += Sum_Solution(n-1)) > 0);return result;}
};
8. JZ65 不用加减乘除做加法
两个数异或:相当于每一位相加,而不考虑进位,产生非进位的加和结果;
两个数相与,并左移一位:相当于求得进位;
将上述两步的结果相加,什么时候进位制为0也就说明两个数相加到了最终点,也就计算结束了。
5-101,7-111
第一步:相加各位的值,不算进位,得到010,二进制每位相加就相当于各位做异或操作,101^111。
第二步:计算进位值,得到1010,相当于各位做与操作得到101,再向左移一位得到1010,(101&111)<<1。
第三步重复上述两步, 各位相加 010^1010=1000,进位值为100=(010&1010)<<1。
继续重复上述两步:1000^100 = 1100,进位值为0,跳出循环,1100为最终结果。
class Solution {
public:int Add(int num1, int num2) {//位运算 迭代int add = num2;// add表示进位值int sum = num1; // sum表示总和 while(add != 0){int temp = sum ^ add;// 将每轮的无进位和与进位值做异或求和add = (sum & add) << 1;// 进位值是用与运算产生的sum = temp;// 更新sum为新的和}return sum;}
};
9. 把字符串转换成整数
要注意的两点,1.越界处理,2.正负号个数处理,越界处理用下面这种写法的时候,怎么改都只能通过95.5%的用例,最后还有两组用例没过。换成练习模式才知道原来是过不了“±5”这种例子,真的无语,加了一个统计正负号个数的判断。
#include <climits>
class Solution {
public:int StrToInt(string s) {int result = 0;int index = 0;int len = s.size();//下标索引 字符串长度//1.剔除特殊情况if(len == 0) return 0;//2.如果有前导空格就去掉,让指针往后偏移到非空格处while(index < len){if(s[index] == ' ') index++;else break;}//如果全是空格,直接返回if(index >= len) return 0;//3.处理正负号int flag = 1;//正负号标志位 正数1 负数-1int flagnum = 0;//如果第一个符号是正负号的情况if(s[index] == '+'){index++;flagnum++;}if(s[index] == '-'){index++;flag = -1;flagnum++;}//最多有一个正 / 负号if(flagnum > 1) return 0;//4.有效数字部分while(index < len){char c = s[index];//后续非法字符,截断if(c < '0' || c > '9') break;//处理越界 这种写法是结果和当前遍历的数字都进行越界处理if(result > INT_MAX / 10 || (result == INT_MAX / 10 && (c - '0') > INT_MAX % 10)) return INT_MAX;if(result < INT_MIN / 10 || (result == INT_MIN / 10 && (c - '0') > -(INT_MIN % 10))) return INT_MIN;result = result * 10 + flag * (c - '0');/*cout << s[index] - '0'<<endl;cout << flag * (s[index] - '0') <<endl;cout << "result:" << result <<endl;*/index++;}return result;}
};
10. 数组中重复的数字
哈希表
使用了循环,循环次数为数组大小,时间复杂度为O(N)。引入额外的集合空间,空间复杂度为O(N),最坏的情况是数组中的元素都不重复。
- 用map
#include <string>
#include <unordered_map>
class Solution {public:int duplicate(vector<int>& numbers) {unordered_map<int, int> m;for(int i=0; i<numbers.size(); i++){m[numbers[i]]++;if(m[numbers[i]] >= 2) return numbers[i]; }return -1;}
};
- 用vector,降低内存复杂度
#include <string>
#include <unordered_map>
class Solution {public:int duplicate(vector<int>& numbers) {//哈希表 数组 直接统计频率 降低内存复杂度vector<int> result(numbers.size(), 0);for(int i=0; i<numbers.size(); i++){result[numbers[i]]++;if(result[numbers[i]] >= 2) return numbers[i];}return -1;}
};
原地解法
- 如果numbers[index] = index,第index个位置的元素也可以用numbers[numbers[index]]来表示
- 使用了循环,循环次数为数组大小,时间复杂度为O(N)。没有引入额外的集合空间,空间复杂度为O(1)
#include <string>
#include <unordered_map>
class Solution {public:int duplicate(vector<int>& numbers) {//原地解法int index = 0;while (index < numbers.size()) {if(numbers[index] == index)//第index个位置的元素=index{index++;continue;}else //第index个位置的元素≠index{//如果第index个位置的元素numbers[index] = index 说明元素重复if(numbers[index] == numbers[numbers[index]]) return numbers[index];//重复元素 返回else swap(numbers[index], numbers[numbers[index]]);//交换位置}}return -1;}
};