牛客网-剑指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, std::greater>

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待赋值的节点)

  1. 全部节点进入数组,因此可以更具坐标索引
  2. 根节点入队,设此时idx坐标为0(根节点坐标),idx+1就是左子,idx+2就是右子
  3. 如果左或右不是空指针,加入queue(我们拿出左右孩子给根节点的left和right赋值,左右孩子本身也要加入queue中,等待给他们的left和right赋值)
  4. 一次用掉两个idx,所以每次更新要+=2, 空指针情况咋办?👴这queue空指针这种臭弟弟也配进来?(所以还有一个if(!vNode[0])的判断),保证根节点也不是空的
  5. 当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;
    }
};

7.搜索

7.1.二维数组中的查找

7.2.旋转数组的最小数字(二分查找)

7.3.数组中数字出现的次数(二分查找)

8.全排列

8.1.字符串的排列

posted @ 2020-07-21 16:44  linsinan1995  阅读(125)  评论(0)    收藏  举报