41.和为S的连续正数序列
使用双指针,形成滑动窗口
class Solution {
public:
vector<vector<int> > FindContinuousSequence(int sum) {
if (0 == sum) return vector<vector<int>>();
vector<vector<int>> ans;
int left = 1, right = 2;
while (left < right) {
// 根据等差数列求和公式,n * (a1 + an) / 2
int sumTmp = (right - left + 1) * (left + right) / 2;
if (sumTmp == sum) {
vector<int> tmp;
for (int i = left; i <= right; i++) {
tmp.push_back(i);
}
ans.push_back(tmp);
left++;
} else if (sumTmp > sum) {
left++;
} else {
right++;
}
}
return ans;
}
};
42.和为S的两个数字
同样的思路,双指针
class Solution {
public:
vector<int> FindNumbersWithSum(vector<int> array,int sum) {
vector<int> ans;
int left = 0, right = array.size() - 1;
while (left < right) {
int sumTmp = array[left] + array[right];
if (sum == sumTmp) {
ans.push_back(array[left]);
ans.push_back(array[right]);
return ans;
} else if (sumTmp < sum) {
left++;
} else {
right--;
}
}
return vector<int>();
}
};
43.左旋转字符串
class Solution {
public:
string LeftRotateString(string str, int n) {
int len = str.size();
if (len == 0) return str;
int index = n % len;
str += str;
return str.substr(index, len);
}
};
44.反转单词序列
class Solution {
public:
string ReverseSentence(string str) {
string ans = "", tmp = "";
for (int i = 0; i < str.size(); i++) {
if (str[i] == ' ') {
ans = " " + tmp + ans;
tmp = "";
} else {
tmp += str[i];
}
}
if (tmp.size()) {
ans = tmp + ans;
}
return ans;
}
};
45.扑克牌顺子
class Solution {
public:
bool IsContinuous( vector<int> numbers ) {
sort(numbers.begin(), numbers.end());
int zero_num = 0;
for (int i = 0; i < numbers.size() - 1; i++) {
if (0 == numbers[i]) zero_num++;
else if (numbers[i] == numbers[i + 1]) return false;
}
return numbers[4] - numbers[zero_num] < 5;
}
};
46.圆圈中的数字
约瑟夫环,这老哥讲的很好
class Solution {
public:
int LastRemaining_Solution(int n, int m) {
int last = 0;
// 最后一轮剩下2个人,所以从2开始反推
for (int i = 2; i <= n; i++) {
last = (last + m) % i;
}
return last;
}
};
47.求1+2+3+…+n
利用 短路与 实现递归,直到n = 0时,可以判断递归结束
class Solution {
public:
int Sum_Solution(int n) {
(n += Sum_Solution(n - 1)) && n;
return n;
}
};
48.不用加减乘除做加法
思路,利用加法器的原理实现
异或运算可以得到,两数之和没有进位
逻辑与,可以得到进位
class Solution {
public:
int Add(int num1, int num2) {
while (num2) {
int sum = num1 ^ num2;
int carry = (num1 & num2) << 1;
num1 = sum;
num2 = carry;
}
return num1;
}
};
49.把字符串转换成整数
主要在于边界处理
class Solution {
public:
int StrToInt(string str) {
int len = str.size();
if (0 == len) return 0;
int i = 0;
while (i < len && str[i] == ' ') i++; // 排除字符串前面空格
if (i == len) return 0; // 全部为 空格
if (!isdigit(str[i]) && str[i] != '-' && str[i] != '+') return 0;
bool flag = str[i] == '-' ? true : false;
i = isdigit(str[i]) ? i : i+1;
long long ans = 0L;
while (i < len && isdigit(str[i])) {
ans = ans * 10 + (str[i++] - '0');
if (!flag && ans > INT_MAX) {
ans = INT_MAX;
break;
}
if (flag && ans > 1 + INT_MAX) {
ans = 1L + INT_MAX;
break;
}
}
if (i != len) return 0;
return !flag ? static_cast<int>(ans) : static_cast<int>(-ans);
}
};
50.数组中重复的数字
哈希加循环
class Solution {
public:
bool duplicate(int numbers[], int length, int* duplication) {
unordered_map<int, int> umap;
for (int i = 0; i < length; i++) {
umap[numbers[i]]++;
if (2 == umap[numbers[i]]) {
*duplication = numbers[i];
return true;
}
}
return false;
}
};
空间优化
class Solution {
public:
bool duplicate(int numbers[], int length, int* duplication) {
vector<bool> vec(length, false);
for (int i = 0; i < length; i++) {
if (!vec[numbers[i]]) {
vec[numbers[i]] = true;
} else {
*duplication = numbers[i];
return true;
}
}
return false;
}
};
原地判断
将数组的元素看做数组的下标,将出现过的下标的元素加上数组的长度,使其大于数组长度
class Solution {
public:
bool duplicate(int numbers[], int length, int* duplication) {
for (int i = 0; i < length; i++) {
int index = numbers[i] % length;
if (numbers[index] >= length) {
*duplication = index;
return true;
}
numbers[index] += length;
}
return false;
}
};
51.构建乘积数组
emmm… 怎么说
class Solution {
public:
vector<int> multiply(const vector<int>& A) {
if (1 == A.size()) return vector<int>();
int num = 1, zero = 0;
for (int n : A) {
if (n == 0) {
zero++; // 记录 0 出现的个数
continue;
}
num *= n; // 计算除了0之外,其余所有数的乘积
}
vector<int> B;
for (int i = 0; i < A.size(); i++) {
if (zero == 0) {
B.push_back(num / A[i]);
} else if (zero == 1 && A[i] == 0) {
B.push_back(num);
} else if (zero > 1){
B.push_back(0);
} else {
B.push_back(0);
}
}
return B;
}
};
这个好
class Solution {
public:
vector<int> multiply(const vector<int>& A) {
if (1 == A.size()) return vector<int>();
vector<int> B(A.size(), 1);
for (int i = 1; i < A.size(); i++) {
B[i] = B[i - 1] * A[i - 1]; // 从左到右计算 数组元素前面的所有累乘积
}
int tmp = 1;
for (int i = A.size() - 1; i >= 0; i--) {
B[i] *= tmp;
tmp *= A[i]; // 从右到左计算 数组元素后面的所有累乘积
}
return B;
}
};
52.正则表达式匹配
单独看思路是理解不了的,照着敲一遍思路就清晰了
class Solution {
public:
bool match(string str, string pattern) {
char a[100], b[100];
strcpy(a,str.c_str());
strcpy(b,pattern.c_str());
return match(a, b);
}
bool match(char* str, char* pattern) {
// 两种特殊情况
if (*str == '\0' && *pattern == '\0') return true;
if (*str != '\0' && *pattern == '\0') return false;
// if the next character in pattern is not '*'
if (*(pattern + 1) != '*') { // 只需简单比较,然后继续比较下一个字符
if (*str == *pattern || (*str != '\0' && *pattern == '.')) {
return match(str + 1, pattern + 1);
} else {
return false;
}
} else { // if the next character in pattern is '*'
if (*str == *pattern || (*str != '\0' && *pattern == '.')) {
// 当‘*’比配0个,直接比较pattern的下一个字符
// 当‘*’匹配1个或多个,str需要移动,pattern不需要移动
return match(str, pattern + 2) || match(str + 1, pattern);
} else {
// 没有匹配到的话,就当做匹配了0个
return match(str, pattern + 2);
}
}
}
};
53.表示数字的字符串
难,太难了
class Solution {
public:
int index = 0;
// 有符号数判断
bool integer(string str) {
if (index < str.length() && str[index] == '-' || str[index] == '+') index++;
// 然后需要判断后面是否为一个无符号整数
return unsigned_integers(str);
}
// 无符号数判断,直到遇见非数字停止
bool unsigned_integers(string str) {
int tmp = index;
while (index < str.size() && str[index] >= '0' && str[index] <= '9') index++;
return index > tmp;
}
bool isNumeric(string str) {
int len = str.size();
if (0 == len) return false;
// 去除前面的空格
while (index < len && str[index] == ' ') index++;
// 去除后面的空格
while (len >= 1 && str[len - 1] == ' ') len--;
// 全是空格
if (len < index) return false;
// 判断是否为有符号的整数
bool flag = integer(str);
// 如果有小数点,需要判断后面是不是一个无符号整数,遇到非数字停止
if (index < len && str[index] == '.') {
index++;
flag = unsigned_integers(str) || flag;
}
// 如果有e
if (index < len && str[index] == 'e' || str[index] == 'E') {
index++;
flag = flag && integer(str);
}
// 是否字符串遍历结束
return flag && (index == len);
}
};
54.字符流中第一个不重复的字符
不要被它的返回值迷惑,就返回第一个不重复的字符即可
使用哈希
class Solution
{
public:
//Insert one char from stringstream
void Insert(char ch) {
vec.push_back(ch);
umap[ch]++;
}
//return the first appearence once char in current stringstream
char FirstAppearingOnce() {
if (vec.empty()) return '#';
for (char c : vec) {
if (umap[c] == 1) return c;
}
return '#';
}
vector<int> vec;
unordered_map<char, int> umap;
};
使用库函数count
class Solution
{
public:
//Insert one char from stringstream
void Insert(char ch) {
vec.push_back(ch);
}
//return the first appearence once char in current stringstream
char FirstAppearingOnce() {
if (vec.empty()) return '#';
for (char c : vec) {
if (count(vec.begin(), vec.end(), c) == 1) return c;
}
return '#';
}
vector<int> vec;
};
55.链表中环的入口结点
这里不要被示例3迷惑了,即使是示例3这样的,在pHead中是自身形成一个环
所以哈希记录的方法是可以的
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
if (!pHead) return nullptr;
unordered_map<ListNode*, int> umap;
while (pHead) {
umap[pHead]++;
if (umap[pHead] == 2) return pHead;
pHead = pHead->next;
}
return nullptr;
}
};
采用快慢双指针
1.slow指针一次走一步,fast指针一次走两步,两者第一次会在C结点处相遇
⒉.slow指针走的路径为A-B-C,距离为X+Y,则fast指针走的距离为2(X+Y),路径为A-B-C-B-C
3.可以得出C-B的距离为X,也就是A-B的距离,此时快慢指针同时指向环的入口结点
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
if (!pHead) return nullptr;
ListNode* slow = pHead;
ListNode* fast = pHead;
while (fast != nullptr && fast->next != nullptr) {
fast = fast->next->next;
slow = slow->next;
// 找到第一次相遇的结点
if (slow == fast) break;
}
// 若是快指针指向nullptr,代表没有环
if (!fast || !fast->next) return nullptr;
// 将快指针指向头结点,快慢指针谁都可以,这里用快指针
fast = pHead;
// 根据计算出的值,再一次相遇结点为入口结点
while (fast != slow) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
};
56.删除链表中重复的结点
直接遍历比较删除
class Solution {
public:
ListNode* deleteDuplication(ListNode* pHead) {
if (!pHead) return nullptr;
ListNode* root = new ListNode(0);
root->next = pHead;
ListNode* cur = root;
while (cur->next && cur->next->next) {
if (cur->next->val == cur->next->next->val) {
int tmp = cur->next->val;
while (cur->next && cur->next->val == tmp) {
cur->next = cur->next->next;
}
} else {
cur = cur->next;
}
}
return root->next;
}
};
57.二叉树的下一个结点
这个思路很简单,就是通过传入的结点找到根节点,然后中序遍历,找到传入结点的下一个结点即可
class Solution {
public:
vector<TreeLinkNode*> vec;
TreeLinkNode* GetNext(TreeLinkNode* pNode) {
if (!pNode) return nullptr;
TreeLinkNode* root = pNode;
while (root->next) root = root->next;
LDR(root);
for (int i = 0; i < vec.size() - 1; i++) {
if (vec[i] == pNode) {
return vec[i + 1];
}
}
return nullptr;
}
void LDR(TreeLinkNode* root) {
if (!root) return ;
LDR(root->left);
vec.push_back(root);
LDR(root->right);
}
};
法二:直接分类寻找,画图就很好理解了
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode) {
// 有右子树
if (pNode->right) {
TreeLinkNode* rchild = pNode->right;
// 找到该右子树的最左下节点为返回值
while (rchild->left) rchild = rchild->left;
return rchild;
}
// 没有右子树且当前节点是其父结点的左子节点
if (pNode->next && pNode->next->left == pNode) {
// 返回其父节点
return pNode->next;
}
// 没有右子树且当前节点是其父节点的右子节点
if (pNode->next) {
TreeLinkNode* ppar = pNode->next;
// 沿着左上一直找到当前结点是其父节点的左子节点为止
while (ppar->next && ppar->next->right == ppar) ppar = ppar->next;
// 返回其父节点
return ppar->next;
}
return nullptr;
}
};
58.对称的二叉树
class Solution {
public:
bool isSymmetrical(TreeNode* pRoot) {
if (!pRoot) return true;
return isEqual(pRoot->left, pRoot->right);
}
bool isEqual(TreeNode* node1, TreeNode* node2) {
if (!node1 && !node2) return true;
if (!node1 || !node2) return false;
if (node1->val != node2->val) return false;
return isEqual(node1->left, node2->right) && isEqual(node1->right, node2->left);
}
};
59.按之字形顺序打印二叉树
利用双向队列实现
通过level代表当前的层高,来判断该层遍历的方向,
默认根节点为第一层,从第二层开始(偶数层),从右往左遍历
然后下一层(奇数层)从左往右遍历,以此类推
刚开始出现一个问题就是如何知道该层需要循环几次,后来发现可以通过上一层添加的子节点数来判断
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
if (!pRoot) return vector<vector<int>>();
vector<vector<int>> ans;
int level = 1; // 从第一层开始
deque<TreeNode*> dq;
dq.push_back(pRoot);
while (!dq.empty()) {
vector<int> tmp; // 存放每一层的元素
int rowLen = dq.size(); // 该层有多少个元素,就要循环多少次
while (rowLen--) {
if (0 == level % 2) { // 当为偶数层,从右往左添加
TreeNode* node = dq.back();
dq.pop_back();
tmp.push_back(node->val);
// 因为添加到tmp容器的方向不同,所以入队的方向不同
if (node->right) dq.push_front(node->right);
if (node->left) dq.push_front(node->left);
} else { // 当为奇数层,从左往右添加
TreeNode* node = dq.front();
dq.pop_front();
tmp.push_back(node->val);
if (node->left) dq.push_back(node->left);
if (node->right) dq.push_back(node->right);
}
}
ans.push_back(tmp);
level++;
}
return ans;
}
};
60.把二叉树打印成多行
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
if (!pRoot) return vector<vector<int>>();
vector<vector<int>> ans;
queue<TreeNode*> q;
q.push(pRoot);
while (!q.empty()) {
vector<int> tmp;
int rowSize = q.size();
while (rowSize--) {
TreeNode* node = q.front();
q.pop();
tmp.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
ans.push_back(tmp);
}
return ans;
}
};