17. 电话号码的字母组合
class Solution {
public:
vector<string> letterCombinations(string digits) {
vector<string> list={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
queue<string> q;
vector<string> v;
if(digits.length() == 0) return v;
string s = list[digits[0]-'0'];
for(int j=0;j<s.length();j++){
string str = s.substr(j,1);
q.push(str);
}
int epoc1 = 0;
for(int i=1;i<digits.length();i++){
cout<<digits[i]<<" "<<s<<endl;
int size = q.size(); //用size控制每一个数字对应的一层字母
if(epoc1 == 0) epoc1 = 1;
for(int t = 0;t < size;t++){ //外层是队列遍历
string tmp = q.front();q.pop();
if(!epoc1) v.push_back(tmp);
s = list[digits[i]-'0'];
for(int j=0;j < s.length();j++){ //内层是对这一层字母的遍历
cout<<s[j]<<endl;
q.push(tmp + s[j]);
}
}
}
while(!q.empty()){
v.push_back(q.front());q.pop();
}
return v;
}
};
旋转数组让用三种方法
方法1:就正常
方法二:
方法三:如下:
class Solution {
public:
void rotate(vector<int>& nums, int k) {
int len = nums.size();
int Gcd = gcd(k,len); //此次旋转只有Gcd个起点
k = k % len; //缩小k;
for(int start=0;start<Gcd;start++){
int current = start;
int prev_value = nums[start];
do{
int next = (current + k) % len;
swap(nums[next],prev_value);
current = next;
}while(start != current);
}
}
};
TOP4. 寻找两个正序数组的中位数
之前学过的马车法
#include <stdio.h>
#include <vector>
using namespace std;
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
int m = nums2.size();
if (n > m) //保证数组1一定最短
{
return findMedianSortedArrays(nums2, nums1);
}
// Ci 为第i个数组的割,比如C1为2时表示第1个数组只有2个元素。
//LMaxi为第i个数组割后的左元素。RMini为第i个数组割后的右元素。
int LMax1, LMax2, RMin1, RMin2, c1, c2, lo = 0, hi = 2 * n;
//我们目前是虚拟加了'#'所以数组1是2*n长度
while (lo <= hi) //二分
{
c1 = (lo + hi) / 2; //c1是二分的结果
c2 = m + n - c1;
LMax1 = (c1 == 0) ? INT_MIN : nums1[(c1 - 1) / 2];
RMin1 = (c1 == 2 * n) ? INT_MAX : nums1[c1 / 2];
LMax2 = (c2 == 0) ? INT_MIN : nums2[(c2 - 1) / 2];
RMin2 = (c2 == 2 * m) ? INT_MAX : nums2[c2 / 2];
if (LMax1 > RMin2)
hi = c1 - 1;
else if (LMax2 > RMin1)
lo = c1 + 1;
else
break;
}
return (max(LMax1, LMax2) + min(RMin1, RMin2)) / 2.0;
}
};
int main(int argc, char *argv[])
{
vector<int> nums1 = { 2,3, 5 };
vector<int> nums2 = { 1,4,7, 9 };
Solution solution;
double ret = solution.findMedianSortedArrays(nums1, nums2);
return 0;
}
84. 柱状图中最大的矩形(单调栈/暴力)
#include<bits/stdc++.h>
using namespace std;
int fun(vector<int>& heights){
stack<int> s;
int size = heights.size();
int res = 0;
for(int i=0;i<size;i++){
while(!s.empty() && heights[s.top()] > heights[i]){
int length = heights[s.top()];
s.pop();
int weight = i;
if(!s.empty()){
weight = i - s.top() - 1;
}
res = max(res, length*weight);
}
s.push(i);
}
while(!s.empty()){
int length = heights[s.top()];
s.pop();
int weight = size;
if(!s.empty()){
weight = size - s.top() -1;
}
res = max(res, length*weight);
}
return res;
}
int main(){
vector<int> heights={2, 1, 5, 6, 2, 3};
cout<<fun(heights)<<endl;
}
单调栈
https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/bao-li-jie-fa-zhan-by-liweiwei1419/
42.接雨水
#include<bits/stdc++.h>
using namespace std;
int fun(int height[],int len){
stack<int> s;
int res = 0;
//cout<<len<<endl;
for(int i=0;i<len;i++){
//cout<<i<<" "<<endl;
while(!s.empty() && height[i] > height[s.top()]){
int bottom = height[s.top()];s.pop();
if(s.empty()){
break;
}
int weight = i - s.top() - 1;
int top= min(height[i],height[s.top()]);
res = res + (weight*(top-bottom));
cout<<"i="<<i<<" res="<<res<<endl;
}
s.push(i);
}
cout<<res<<" ";
return res;
}
int main(){
int a[] = {0,1,0,2,1,0,1,3,2,1,2,1};
int len = sizeof(a)/sizeof(a[0]);
fun(a,len);
}
59-1滑动窗口最大值 – 双向队列deque
46.全排列 – dfs
class Solution {
public:
int len = 0;
void fun(vector<vector<int>> &s,vector<int> &single,int first){
if(first == len){
s.emplace_back(single);
return ;
}
for(int i=first;i<len;i++){
swap(single[first],single[i]);
fun(s,single,first+1);
swap(single[first],single[i]);
}
}
vector<vector<int>> permute(vector<int>& nums) {
len = nums.size();
vector<vector<int>> s;
fun(s,nums,0);
return s;
}
};
415.大数相加
假装位数一样不够的前面补零
class Solution {
public:
string addStrings(string num1, string num2) {
int l1=num1.length()-1,l2=num2.length()-1;
int carry=0;
string s="";
while(carry || l1>=0 || l2>=0){
int x = l1<0?0:num1[l1--]-'0';
int y = l2<0?0:num2[l2--]-'0';
s.insert(0,1,char((x+y+carry)%10)+'0');
carry=(x+y+carry)/10;
}
return s;
}
};
03.数组中的重复数字
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1
的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。示例 1:
输入: [2, 3, 1, 0, 2, 5, 3] 输出:2 或 3
二重循环失败原因:超时hashset失败原因:只有java有hashset,c++没有这个数据结构
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<Integer>();
int repeat = -1;
for (int num : nums) {
if (!set.add(num)) {
repeat = num;
break;
}
}
return repeat;
}
}
- bool数组将原数组作为索引 成功
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
bool b[nums.size()];
memset(b,false,sizeof(b));
for(int i=0;i<nums.size();i++){
if(b[nums[i]]==false){
b[nums[i]] = true;
}else{
cout<<nums[i]<<endl;
return nums[i];
}
}
return -1;
}
};
21. 调整数组顺序使奇数位于偶数前面
两种很好的方法:首尾双指针和快慢双指针
//首尾双指针
vector<int> fun1(vector<int>& nums){
int l = 0,r = nums.size()-1;
while(l<r){
if(nums[l] %2 ==1){
l++;
continue;
}
if(nums[r] %2 ==0){
r--;
continue;
}
swap(nums[l],nums[r]);
}
for(int i=0;i<nums.size();i++){
cout<<nums[i]<<" ";
}
return nums;
}
#include<bits/stdc++.h>
using namespace std;
//快慢双指针
vector<int> fun2(vector<int>& nums){
int low=0,fast=0;
while(fast < nums.size()){
if(nums[fast] % 2 == 1){
swap(nums[low],nums[fast]);
low++;
}
fast++;
}
for(int i=0;i<nums.size();i++){
cout<<nums[i]<<" ";
}
return nums;
}
int main(){
vector<int> nums{1,3,4,5,6,7,2,9,8};
fun1(nums);
cout<<endl;
fun2(nums);
}
30.包含min函数的栈
class MinStack {
public:
/** initialize your data structure here. */
stack<int> a,b;
MinStack() {
}
void push(int x) {
a.push(x);
if(b.empty() || b.top() <= x) b.push(x);
}
void pop() {
int x = a.top();a.pop();
if(b.top() == x) b.pop();
}
int top() {
return a.top();
}
int min() {
return b.top();
}
};
31. 栈的压入弹出序列
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int> s;
int j=0;
cout<<pushed.size()<<endl;
for(int i=0;i<pushed.size();i++) {
s.push(pushed[i]); //先放进去再说
while(!s.empty() && s.top() == popped[j]){
s.pop();
j++;
}
}
return s.empty();
}
};
04. 二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
先查找左边界,再锁定到具体行,行内排查。失败:并不是下一行开头就比上一行结尾大,题意理解不当。- 找规律:
note:必须要有判断矩阵是否为空的判断,否则无法执行。
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
if (matrix.size() == 0){
return false;
}
int n =matrix.size();
int m = matrix[0].size();
int i=n-1;
int j=0;
while(i>=0 && j <m){
if(target == matrix[i][j]){
return true;
}else if(target < matrix[i][j]){
i--;
//cout<<matrix[i][j]<<" i--";
}else{
j++;
//cout<<matrix[i][j]<<" j++";
}
}
return false;
}
};
- 递归
在 midmidmid 列寻找满足条件
matrix[row − 1][mid] < target < matrix[row][mid]
的点,比如当 row=3,mid=2时(黄色区域),9<target<14,这时我们可以判断出来 target 一定在左下或者右上区域:
由 target>9,可知 target在 9 的右侧或下侧;
由 target<14,可知 target在 14的上侧或左侧;
因此对左下和右上两个区域进行递归,直到遇到终止条件进行回溯,返回结果。 终止条件为:
区域中没有元素;
target大于深色区域右下角的值(最大值)或小于深色区域左上角的值(最小值)
其中,找到黄色点的方法如下:
列索引 mid采用二分查找;
行索引沿 mid 列从上向下移动,并保持该位置元素小于 target。
### 05. 替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
没有开辟额外空间,先根据空格数量在字符串末尾扩容两个字符的空间(因为一个空格变为%20需要多出两个空间),
然后倒叙遍历将原来位置的字符放到后面, 最后返回s就可以了.
class Solution {
public:
string replaceSpace(string s) {
int l1 = s.length()-1;
for (int i = 0; i <= l1; i++) {
if (s[i] == ' ') {
s += "00";
}
}
int l2 = s.length() - 1;
if (l2 <= l1) {
return s;
}
for (int i = l1; i >= 0; i--) {
char c = s[i];
if (c == ' ') {
s[l2--] = '0';
s[l2--] = '2';
s[l2--] = '%';
} else {
s[l2--] = c;
}
}
return s;
}
};
48.旋转图像(矩阵)
采用分层来进行平移的方式,将矩阵的每一层都分开进行旋转,比如5*5的矩阵可以分为3层
旋转的时候,每四个矩阵块作为一组进行相应的旋转
可以看出,第二次旋转的时候比第一次旋转偏移了一格,这里我们使用add变量来记录矩阵块的偏移量,首先不考虑偏移量的时候写出左上角的坐标为(pos1,pos1),右上角的坐标为(pos1,pos2),左下角的坐标为(pos2,pos1),右下角的坐标为(pos2,pos2),则能够写出偏移之后对应的坐标
每次计算完一层之后,矩阵向内收缩一层,
所以有pos1 = pos1+1,pos2 = pos2-1,终止的条件为pos1 < pos2
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int p1=0,p2=matrix.size()-1;
while(p1<p2){ //每一层
int add=0;
while(add<p2-p1){
int tmp=matrix[p1+add][p2];
matrix[p1+add][p2] = matrix[p1][p1+add];
matrix[p1][p1+add] = matrix[p2-add][p1];
matrix[p2-add][p1] = matrix[p2][p2-add];
matrix[p2][p2-add] = tmp;
add +=1; //每一层中每个点的迁移
}
p1+=1; //收缩层
p2-=1;
}
}
};
12.矩阵中的路径(dfs)
dfs+剪枝
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
rows = board.size();
cols = board[0].size();
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
if(dfs(board, word, i, j, 0)) return true;
}
}
return false;
}
private:
int rows, cols;
bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {
if(i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false; //不是起点或者越界
if(k == word.size() - 1) return true;
board[i][j] = '\0';
bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
board[i][j] = word[k];
return res;
}
};
29.顺时针旋转矩阵
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& m) {
vector<int> s;
if(m.size() == 0) return s;
int l=0,r=m[0].size()-1,t=0,b=m.size()-1,x=0;
while(true){
for(int i = l; i <= r; i++) s.push_back(m[t][i]);
if(++t > b) break;
for(int i = t; i <= b; i++) s.push_back(m[i][r]);
if(--r < l) break;
for(int i = r; i >= l; i--) s.push_back(m[b][i]);
if(--b < t) break;
for(int i = b; i >= t; i--) s.push_back(m[i][l]);
if(++l > r) break;
}
for(vector<int>::iterator it = s.begin(); it != s.end(); it++){
cout<<*it<<" ";
}
return s;
}
};
56-1.数组中数字出现的次数
位异或,不要急拿笔演算一下试一试,但是只适用于双数出现中挑选出单数出现的,局限性比较大
public int[] singleNumbers(int[] nums) {
//xor用来计算nums的异或和
int xor = 0;
// 计算异或和 并存到xor
// e.g. [2,4,2,3,3,6] 异或和:(2^2)^(3^3)^(4^6)=2=010
for(int num : nums) xor ^= num;
//设置mask为1,则二进制为0001
// mask是一个二进制数,且其中只有一位是1,其他位全是0,比如000010,
// 表示我们用倒数第二位作为分组标准,倒数第二位是0的数字分到一组,倒数第二位是1的分到另一组
int mask = 1;
// & operator只有1&1时等于1 其余等于0
// 用上面的e.g. 4和6的二进制是不同的 我们从右到左找到第一个不同的位就可以分组 4=0100 6=0110
// 根据e.g. 010 & 001 = 000 = 0则 mask=010
// 010 & 010 != 0 所以mask=010
// 之后就可以用mask来将数组里的两个数分区分开
while((xor & mask)==0){
mask <<= 1;
}
//两个只出现一次的数字
int a=0, b=0;
for(int num : nums){
//根据&是否为0区分将两个数字分区,并分别求异或和
if((num & mask)==0) a ^= num;
else{
b ^= num;
}
}
return new int[]{a,b};
}
56-2
map的写法,比较倾向于是一个通法。
class Solution {
public:
int singleNumber(vector<int>& nums) {
//哈希表方法
map<int,int> mp;
for(int i=0;i<nums.size();i++){
mp[nums[i]]++;
cout<<nums[i]<<":"<<mp[nums[i]]<<endl;
}
int ans;
for(map<int,int>::iterator it = mp.begin();it!=mp.end();it++){
if(it->second == 1){
ans = it->first;
cout<<ans<<endl;
break;
}
}
return ans;
}
};
方法二:位运算
- 值得注意的是:如果某个数字出现3次,那么这个3个数字的和肯定能被3整除,则其对应二进制位的每一位的和也能被3整除
- 统计数组中每个数字的二进制中每一位的和,判断该和是否能被3整除。
- 若可以,则只出现一次的数字的二进制数中那一位为0,否则为1
话不多说看不懂的话没关系,推演一下就明白了,这个方法也可以作为通式通法
class Solution {
public:
int singleNumber(vector<int>& nums) {
//位运算方法
int ans = 0;
for(int i=0;i<32;i++){
int cnt = 0;
for(int j=0;j<nums.size();j++){
if(nums[j] & (1<<i)) cnt++;
}
if(cnt % 3 == 1) ans ^= (1<<i);
}
cout<<ans;
return ans;
}
};