剑指Offer题解合集
剑指Offer题单及题解
题目顺序为牛客上剑指Offer专题
JZ3、数组中重复的数字
分析
可以直接对数组进行排序,通过判断首末数字大小来判断数字越界情况,注意数组为空的情况。发现 \(0 \leq nums[i] \leq n - 1\), 因此直接开一个数组判断是否有重复数字即可,返回第一个重复数字。
代码实现
class Solution {
public:
int duplicateInArray(vector<int>& nums) {
int n = size(nums);
std::sort(nums.begin(), nums.end());
if (n == 0 || nums.back() > n || nums[0] < 0) {
return -1;
}
std::vector<int> buc(n);
for (auto x : nums) {
buc[x] += 1;
if (buc[x] > 1) {
return x;
}
}
return -1;
}
};
JZ5。替换空格
分析
直接调用std::string
中的replace
方法替换
即可。
代码实现
class Solution {
public:
string replaceSpaces(string &str) {
for (int i = 0; i < size(str); ++i) {
if (str[i] == ' ') {
str.replace(str.begin() + i, str.begin() + i + 1, "%20");
}
}
return str;
}
};
JZ6、从尾到头打印链表
分析
直接遍历链表,将链表中的数据存入vector
,最后reverse
一下返回即可。
代码实现
class Solution {
public:
vector<int> printListReversingly(ListNode* head) {
std::vector<int> ans;
while (head != nullptr) {
ans.push_back(head->val);
head = head->next;
}
std::reverse(ans.begin(), ans.end());
return ans;
}
};
JZ7、重建二叉树
分析
前序:根左右
中序:左根右
按照规则两个棵树同时进行递归即可,每次通过前序确定根节点,然后通过中序确定左右子树大小来改变递归范围。
代码实现
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = size(preorder);
auto dfs = [&](auto &&self, int preL, int preR, int inL, int inR)->TreeNode* {
if (inL > inR) return NULL;
int root = preorder[preL], mid = inL;
// std::cerr << root << ' ' << preL << ' ' << preR << ' ' << inL << ' ' << inR << '\n';
TreeNode *node = new TreeNode(root);
while (mid < inR && inorder[mid] != root) {
mid += 1;
}
node->left = self(self, preL + 1, preL + mid - inL, inL, mid - 1);
node->right = self(self, preL + mid - inL + 1, preR, mid + 1, inR);
return node;
};
return dfs(dfs, 0, n - 1, 0, n - 1);
}
};
JZ8、二叉树的下一个节点
分析
可以分为两种情况讨论:
- 情况1:当前节点的右节点不为空,则下一个节点为其右子树的左子树的最下面的节点
- 情况2:当前节点的右节点为空,则下一个节点为其右子树祖先节点第一个存在右子树节点
代码实现
class Solution {
public:
TreeNode* inorderSuccessor(TreeNode* p) {
if (p->right != nullptr) {
p = p->right;
while (p->left != nullptr) {
p = p->left;
}
return p;
}
while (p->father != nullptr && p->father->right == p) {
p = p->father;
}
return p->father;
}
};
JZ9、用两个栈实现队列
分析
利用栈后进先出的特点,将stk1
栈设置为入队栈只执行插入操作,stk2
栈设置为出队栈只执行出队操作,当stk2
中元素出队完后,将stk1
元素往stk2
插即可,此时插过来的元素一定在栈底,满足队列条件。
代码实现
class MyQueue {
public:
/** Initialize your data structure here. */
std::stack<int> stk1, stk2;
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
stk1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
if (!size(stk2)) {
while (size(stk1)) {
stk2.push(stk1.top());
stk1.pop();
}
}
int ans = stk2.top();
stk2.pop();
return ans;
}
/** Get the front element. */
int peek() {
if (!size(stk2)) {
while (size(stk1)) {
stk2.push(stk1.top());
stk1.pop();
}
}
return stk2.top();
}
/** Returns whether the queue is empty. */
bool empty() {
return !(size(stk1) || size(stk2));
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* bool param_4 = obj.empty();
*/
JZ10、斐波那契数列
分析
没什么可说的,\(n \leq 39\)直接暴力即可,甚至不用开long long
代码实现
class Solution {
public:
int Fibonacci(int n) {
int n1 = 0, n2 = 1;
for (int i = 0; i < n; ++i) {
int n3 = n1 + n2;
n1 = n2, n2 = n3;
}
return n1;
}
};
JZ11、旋转数组的最小数字
分析
...直接乱写了,get不到这个题目的考点
代码实现
class Solution {
public:
int findMin(vector<int>& nums) {
if (size(nums) == 0) return -1;
return *min_element(nums.begin(), nums.end());
}
};
JZ12、矩阵中的路径
分析
直接根据枚举起点都搜一遍就行了。
代码实现
class Solution {
public:
bool hasPath(vector<vector<char>>& matrix, string &str) {
if (size(matrix) == 0) return false;
int n = size(matrix), m = size(matrix[0]);
std::vector<std::vector<bool>> vis(n, std::vector<bool>(m));
auto dfs = [&](auto &&self, int u, int x, int y)->bool {
if (u == size(str)) return true;
for (auto [dx, dy] : {std::pair{x, y + 1}, {x, y - 1}, {x - 1, y}, {x + 1, y}}) {
if (dx >= 0 && dx < n && dy >= 0 && dy < m
&& !vis[dx][dy] && str[u] == matrix[dx][dy]) {
vis[dx][dy] = true;
if (self(self, u + 1, dx, dy)) {
return true;
}
vis[dx][dy] = false;
}
}
return false;
};
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (matrix[i][j] == str[0]) {
vis[i][j] = true;
if (dfs(dfs, 1, i, j)) {
return true;
}
vis[i][j] = false;
}
}
}
return false;
}
};
JZ13、机器人的运动范围
分析
直接从起点开始dfs搜一遍,记录哪些点可以到达即可。
代码实现
class Solution {
public:
int movingCount(int threshold, int rows, int cols) {
if (rows == 0 || cols == 0) return 0;
std::vector<std::vector<bool>> vis(rows, std::vector<bool>(cols));
auto calc = [&](int x, int y) {
return x % 10 + x / 10 % 10 + y % 10 + y / 10 % 10;
};
auto dfs = [&](auto &&self, int x, int y)->int {
int cnt = 1;
vis[x][y] = true;
for (auto [dx, dy] : {std::pair{x + 1, y}, {x - 1, y}, {x, y + 1}, {x, y - 1}}) {
if (dx >= 0 && dx < rows && dy >= 0 && dy < cols && !vis[dx][dy] && calc(dx, dy) <= threshold) {
cnt += self(self, dx, dy);
}
}
return cnt;
};
return dfs(dfs, 0, 0);
}
};
JZ14、剪绳子
分析
发现n非常小,因此可以选择直接枚举分成m段,显然的当各个位数越接近时他们的乘积越大
代码实现
class Solution {
public:
int maxProductAfterCutting(int length) {
int ans = 1;
for (int m = 2; m < length; ++m) {
int p = length / m;
std::vector<int> num(m, p);
for (int i = 0; i < length - p * m; ++i) {
num[i] += 1;
}
int mul = 1;
for (auto &x : num) {
mul *= x;
}
ans = std::max(mul, ans);
}
return ans;
}
};
JZ15、二进制中1的个数
分析
__builtin_popcount()
直接秒了
代码实现
class Solution {
public:
int NumberOf1(int n) {
// int ans = 0;
// while (n) {
// n &= n - 1;
// ans += 1;
// }
// return ans;
return __builtin_popcount(n);
}
};
JZ16、数值的整数次方
分析
快速幂模拟一下即可,如果exponent
为负数最后被1除一下就行了
代码实现
class Solution {
public:
double Power(double base, int exponent) {
using i64 = long long;
bool flag = exponent > 0;
i64 c = std::abs((i64)exponent);
double ans = 1;
while (c) {
if (c & 1) ans *= base;
base *= base;
c >>= 1;
}
if (!flag) {
ans = 1. / ans;
}
return ans;
}
};
JZ17、打印从1到最大的n位数
分析
直接开一个\(10^n - 1\)大小的数组从头开始赋值即可。
代码实现
class Solution {
public:
vector<int> printNumbers(int n) {
std::vector<int> f((int)pow(10, n) - 1);
for (int idx = 1; auto &x : f) {
x = idx++;
}
// std::iota(f.begin(), f.end(), 1);
return f;
}
};
JZ18、在O(1)时间删除链表结点
分析
将当前节点的值拷贝为下一个节点的值,再将下一个节点删除等价于删除当前节点。
代码实现
class Solution {
public:
void deleteNode(ListNode* node) {
ListNode *now = node->next;
node->val = now->val;
node->next = node->next->next;
delete now;
}
};
JZ20、表示数值的字符串
分析
面向样例编程最爽的一集,模拟一下即可。
代码实现
class Solution {
public:
bool isNumber(string s) {
if (s == "") return false;
int pe = -1, pE = -1;
for (int idx = 0; auto &c : s) {
if (!((c >= '0' && c <= '9')
|| c == '+' || c == '-' || c == '.'
|| c == 'E' || c == 'e')) {
return false;
}
if (c == 'e') {
if (pe != -1) {
return false;
} else pe = idx;
}
if (c == 'E') {
if (pE != -1) {
return false;
} else pE = idx;
}
idx += 1;
}
auto check = [&](std::string &t) {
int pAdd = t.rfind('+'), pSub = t.rfind('-');
if (pAdd != -1 && pAdd != 0) return false;
if (pSub != -1 && pSub != 0) return false;
if (std::count(t.begin(), t.end(), '.') > 1) return false;
if (t == "+." || t == "-." || t == ".-" || t == ".+"
|| t == "+" || t == "-" || t == ".") return false;
return true;
};
if (pe != -1 && pE != -1) {
return false;
} else if (pe != -1 || pE != -1) {
int p = pe != -1 ? pe : pE;
std::string s1 = s.substr(0, p), s2 = s.substr(p + 1);
if (s1 == "" || s2 == "" || s2.find('.') != string::npos) return false;
return check(s1) && check(s2);
} else {
return check(s);
}
}
};
JZ21、调整数组顺序使奇数位于偶数前面
分析
直接拿两个数组记录所有奇数和偶数再插入原数组中即可。
代码实现
class Solution {
public:
void reOrderArray(vector<int> &array) {
int n = size(array);
std::vector<int> even, odd;
for (auto x : array) {
x % 2 ? odd.push_back(x) : even.push_back(x);
}
int idx = 0;
for (auto x : odd) {
array[idx++] = x;
}
for (auto x : even) {
array[idx++] = x;
}
}
};
JZ22、链表中倒数最后k个结点
分析
记录两个间距为k的指针,然后同时往后跳到最后即可找到第k个节点
代码实现
class Solution {
public:
ListNode* findKthToTail(ListNode* pHead, int k) {
if (pHead == nullptr) {
return nullptr;
}
ListNode *fast = pHead, *slow = pHead;
while (k--) {
fast = fast->next;
if (fast == nullptr && k > 0) {
return nullptr;
}
}
while (fast != nullptr) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
JZ23、链表中环的入口结点
分析
快慢指针,一个指针每次走1步,一个指针每次走两步,如果能够相遇则说明存在环。随后将其中一个指针调到起点,接着走,相遇的节点即位入口节点。
代码实现
class Solution {
public:
ListNode *entryNodeOfLoop(ListNode *pHead) {
if (pHead == nullptr) {
return nullptr;
}
ListNode *fast = pHead, *slow = pHead;
do {
fast = fast->next;
slow = slow->next;
if (fast != nullptr) {
fast = fast->next;
} else return nullptr;
if (fast == slow) {
break;
}
} while (fast != nullptr);
if (fast == nullptr) {
return nullptr;
}
while (pHead != slow) {
slow = slow->next;
pHead = pHead->next;
}
return pHead;
}
};
JZ24、反转链表
分析
每次拿一个中间点转换一下两个点指针方向即可。
代码实现
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return head;
}
ListNode *n1 = head, *n2 = head->next;
head->next = nullptr;
while (n2 != nullptr) {
ListNode *n3 = n2->next;
n2->next = n1;
n1 = n2;
n2 = n3;
}
return n1;
}
};
JZ25、合并两个排序的链表
分析
需要用一个新的指针来接受新链表,旧的两个指针每次比较头元素,然后将较小的元素插入新链表即可。
代码实现
class Solution {
public:
ListNode* merge(ListNode* pHead1, ListNode* pHead2) {
if (pHead1 == nullptr) {
return pHead2;
}
if (pHead2 == nullptr) {
return pHead1;
}
ListNode *ans;
if (pHead1->val > pHead2->val) {
std::swap(pHead1, pHead2);
}
ans = pHead1;
while (pHead1->next != nullptr && pHead2 != nullptr) {
// std::cout << pHead1->next->val << ' ' << pHead2->val << "!\n";
if (pHead1->next->val > pHead2->val) {
ListNode *temp = pHead2;
pHead2 = pHead2->next;
temp->next = pHead1->next;
pHead1->next = temp;
}
pHead1 = pHead1->next;
// std::cout << pHead1->val << "?\n";
}
if (pHead1->next == nullptr) {
pHead1->next = pHead2;
}
return ans;
}
};
JZ26、树的子结构
分析
遍历A树,遇到和Bval相等的节点就到B中搜索一下看查是否为子树即可。
代码实现
class Solution {
public:
bool hasSubtree(TreeNode* A, TreeNode* B) {
if (B == NULL) return false;
auto check = [&](auto &&self, TreeNode *a, TreeNode *b)->bool {
if (a == nullptr && b == nullptr) return true;
if (b == nullptr) return true;
if (a == nullptr) return false;
if (a->val != b->val) return false;
return self(self, a->left, b->left) && self(self, a->right, b->right);
};
auto dfs = [&](auto &&self, TreeNode *root)->bool {
if (root == NULL) return false;
if (root->val == B->val) {
if (check(check, root, B)) return true;
}
if (self(self, root->right)) return true;
if (self(self, root->left)) return true;
return false;
};
return dfs(dfs, A);
}
};
JZ27、二叉树的镜像
分析
每次递归完交换左右子树即可
代码实现
class Solution {
public:
void mirror(TreeNode* pRoot) {
auto dfs = [&](auto &&self, TreeNode *root)->void {
if (root == nullptr) return ;
self(self, root->left);
self(self, root->right);
std::swap(root->left, root->right);
};
dfs(dfs, pRoot);
}
};
JZ29、顺时针打印矩阵
分析
模拟,注意判断边界就行了
代码实现
class Solution {
public:
vector<int> printMatrix(vector<vector<int> > matrix) {
if (size(matrix) == 0) return {};
int n = size(matrix), m = size(matrix[0]);
std::array<int, 4> dir{m - 1, n - 1, 0, 1};
std::vector<int> ans;
int x = 0, y = 0, idx = 0;
while (size(ans) != n * m) {
// std::cerr << idx << ' ' << x << ' ' << y << '\n';
if (idx % 4 == 0) {
while (y <= dir[idx % 4]) {
ans.push_back(matrix[x][y++]);
}
dir[idx % 4] -= 1;
y -= 1, x += 1;
} else if (idx % 4 == 1) {
while (x <= dir[idx % 4]) {
ans.push_back(matrix[x++][y]);
}
dir[idx % 4] -= 1;
x -= 1, y -= 1;
} else if (idx % 4 == 2) {
while (y >= dir[idx % 4]) {
ans.push_back(matrix[x][y--]);
}
dir[idx % 4] += 1;
y += 1, x -= 1;
} else {
while (x >= dir[idx % 4]) {
ans.push_back(matrix[x--][y]);
}
dir[idx % 4] += 1;
x += 1, y += 1;
}
idx += 1;
}
return ans;
}
};
JZ30、包含min函数的栈
分析
维护两个栈,其中一个单调存储元素即可。
代码实现
class MinStack {
public:
std::stack<int> stk, stkMin;
MinStack() {
}
void push(int x) {
if (size(stkMin) == 0 || stkMin.top() >= x) {
stkMin.push(x);
}
stk.push(x);
}
void pop() {
if (stkMin.top() == stk.top()) {
stkMin.pop();
}
stk.pop();
}
int top() {
return stk.top();
}
int getMin() {
return stkMin.top();
}
};
JZ30、包含min函数的栈
分析
维护两个栈,其中一个单调存储元素即可。
代码实现
class MinStack {
public:
std::stack<int> stk, stkMin;
MinStack() {
}
void push(int x) {
if (size(stkMin) == 0 || stkMin.top() >= x) {
stkMin.push(x);
}
stk.push(x);
}
void pop() {
if (stkMin.top() == stk.top()) {
stkMin.pop();
}
stk.pop();
}
int top() {
return stk.top();
}
int getMin() {
return stkMin.top();
}
};
JZ31、栈的压入、弹出序列
分析
使用一个栈模拟出入栈即可。
代码实现
class Solution {
public:
bool isPopOrder(vector<int> pushV,vector<int> popV) {
if (size(pushV) != size(popV)) return false;
if (size(pushV) == 0) return true;
std::stack<int> stk;
int puv = 0, pov = 0, n = size(pushV);
while (puv != (int)size(pushV) && pov != (int)size(popV)) {
// std::cerr << size(stk) << ' ' << pov << ' ' << puv << "?\n";
while ((int)size(stk) == 0 || (puv < n && stk.top() != popV[pov])) {
stk.push(pushV[puv++]);
}
while (size(stk) && pov < n && stk.top() == popV[pov]) {
stk.pop();
pov++;
}
if (puv == n && size(stk) != 0 && pov != n) {
return false;
}
}
return true;
}
};
JZ32、从上往下打印二叉树
分析
即二叉树的层序遍历
代码实现
class Solution {
public:
vector<int> printFromTopToBottom(TreeNode* root) {
if (root == nullptr) return {};
std::vector<TreeNode *> q;
std::vector<int> ans;
q.push_back(root);
for (int i = 0; i < (int)size(q); ++i) {
TreeNode *u = q[i];
ans.push_back(u->val);
if (u->left != nullptr) {
q.push_back(u->left);
}
if(u->right != nullptr) {
q.push_back(u->right);
}
}
return ans;
}
};
JZ33、二叉搜索树的后序遍历序列
分析
二叉搜索树的性质:左子树都比右子树小。
并且由于是后续遍历可以直接确定根节点,因此本题可以通过根节点来寻找左右子树边界,最后分别递归判断右子树是否全部大于根节点即可。
代码实现
class Solution {
public:
bool verifySequenceOfBST(vector<int> sequence) {
if (size(sequence) == 0) return true;
int n = size(sequence);
auto dfs = [&](auto &&self, int l, int r)->bool {
if (l >= r) return true;
int root = sequence[r], mid = l;
while (mid < r && sequence[mid] < root) {
mid += 1;
}
// std::cout << l << ' ' << r << ' ' << mid << '\n';
for (int i = mid + 1; i < r; ++i) {
if (sequence[i] < root) return false;
}
return self(self, l, mid - 1) && self(self, mid, r - 1);
};
return dfs(dfs, 0, n - 1);
}
};
JZ35、复杂链表的复制
分析
使用map记录下被映射的节点,如果未创建则需要重新创建一个,最后就是遍历链表并记录新链表。
代码实现
class Solution {
public:
ListNode *copyRandomList(ListNode *head) {
std::map<ListNode *, ListNode *> mp;
mp[nullptr] = nullptr;
ListNode *dup = new ListNode(-1), *tail = dup;
auto dfs = [&](auto &&self, ListNode *root)->void {
if (root == nullptr) return ;
if (!mp.count(root)) {
mp[root] = new ListNode(root->val);
}
if (!mp.count(root->random)) {
mp[root->random] = new ListNode(root->random->val);
}
tail->next = mp[root];
tail->next->random = mp[root->random];
tail = tail->next;
self(self, root->next);
};
dfs(dfs, head);
return dup->next;
}
};
JZ36、二叉搜索树与双向链表
分析
dfs的时候注意记录下父亲节点就行了。
代码实现
class Solution {
public:
TreeNode* convert(TreeNode* root) {
if (root == nullptr) return nullptr;
TreeNode *head = nullptr, *pre = nullptr;
auto dfs = [&](auto &&self, TreeNode *root)->void {
if (root == nullptr) return ;
self(self, root->left);
if (pre == nullptr) {
head = root;
} else {
pre->right = root;
}
root->left = pre;
pre = root;
self(self, root->right);
};
dfs(dfs, root);
return head;
}
};
JZ37、序列化二叉树
分析
使用std::stringstream
模拟std::cin
和std::cout
输入输出前序序列即可。
代码实现
class Solution {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
std::stringstream cout;
auto dfs = [&](auto &&self, TreeNode *root)->void {
if (root == nullptr) {
cout << (int)1e9 << ' ';
} else {
cout << root->val << ' ';
self(self, root->left);
self(self, root->right);
}
};
dfs(dfs, root);
return cout.str();
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
std::stringstream cin(data);
auto dfs = [&](auto &&self)->TreeNode* {
int val;
cin >> val;
if (val == 1e9) return nullptr;
TreeNode *node = new TreeNode(val);
node->left = self(self);
node->right = self(self);
return node;
};
return dfs(dfs);
}
};
JZ38、字符串的排列
分析
std::next_permutation
秒了
代码实现
class Solution {
public:
vector<string> Permutation(string str) {
std::vector<std::string> ans;
std::sort(str.begin(), str.end());
do {
ans.push_back(str);
} while (std::next_permutation(str.begin(), str.end()));
return ans;
}
};
JZ39、数组中出现次数超过一半的数字
分析
std::unordered_map
记录一下所有数字出现次数即可
代码实现
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int>& nums) {
std::unordered_map<int, int> mp;
for (auto x : nums) {
mp[x] += 1;
}
int ans = -1;
for (auto [x, cnt] : mp) {
if (cnt * 2 >= size(nums)) {
ans = x;
break;
}
}
return ans;
}
};
JZ40、最小的K个数
分析
排序一下让后删除最后的\(n-k\)个数即可
代码实现
class Solution {
public:
vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
std::sort(input.begin(), input.end());
input.erase(input.begin() + k, input.end());
return input;
}
};
JZ41、数据流中的中位数
分析
使用两个std::priority_queue
分别维护一半的数组,中位数即位两个堆顶的平均数,或者小根堆的堆顶。
代码实现
class Solution {
public:
std::priority_queue<int, std::vector<int>, std::greater<>> pre;
std::priority_queue<int> suf;
void insert(int num){
pre.push(num);
while (size(pre) > size(suf) + 1) {
int a = pre.top();
pre.pop();
suf.push(a);
}
while (size(pre) && size(suf) && pre.top() < suf.top()) {
int a = pre.top(), b = suf.top();
pre.pop(), suf.pop();
suf.push(a), pre.push(b);
}
}
double getMedian(){
if (size(pre) == size(suf)) {
return 1. * (pre.top() + suf.top()) / 2;
} else {
return pre.top();
}
}
};
JZ43、从1到n整数中1出现的次数
分析
枚举每个位置上1出现的次数即可。
代码实现
class Solution {
public:
int numberOf1Between1AndN_Solution(int n) {
int ans = 0, p = 1, x = 0;
while (n) {
int t = n % 10;
if (p == 1) {
ans += n / 10 + (t != 0);
} else {
if (t <= 1) {
ans += n / 10 * p + (x + 1) * t;
} else {
ans += (n / 10 + 1) * p;
}
}
x = p * t + x;
p *= 10, n /= 10;
}
return ans;
}
};
JZ44、数字序列中某一位的数字
分析
优先找到这个下标所属的位数,然后就可以快速定位到是哪一个数字。
代码实现
class Solution {
public:
int digitAtIndex(int n) {
if (n <= 9) return n;
using i64 = long long;
n -= 10;
i64 p = 90, len = 2;
while (n >= p * len) {
n -= p * len;
p *= 10, len += 1;
}
int y = std::pow(10, len - 1) + n / len;
return std::to_string(y)[n % len] - '0';
}
};
JZ45、把数组排成最小的数
分析
直接进行排序即可判断哪两个数相加更小
代码实现
class Solution {
public:
string printMinNumber(vector<int>& nums) {
std::sort(nums.begin(), nums.end(),
[&](const int a, const int b) {
return std::to_string(a) + std::to_string(b) < std::to_string(b) + std::to_string(a);
});
std::string ans;
for (int i = 0; i < (int)size(nums); ++i) {
ans += std::to_string(nums[i]);
}
return ans;
}
};
JZ46、把数字翻译成字符串
代码实现
class Solution {
public:
int getTranslationCount(std::string s) {
int n = size(s);
std::vector<int> f(n);
f[n - 1] = 1;
for (int i = n - 2; i >= 0; --i) {
f[i] = f[i + 1];
if (s[i] == '1' || (s[i] == '2' && s[i + 1] < '6')) {
f[i] += f[i + 2];
}
}
return f[0];
}
};
JZ47、礼物的最大价值
代码实现
class Solution {
public:
int getTranslationCount(std::string s) {
int n = size(s);
std::vector<int> f(n);
f[n - 1] = 1;
for (int i = n - 2; i >= 0; --i) {
f[i] = f[i + 1];
if (s[i] == '1' || (s[i] == '2' && s[i + 1] < '6')) {
f[i] += f[i + 2];
}
}
return f[0];
}
};
JZ47、礼物的最大价值
分析
双指针维护一下集合中字符的重复性即可
代码实现
class Solution {
public:
int longestSubstringWithoutDuplication(std::string s) {
int n = size(s);
std::vector<bool> used(26);
int l = 0, r = 0, ans = 0;
while (r < n) {
while (l < r && used[s[r] - 'a']) {
used[s[l] - 'a'] = false;
l += 1;
}
used[s[r] - 'a'] = true;
r += 1;
ans = std::max(r - l, ans);
}
return ans;
}
};
JZ48、最长不含重复字符的子字符串
分析
双指针维护一下集合中字符的重复性即可
代码实现
class Solution {
public:
int longestSubstringWithoutDuplication(std::string s) {
int n = size(s);
std::vector<bool> used(26);
int l = 0, r = 0, ans = 0;
while (r < n) {
while (l < r && used[s[r] - 'a']) {
used[s[l] - 'a'] = false;
l += 1;
}
used[s[r] - 'a'] = true;
r += 1;
ans = std::max(r - l, ans);
}
return ans;
}
};
JZ49、丑数
分析
优先队列维护未被使用过的最小值并添加到std::set
中去重
代码实现
class Solution {
public:
int getUglyNumber(int n) {
std::set<long long> st = {1};
std::priority_queue<long long, std::vector<long long>, std::greater<>> heap;
heap.push(1);
while (size(st) < 3LL * n) {
auto x = heap.top();
heap.pop();
for (auto p : {2, 3, 5}) {
if (!st.count(x * p)) {
heap.push(x * p);
st.insert(x * p);
}
}
}
while (size(st) != n) {
st.erase(prev(st.end()));
}
return *st.rbegin();
}
};
JZ50、第一个只出现一次的字符
代码实现
class Solution {
public:
char firstNotRepeatingChar(string s) {
if (size(s) == 0) return '#';
std::unordered_map<char, std::vector<int>> mp;
for (int i = 0; i < size(s); ++i) {
mp[s[i]].push_back(i);
}
char ans = -1, pos = 1E9;
for (char c = 'a'; c <= 'z'; ++c) {
if (size(mp[c]) == 1) {
if (mp[c][0] < pos) {
ans = c;
pos = mp[c][0];
}
}
}
for (char c = 'A'; c <= 'Z'; ++c) {
if (size(mp[c]) == 1) {
if (mp[c][0] < pos) {
ans = c;
pos = mp[c][0];
}
}
}
if ((int)ans == -1) {
return '#';
} else {
return ans;
}
}
};
JZ51、数组中的逆序对
代码实现
class Solution {
public:
int inversePairs(vector<int>& nums) {
using i64 = long long;
auto mergeSort = [&](auto &&self, int l, int r)->i64 {
if (l >= r) return 0;
int mid = l + r >> 1;
int ans = self(self, l, mid) + self(self, mid + 1, r);
std::vector<int> tmp;
int i = l, j = mid + 1;
while (i <= mid && j <= r) {
if (nums[i] <= nums[j]) {
tmp.push_back(nums[i++]);
} else {
ans += mid - i + 1;
tmp.push_back(nums[j++]);
}
}
while (i <= mid) {
tmp.push_back(nums[i++]);
}
while (j <= r) {
tmp.push_back(nums[j++]);
}
for (int i = 0; i < size(tmp); ++i) {
nums[l + i] = tmp[i];
}
return ans;
};
return mergeSort(mergeSort, 0, size(nums) - 1);
}
};
JZ52、两个链表的第一个公共结点
分析
快慢指针跑一遍即可
代码实现
class Solution {
public:
ListNode* FindFirstCommonNode(ListNode* pHead1, ListNode* pHead2) {
if (pHead1 == nullptr || pHead2 == nullptr) {
return nullptr;
}
ListNode *p1 = pHead1, *p2 = pHead2;
while (p1 != p2) {
p1 = p1->next;
if (p1 == nullptr) {
p1 = pHead2;
pHead2 = nullptr;
}
p2 = p2->next;
if (p2 == nullptr) {
p2 = pHead1;
pHead1 = nullptr;
}
}
return p1;
}
};
JZ53、数字在升序数组中出现的次数
分析
二分至相同数字的左右端点即可
代码实现
class Solution {
public:
int getNumberOfK(vector<int> &nums , int k) {
if (size(nums) == 0) return 0;
int n = size(nums);
int l = 0, r = n - 1;
while (l < r) {
int mid = l + r >> 1;
if (nums[mid] < k) {
l = mid + 1;
} else {
r = mid;
}
}
if (nums[r] != k) {
return 0;
}
int pl = r;
l = 0, r = n - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (nums[mid] <= k) {
l = mid;
} else {
r = mid - 1;
}
}
return r - pl + 1;
}
};
JZ54、二叉搜索树的第k个节点
分析
即求前序遍历的第k的点
代码实现
class Solution {
public:
TreeNode* kthNode(TreeNode* root, int k) {
int cnt = 0;
std::vector<TreeNode *> ans;
auto dfs = [&](auto &&self, TreeNode *u)->void {
if (u == nullptr) return ;
self(self, u->left);
ans.push_back(u);
self(self, u->right);
};
dfs(dfs, root);
return ans[k - 1];
}
};
JZ55、二叉树的深度
代码实现
class Solution {
public:
int treeDepth(TreeNode* root) {
int maxDep = 0;
std::vector<TreeNode *> ans;
auto dfs = [&](auto &&self, TreeNode *u, int dep)->void {
if (u == nullptr) return ;
maxDep = std::max(maxDep, dep);
self(self, u->left, dep + 1);
ans.push_back(u);
self(self, u->right, dep + 1);
};
dfs(dfs, root, 1);
return maxDep;
}
};
JZ56、数组中只出现一次的两个数字
代码实现
class Solution {
public:
vector<int> findNumsAppearOnce(vector<int>& nums) {
int n = size(nums), xy = 0;
for (int i = 0; i < n; ++i) {
xy ^= nums[i];
}
int k = 31 - __builtin_clz(xy);
int x = 0;
for (int i = 0; i < n; ++i) {
if ((nums[i] >> k & 1) == 1) {
x ^= nums[i];
}
}
return {x, x ^ xy};
}
};
JZ57、数组中只出现一次的两个数字
分析
对原数组进行排序后枚举每个数字进行二分查找target - nums[i]
是否存在。
代码实现
class Solution {
public:
vector<int> findNumbersWithSum(vector<int>& nums, int target) {
std::sort(nums.begin(), nums.end());
for (int i = 0; i < size(nums); ++i) {
auto pos = std::lower_bound(nums.begin(), nums.end(), target - nums[i]);
if (pos != nums.end()) {
if (nums[i] + (*pos) == target) {
return {nums[i], *pos};
}
}
}
return {};
}
};
未完待续...