牛客网-剑指offer刷题记录
1.list
1.1.从尾到头打印链表
方法:
- 递归
- 遍历后翻转
- stack
vector<int> printListFromTailToHead(ListNode* head) {
if (!head) return {};
vector<int> res {};
while(head) {
res.push_back(head->val);
head = head->next;
}
return vector<int>{res.rbegin(), res.rend()};
}
1.2.链表中倒数第k个结点
遍历取size-k
快慢指针
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
vector<ListNode*> res {};
while(pListHead) {
res.push_back(pListHead);
pListHead = pListHead->next;
}
if (k > res.size() || k == 0) return nullptr;
return res[res.size()-k];
}
快慢指针, 慢指针比快指针慢k步, 当快指针到尾了,慢指针就是倒数第k个(要判断是否k > cnt,可能链表没有k个数)
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
ListNode *pFast = pListHead, *pSlow = pListHead;
int cnt = 0;
while (pFast)
{
pFast = pFast->next;
if (cnt >= k)
pSlow = pSlow->next;
cnt ++;
}
if (k > cnt) return nullptr;
return pSlow;
}
ListNode* findKthToTail(ListNode* pListHead, int k) {
auto next_k_node = pListHead;
while (--k && next_k_node)
{
next_k_node = next_k_node->next;
}
if (!next_k_node) return nullptr;
auto node = pListHead;
while(next_k_node->next)
{
next_k_node = next_k_node->next;
node = node->next;
}
return node;
}
1.3.翻转链表
- 递归 时间On,空间On
- 遍历 On, O1
prev,cur 和一个next
ListNode* ReverseList(ListNode* pHead) {
if (!pHead || !pHead->next) return pHead;
ListNode *prevNode = nullptr;
ListNode *cur = pHead;
while(cur)
{
ListNode *next = cur->next;
cur->next = prevNode;
prevNode = cur, cur = next;
}
return prevNode;
}
1.4.链表中环的入口结点
快慢指针详解 注意慢指针肯定一圈都没走完,因为快指针能追上他
x+y+n*(y+z) = 2(x+y)
x=(n−1)×(y+z)+z
当x的距离就是n-1圈加上z,走了x步后的快指针(相遇后改为速度1)会与慢指针相遇
ListNode* EntryNodeOfLoop(ListNode* pHead) {
ListNode *fast = pHead;
ListNode *slow = pHead;
while (fast && slow) {
slow = slow->next;
fast = fast->next;
if (fast) fast = fast -> next;
else return nullptr;
if (fast == slow) {
slow = pHead;
while (fast != slow) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
}
}
1.5.合并两个排序数组
不new,只用修改pHead1或者pHead2也可以。
ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
if (!pHead1) return pHead2;
if (!pHead2) return pHead1;
ListNode* node = new ListNode(0);
auto node_head = node;
while(pHead1 && pHead2){
if (pHead1->val > pHead2->val) {
node->next = new ListNode(pHead2->val);
pHead2 = pHead2->next;
node = node->next;
} else {
node->next = new ListNode(pHead1->val);
pHead1 = pHead1->next;
node = node->next;
}
}
while (pHead1) {
node->next = new ListNode(pHead1->val);
node = node->next;
pHead1 = pHead1->next;
}
while (pHead2) {
node->next = new ListNode(pHead2->val);
node = node->next;
pHead2 = pHead2->next;
}
auto res = node_head->next;
delete node_head;
return res;
}
1.6.复杂链表的复制
原始node后面new一个一模一样的node,则random若存在新的node的random指向node->random->next;
RandomListNode* Clone(RandomListNode* pHead)
{
if (!pHead) return nullptr;
auto head = pHead;
while(pHead) {
auto next = pHead->next;
pHead->next = new RandomListNode(pHead->label);
pHead->next->next = next;
pHead = next;
}
pHead = head;
while(pHead) {
if (pHead->random) pHead->next->random = pHead->random->next;
pHead = pHead->next->next;
}
auto res = new RandomListNode(-1);
auto res_head = res;
while(head) {
auto next = head->next->next;
res->next = head->next;
res = res->next;
head->next = next;
head = next;
}
return res_head->next;
}
1.7.两个链表的第一个公共结点
一个链表遍历完了就到另一个的头
两种情况下,都会在遍历结束时(没有公共节点)或者公共节点处停下。
https://www.acwing.com/solution/content/1773/
方法1:先计算出两个链表的长度,可以让比较长的先走两个链表长度之差的步数,两个再一起走。(把公共节点对齐)
方法2:不同部分为a, 和b,公共部分为c;a + c + b = b + c + a;让两个一起走,a走到头就转向b, b走到头转向a,则在公共部分相遇
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
ListNode* pHead1_cp = pHead1;
ListNode* pHead2_cp = pHead2;
while (pHead1 != pHead2) {
if (!pHead1) pHead1 = pHead2_cp;
else pHead1 = pHead1->next;
if (!pHead2) pHead2 = pHead1_cp;
else pHead2 = pHead2->next;
}
return pHead1;
}
1.8.圆圈中最后剩下的数字
约瑟夫问题(枪毙问题)
- 链表
list::erase 会返回下一个iterator
int LastRemaining_Solution(int n, int m)
{
if (n==0) return -1;
list<int> l;
for (int i = 0; i < n; ++i) l.push_back(i);
auto it = l.begin();
while (l.size() > 1) {
for (int i=0; i < m-1; i++) {
it++;
if (it == l.end()) it = l.begin();
}
it = l.erase(it);
if (it == l.end()) it = l.begin();
}
return l.front();
}
- 递推
找映射关系
class Solution {
public:
int lastRemaining(int n, int m){
if(n==1)
return 0;
else
return (lastRemaining(n-1,m)+m)%n;
}
};
1.9.O(1)删除节点
class Solution {
public:
void deleteNode(ListNode* node) {
if (!node) return;
auto next = node->next;
node->val = next->val;
node->next = next->next;
delete next;
}
};
1.10. 删除链表中重复的节点
引入dummy节点 保证边界情况。 只更新dummy->next 不更新dummy!活用
ListNode* deleteDuplication(ListNode* head) {
if (!head) return head;
auto dummy = new ListNode(-1);
auto pDummy = dummy;
dummy->next = head;
while (dummy->next) {
auto next = dummy->next->next;
while (next && dummy->next->val == next->val) next = next->next;
// not update
if (dummy->next->next == next) dummy = dummy->next;
dummy->next = next;
}
return pDummy->next;
}
with memory management
ListNode* deleteDuplication(ListNode* head) {
if (!head) return head;
auto dummy = new ListNode(-1);
auto pDummy = dummy;
dummy->next = head;
while (dummy->next) {
auto next = dummy->next->next;
while (next && dummy->next->val == next->val) {
auto prev = next;
next = next->next;
delete prev;
}
// not update
if (dummy->next->next == next) dummy = dummy->next;
else delete dummy->next;
dummy->next = next;
}
auto res = pDummy->next;
delete pDummy;
return res;
}
2.队列、栈和堆
2.1.用两个栈实现队列
class MyQueue {
struct MyStack {
MyStack() : head(0) {}
void push(int x) { ele[head++] = x; }
int top() { return ele[head-1]; }
int pop() {head--;}
bool empty() {return head<=0;}
int ele[10000], head;
};
public:
/** Initialize your data structure here. */
MyQueue() {}
/** Push element x to the back of queue. */
void push(int x) {
while(!rstk.empty()) {
buffer_stk.push(rstk.top()); rstk.pop();
}
buffer_stk.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
while(!buffer_stk.empty()) {
rstk.push(buffer_stk.top()); buffer_stk.pop();
}
int res = rstk.top();
rstk.pop();
return res;
}
/** Get the front element. */
int peek() {
if (buffer_stk.empty()) return rstk.top();
return buffer_stk.ele[0];
}
/** Returns whether the queue is empty. */
bool empty() {
return buffer_stk.empty() && rstk.empty();
}
private:
MyStack buffer_stk;
MyStack rstk;
};
/**
* 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();
*/
2.2. 栈的压入、弹出序列
搞个栈模拟就好,一次进一个,while判断弹出几个,若pop vec没空,就是错误的
class Solution {
public:
bool isPopOrder(vector<int> pushV,vector<int> popV) {
if(pushV.size()!=popV.size()) return false;
stack<int> s {}; int pop_idx=0;
for (int i=0; i<pushV.size(); i++)
{
s.push(pushV[i]);
while (!s.empty() && s.top() == popV[pop_idx])
{
pop_idx++;
s.pop();
}
}
return pop_idx == popV.size();
}
};
2.3.包含min函数的栈
class MinStack {
public:
/** initialize your data structure here. */
MinStack() : head(0), minima(0x7fffffff) {
}
void push(int x) {
if (x < minima) {minima = x, min_idx = head;};
ele[head++] = x;
}
void pop() {
head--;
if (min_idx == head) {
minima = head == 0 ?
0x7fffffff :
*std::min_element(ele, ele+head);
}
}
int top() {
return ele[head-1];
}
int getMin() {
return minima;
}
private:
int ele[100000], head, minima, min_idx;
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
2.4.翻转单词顺序
用堆也行, getline(iss, token, ' ')切分
class Solution {
public:
string reverseWords(string s) {
istringstream iss(s);
string res {};
string token {};
vector<string> strVec {};
while (getline(iss, token, ' '))
{
strVec.push_back(token);
}
for (int i=strVec.size()-1; i>=0; --i)
res += strVec[i] + " ";
return res.substr(0, res.size()-1);
}
};
先翻转全部,切分后再对单词进行翻转
class Solution {
public:
string reverseWords(string s) {
reverse(s.begin(), s.end());
for (size_t idx = 0, idx_end = 0; idx != string::npos; )
{
idx_end = s.find(' ', idx);
if (idx_end != string::npos) {
reverse(s.begin()+idx, s.begin()+idx_end);
} else {
reverse(s.begin()+idx, s.end());
}
idx = idx_end!=string::npos ?
idx_end+1 :
string::npos;
}
return s;
}
};
2.5.滑动窗口的最大值
经典单调队列,情况分析比较复杂,注意储存的是坐标而不是元素
class Solution {
public:
vector<int> maxInWindows(vector<int>& nums, int k) {
deque<int> q {0}; vector<int> vec {};
for (int idx = 0; idx < nums.size(); idx++) {
while(!q.empty() && nums[q.back()] <= nums[idx]) q.pop_back();
q.push_back(idx);
if (idx-q.front() >= k) q.pop_front();
if (idx+1 >= k) vec.push_back(nums[q.front()]);
}
return vec;
}
};
class Solution {
public:
vector<int> maxInWindows(vector<int>& nums, int k) {
if (nums.size() == 0) return {};
vector<int> res {};
deque<int> q {0};
for (int i=1; i<k; i++) {
while (!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
}
for (int i=k; i<nums.size(); i++) {
res.push_back(nums[q.front()]);
if (i-q.front() > k-1)
q.pop_front();
while(!q.empty() && nums[i] >= nums[q.back()]) {
q.pop_back();
}
q.push_back(i);
}
res.push_back(nums[q.front()]);
return res;
}
};
2.6.最小的k个数
排序输出前k
class Solution {
public:
vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
sort(input.begin(), input.end());
if (input.size() < k) return input;
return {input.begin(), input.begin()+k};
}
};
小根堆priority_queue<int, std::vector
class Solution {
public:
vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
priority_queue<int, std::vector<int>, std::greater<int>> heap{};
for(auto &e : input) heap.push(e);
vector<int> vec{};
while(k--) {
vec.push_back(heap.top());
heap.pop();
}
return vec;
}
};
2.7.堆的实现
插入
删除
大根堆本质就是一个父节点比子节点要大的二叉树,右节点要比左节点大,插入时把元素放到树的尾部,再往上交换。
删除时,头节点与尾节点交换,然后删除尾节点, 再往下交换。
3.树
三个遍历别忘了
- 前序遍历:1 2 4 5 7 8 3 6 根结点 ---> 左子树 ---> 右子树
- 中序遍历:4 2 7 5 8 1 3 6 左子树 ---> 根结点 ---> 右子树
- 后序遍历:4 7 8 5 2 6 3 1 左子树 ---> 右子树 ---> 根结点
- 层次遍历:1 2 3 4 5 6 7 8
3.1.二叉树的下一个节点(中序)
熟练递归顺序!
class Solution {
public:
TreeNode* inorderSuccessor(TreeNode* p) {
if (!p) return p;
// 左子,根,右子
// if 有右,往右走左到底
if (p->right) {
p = p->right;
while(p->left) p = p->left;
} else {
// 无右,看是其是父的左还是右节点(p == p->father->right)
// 若p为p->father->left, 后继就是p->father, 反之则是除了第一个右节点不为p的父节点
while(p->father && p == p->father->right) p = p->father;
p = p->father;
}
return p;
}
};
3.2.树的子结构
经典递归问题
class Solution {
public:
bool hasSubtree(TreeNode* pRoot1, TreeNode* pRoot2) {
if (!pRoot1 || !pRoot2) return false;
if (compare(pRoot1, pRoot2)) return true;
// 遍历树root1所有节点来比较, 任意节点下有即可,所以应该是||
return hasSubtree(pRoot1->left, pRoot2) || hasSubtree(pRoot1->right, pRoot2);
}
// 由顶向下
bool compare(TreeNode* n, TreeNode* m) {
if (!m) return true; // 子树可以缺少结点,不能多
if (!n || n -> val != m -> val) return false;
// 必须全true
return compare(n->left, m->left) && compare(n->right, m->right);
}
};
3.3.二叉树的镜像
ez递归
class Solution {
public:
void mirror(TreeNode* root) {
if (!root) return;
swap(root->left, root->right);
if (root->left) mirror(root->left);
if (root->right) mirror(root->right);
}
};
3.4.对称的二叉树
找规律+dfs
// 抄自y总
// 作者:yxc
// 链接:https://www.acwing.com/solution/content/747
// 来源:AcWing
class Solution {
public:
bool isSymmetric(TreeNode* root) {
return !root || dfs(root->left, root->right);
}
bool dfs(TreeNode*p, TreeNode*q)
{
// 存在空指针,如果都为空则true反之false
if (!p || !q) return !p && !q;
return p->val == q->val && dfs(p->left, q->right) && dfs(p->right, q->left);
}
};
根的节点中序遍历, 右节点反中序遍历(右节点-根-左节点),能得到镜面效果,而且要用非递归
// 作者:yxc
// 链接:https://www.acwing.com/solution/content/747
// 来源:AcWing
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (!root) return true;
stack<TreeNode*> left, right;
TreeNode *lc = root->left;
TreeNode *rc = root->right;
while(lc || rc || left.size())
{
while (lc && rc)
{
left.push(lc), right.push(rc);
lc = lc->left, rc = rc->right;
}
if (lc || rc) return false;
lc = left.top(), rc = right.top();
left.pop(), right.pop();
if (lc->val != rc->val) return false;
lc = lc->right, rc = rc->left;
}
return true;
}
};
bfs暴力 shit code
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (!root) return true;
deque<TreeNode*> dq {{root->left, root->right}};
while(!dq.empty()) {
auto size_level = dq.size();
for (size_t idx = 0; idx < size_level/2; idx++) {
auto idx_back = size_level-idx-1;
// 有空指针,且不同时为空返回false
if ((!dq[idx] || !dq[idx_back]) && (dq[idx] != dq[idx_back])) return false;
// 只需要判断dq[idx]就知道dq[idx_back]是否为空了(不同的话早就已经返回false了)
if (dq[idx] && dq[idx]->val != dq[idx_back]->val) return false;
}
int cnt = 0;
// 或者开空间放node
for (size_t idx = 0; idx < size_level; idx++) {
auto node = dq.front(); dq.pop_front();
if (node) {
// cnt += 2, cnt == size_level*2
dq.push_back(node->left);
dq.push_back(node->right);
} else {
cnt++;
dq.push_back(node);
dq.push_back(node);
}
}
if (cnt == size_level) break;
}
return true;
}
};
3.5.不分行从上往下打印二叉树
bfs + 计数
class Solution {
public:
vector<int> printFromTopToBottom(TreeNode* root) {
if (!root) return {};
vector<int> vec {};
queue<TreeNode*> q {{root}};
while (!q.empty()) {
auto node = q.front(); q.pop();
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
vec.push_back(node->val);
}
return vec;
}
};
看到一堆臭弟弟疯狂emplace_back(lvalue),搞得还以为我记错了。复习一下 emplace_back和lvalue -> 实际就是copy ctor
https://stackoverflow.com/questions/55222962/stdvectoremplace-back-with-lvalue-expression
3.6.分行从上往下打印二叉树
取反操作是 flag = !flag不是flag != flag
直接用size开好空间,又快又不用reverse
class Solution {
public:
vector<vector<int>> printFromTopToBottom(TreeNode* root) {
vector<vector<int>> res {};
if (!root) return res;
queue<TreeNode*> q {{root}};
bool flag = true;
while (!q.empty()) {
auto size = q.size();
vector<int> vLevel(size);
for (size_t i = 0; i < size; i++) {
auto node = q.front(); q.pop();
auto idx = flag? i : size-1-i;
vLevel[idx] = node->val;
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
flag = !flag;
res.emplace_back(move(vLevel));
}
return res;
}
};
3.7.序列化和反序列化二叉树
这种vector+queue的变化题很有意思,牢记!
全bfs
序列化-> 正常bfs, queue空了结束,记得补上null
反序列化 -> bfs, queue + vector配合达到,遇到空子树跳过的效果(queue中弹出根节点,再用坐标idx定位其左右孩子)
vector->找孩子
queue->找根的(存left, right待赋值的节点)
- 全部节点进入数组,因此可以更具坐标索引
- 根节点入队,设此时idx坐标为0(根节点坐标),idx+1就是左子,idx+2就是右子
- 如果左或右不是空指针,加入queue(我们拿出左右孩子给根节点的left和right赋值,左右孩子本身也要加入queue中,等待给他们的left和right赋值)
- 一次用掉两个idx,所以每次更新要+=2, 空指针情况咋办?👴这queue空指针这种臭弟弟也配进来?(所以还有一个if(!vNode[0])的判断),保证根节点也不是空的
- 当queue空了,就收工了
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
if (!root) return "[null]";
string res {'[' + to_string(root->val) + ','};
deque<TreeNode*> dq {{root}};
while(!dq.empty()) {
auto node = dq.front(); dq.pop_front();
if (node->left) {
dq.push_back(node->left);
res += to_string(node->left->val) + ',';
} else res += "null,";
if (node->right) {
dq.push_back(node->right);
res += to_string(node->right->val) + ',';
} else res += "null,";
}
res[res.size()-1] = ']';
return res;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(const string& data) {
vector<TreeNode*> vNode {};
istringstream iss {data.substr(1, data.size()-2)};
string token {};
while (getline(iss, token, ',')) {
if (token =="null")
vNode.emplace_back(nullptr);
else
vNode.emplace_back(new TreeNode(stoi(token)));
}
// root is valid!
if (!vNode[0]) return nullptr;
// root
queue<TreeNode*> qNode {{vNode[0]}};
int idx = 0;
while (!qNode.empty()) {
auto root = qNode.front(); qNode.pop();
root->left = vNode[idx+1];
root->right = vNode[idx+2];
if (root->left) qNode.push(root->left);
if (root->right) qNode.push(root->right);
idx+=2;
}
return vNode[0];
}
};
3.8.二叉树中和为某一值的路径
dfs+vector回溯(在分支前push, 分支结束后pop)
class Solution {
public:
vector<vector<int>> ans {};
vector<int> path {};
vector<vector<int>> findPath(TreeNode* root, int sum) {
dfs(root, sum);
return ans;
}
void dfs(TreeNode* root, int sum) {
if (!root) return;
sum -= root->val;
path.push_back(root->val);
if (!root->left && !root->right && sum == 0) ans.push_back(path);
dfs(root->left, sum);
dfs(root->right, sum);
path.pop_back();
}
};
3.9.二叉树深度
ez递归
递归
class Solution {
public:
int treeDepth(TreeNode* root) {
if (!root) return 0;
return max(treeDepth(root->left), treeDepth(root->right)) + 1;
}
};
回溯版
class Solution {
public:
int dep = 0;
int mDep = 0;
int treeDepth(TreeNode* root) {
helper(root);
return mDep;
}
void helper(TreeNode* root) {
if (!root) return;
dep ++;
if (dep > mDep) mDep = dep;
helper(root->left);
helper(root->right);
dep --;
}
};
3.10.重建二叉树
前序中序性质。
- 先利用前序遍历找根节点:前序遍历的第一个数,就是根节点的值;
- 在中序遍历中找到根节点的位置 kk,则 kk 左边是左子树的中序遍历,右边是右子树的中序遍历;
- 假设左子树的中序遍历的长度是 ll,则在前序遍历中,根节点后面的 ll 个数,是左子树的前序遍历,剩下的数是右子树的前序遍历;
- 有了左右子树的前序遍历和中序遍历,我们可以先递归创建出左右子树,然后再创建根节点;
class Solution {
public:
map<int, int> hash;
vector<int> preorder, inorder;
TreeNode* buildTree(vector<int>& mpreorder, vector<int>& minorder) {
preorder = mpreorder, inorder = minorder;
size_t size = preorder.size();
for(size_t i=0; i<size; i++) hash[inorder[i]] = i; // 前序的值在中序的位置
return dfs(0, size-1, 0, size-1);
}
TreeNode* dfs(int preNode, int preB, int inNode, int inB) {
if (preNode > preB) return nullptr;
auto root = new TreeNode(preorder[preNode]);
int k = hash[preorder[preNode]];
// preorder: root(preNode), left tree root(preNode+1), ...(preNode+k-inNode->length of left tree), right tree root, ...
// inorder : ..., root of left tree(k-1), root(k), ...
root->left = dfs(preNode+1,
preNode+k-inNode,
inNode,
k-1);
root->right = dfs(preNode+k-inNode+1,
preB,
k+1,
inB);
return root;
}
};
3.11.二叉搜索树的第k个结点
bst
- 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若任意节点的右子树不空,则右子树上所有节点的值均大于或等于它的根节点的值;
- 任意节点的左、右子树也分别为二叉查找树;
中序遍历,走过的顺序就是节点大小顺序。
class Solution {
public:
TreeNode* kthNode(TreeNode* root, int k) {
auto a = root;
stack<TreeNode *> sroot {};
int cnt = 0;
while (root || !sroot.empty()) {
cnt ++;
while(root) {
// cout<< root->val<< " ";
sroot.push(root);
root=root->left;
}
if(!sroot.empty()) {
root=sroot.top();
sroot.pop();
if (cnt == k){
return root;
}
root = root->right;
}
}
}
};
递归版
class Solution {
public:
TreeNode* ans;
int kk = 0;
TreeNode* kthNode(TreeNode* root, int k) {
kk = k;
dfs(root);
return ans;
}
void dfs(TreeNode* root) {
if (!root) return;
dfs(root->left);
kk--;
if (kk == 0) ans = root;
// if kk is greater than k, stop traversal
if (kk > 0) dfs(root->right);
}
};
3.12.二叉搜索树与双向链表
bst的中序遍历就是排序好的序列
class Solution {
public:
TreeNode *mRoot;
TreeNode *dummy;
TreeNode* convert(TreeNode* root) {
if (!root) return root;
mRoot = new TreeNode(1);
dummy = mRoot;
dfs(root);
auto result = dummy->right;
// release
delete dummy;
result->left = nullptr;
return result;
}
void dfs(TreeNode *root) {
if (!root) return;
// inorder
dfs(root->left);
mRoot->right = new TreeNode(root->val);
mRoot->right->left = mRoot;
mRoot = mRoot->right;
dfs(root->right);
}
};
3.13.二叉搜索树后序遍历
4.hash
4.1.第一个只出现一次的字符
map不需要处理Map[i]=0的情况,直接++就行了,实现中有处理。
class Solution {
public:
char firstNotRepeatingChar(string s) {
map<char, int> cmap {};
for (auto c :s) {
// c = tolower(c);
// 为0的情况自己处理了!
cmap[c] ++;
}
for (auto c :s) {
if (cmap[c] == 1) return c;
}
return '#';
}
};
5.dfs
5.
(DFS) O(n^2 * 3^k)
在深度优先搜索中,最重要的就是考虑好搜索顺序。
我们先枚举单词的起点,然后依次枚举单词的每个字母。
过程中需要将已经使用过的字母改成一个特殊字母,以避免重复使用字符。
时间复杂度分析:单词起点一共有 n^2 个,单词的每个字母一共有上下左右四个方向可以选择,但由于不能走回头路,所以除了单词首字母外,仅有三种选择。所以总时间复杂度是 O(n^2 * 3^k)。
class Solution {
public:
bool hasPath(vector<vector<char>>& matrix, string str) {
for (int i = 0; i < matrix.size(); i ++ )
for (int j = 0; j < matrix[i].size(); j ++ )
if (dfs(matrix, str, 0, i, j))
return true;
return false;
}
bool dfs(vector<vector<char>> &matrix, string &str, int u, int x, int y) {
if (matrix[x][y] != str[u]) return false;
if (u == str.size() - 1) return true;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
char t = matrix[x][y];
matrix[x][y] = '*'; // 覆盖,不重复走
for (int i = 0; i < 4; i ++ ) {
int a = x + dx[i], b = y + dy[i];
if (a >= 0 && a < matrix.size() && b >= 0 && b < matrix[a].size()) {
if (dfs(matrix, str, u + 1, a, b)) return true;
}
}
matrix[x][y] = t; // 回溯
return false;
}
};
作者:yxc
链接:https://www.acwing.com/solution/content/728/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
public:
int dx[4] {-1, 1, 0, 0};
int dy[4] {0, 0, 1, -1};
bool hasPath(vector<vector<char>>& matrix, string &str) {
for (size_t i = 0; i < matrix.size(); i++) {
for (size_t j = 0; j < matrix[0].size(); j++) {
if (dfs(matrix, str, i, j, 0)) return true;
}
}
return false;
}
bool dfs(vector<vector<char>>& matrix, string &str, int x, int y, int idx) {
if (matrix[x][y] != str[idx]) return false;
if (idx == str.size()-1) return true;
char c = matrix[x][y];
matrix[x][y] = '*';
for (int i=0; i < 4; i++) {
int new_x = x+dx[i], new_y = y+dy[i];
if (new_x < 0 || new_x == matrix.size() ||
new_y < 0 || new_y == matrix[0].size()) continue;
// 如果有一条路全true了就会直接返回true
if (dfs(matrix, str, new_x, new_y, idx+1)) return true;
}
// 回溯
matrix[x][y] = c;
return false;
}
};
6.bfs
6.1.机器人的运动范围
class Solution {
public:
int movingCount(int threshold, int rows, int cols) {
if (rows == 0|| cols == 0) return 0;
vector<vector<bool>> matrix(rows, vector<bool>(cols));
int dx[4] {0, 0, 1, -1}, dy[4] {1, -1, 0, 0};
queue<pair<int, int>> q {{{0, 0}}};
matrix[0][0] = true;
int res = 1;
while (!q.empty()) {
auto [x, y] = q.front();
q.pop();
for (int i=0; i<4; i++) {
int new_x = x+dx[i], new_y = y+dy[i];
if (isLegit(new_x, new_y, threshold) &&
new_x >= 0 && new_x < rows && new_y >= 0 &&
new_y < cols && !matrix[new_x][new_y]) {
matrix[new_x][new_y] = true;
res ++;
q.push({new_x, new_y});
}
}
}
return res;
}
bool isLegit(int i, int j, int threshold) {
int sum = 0;
while(i) {
sum += i%10;
i/=10;
}
while(j) {
sum += j%10;
j/=10;
}
return threshold >= sum;
}
};
dfs 版本
class Solution {
public:
vector<vector<bool>> matrix;
int mthreshold;
int dx[4] {0, 0, 1, -1}, dy[4] {1, -1, 0, 0};
int mcol, mrow;
int movingCount(int threshold, int rows, int cols) {
if (rows == 0|| cols == 0) return 0;
mcol = cols; mrow = rows;
matrix = vector<vector<bool>>(rows, vector<bool>(cols, false));
mthreshold = threshold;
return dfs(0, 0);
}
int dfs(int x, int y) {
if (!isLegit(x, y) || matrix[x][y]) return 0;
int sum = 1;
matrix[x][y] = true;
for (int i=0; i<4; i++) {
int new_x = x+dx[i], new_y = y+dy[i];
if (new_x >= 0 && new_x < mrow &&
new_y >= 0 && new_y < mcol && !matrix[new_x][new_y]) {
sum += dfs(new_x, new_y);
}
}
return sum;
}
bool isLegit(int i, int j) {
int sum = 0;
while(i) {
sum += i%10;
i/=10;
}
while(j) {
sum += j%10;
j/=10;
}
return mthreshold >= sum;
}
};