数据结构非线性部分(二)review

week8-8

二叉树的前序遍历:

cpp 复制代码
void preOrder(treeNode* root) {
        treeNode* cur=nullptr;
        stack<treeNode*>st;
        st.push(root); 
        while(!st.empty()){
          cur=st.top();
          st.pop();
          cout<<cur->data<<" ";
          if(cur->right){
            st.push(cur->right);
          }
          if(cur->left){
            st.push(cur->left);
          }
        }     
    }

思路:先将root压栈,然后进入循环,对于每一次循环,输出当前栈顶元素,然后先压右孩子入栈,再压左孩子,因为栈先进后出嘛

中序遍历

核心思路

当前节点 = root

while 当前节点不为空 或者 栈不为空:

while 当前节点不为空:

将当前节点入栈

当前节点 = 当前节点.left

弹出栈顶 → visit(输出)

当前节点 = 栈顶.right

老是想不好要怎么写

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
   
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int>res;
        if(!root)return res;
        stack<TreeNode*>st;
        //st.push(root);
        TreeNode* cur=root;
        while (cur != nullptr || !st.empty()) {
            // 一路向左
            while (cur != nullptr) {
                st.push(cur);
                cur = cur->left;
            }

            // 弹栈访问
            cur = st.top();
            st.pop();
            res.push_back(cur->val);

            // 转向右子树
            cur = cur->right;
        }
        return res;
    }
};

线索化二叉树的中序遍历(不是很会)

cpp 复制代码
#include "solution.h"
#include<stack>
#include<vector>


ThreadedTreeNode* Solution::threaded(ThreadedTreeNode* root){
    if(!root)return nullptr;
    ThreadedTreeNode* cur=root; 
    stack<ThreadedTreeNode*>st;
    vector<ThreadedTreeNode*>order;
    while(cur||!st.empty()){
        while(cur){
            st.push(cur);
            cur=cur->left;
        }
        cur=st.top();
        order.push_back(cur);
        st.pop();
        cur=cur->right;
    }
    // 构建线索
    for (int i = 0; i < (int)order.size(); ++i) {
        ThreadedTreeNode* node = order[i];
        if (!node->left) {
            node->ltag = 1;
            node->left = (i == 0 ? nullptr : order[i - 1]);
        }
        if (!node->right) {
            node->rtag = 1;
            node->right = (i == order.size() - 1 ? nullptr : order[i + 1]);
        }
    }
  
    return root;
}



vector<int> Solution::inorderTraversal(ThreadedTreeNode* root){
    vector<int> ans;
    if (!root) return ans;

    ThreadedTreeNode* cur = root;
    while (cur && cur->ltag == 0) cur = cur->left;

    while (cur) {
        ans.push_back(cur->val);
        if (cur->rtag == 1)
            cur = cur->right;
        else {
            cur = cur->right;
            while (cur && cur->ltag == 0)
                cur = cur->left;
        }
    }
    return ans;
}

构建线索的时候i=0和i=n-1的细节要注意

遍历线索二叉树是重点

首先先得到第一个节点

之后以此开始按遇到的情况来讨论,进入循环后:

首先直接输出当前节点

之后看tag,如果rtag==1,说明这个右节点是之前已经遍历过的,直接输出就好

如果rtag==0,那么这个右节点就是不能马上输出的

要cur先变成右节点,然后不停的往left找,注意此时的条件是

while (cur && cur->ltag == 0)cur = cur->left;

这样才能干脆的找到下一个要输出的节点

W8-6计算前缀和

cpp 复制代码
#include<iostream>
#include<vector>
using namespace std;

int main(){
    int m,n;
    cin>>m>>n;
    vector<vector<long long>>matrix(m+1,vector<long long >(n+1,0));
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            cin>>matrix[i][j];
            matrix[i][j]=matrix[i][j]+matrix[i][j-1]+matrix[i-1][j]-matrix[i-1][j-1];
        }
    }
    int q;
    cin>>q;
    while(q--){
        int l1,r1,l2,r2;
        cin>>l1>>r1>>l2>>r2;
        l1++;
        r1++;
        l2++;
        r2++;
        long long sum=matrix[r1][r2]-matrix[r1][l2-1]-matrix[l1-1][r2]+matrix[l1-1][l2-1];
        cout<<sum<<endl;
    }
}

通过预处理的小技巧来算出前缀和

preij=prei−1j+preij−1−prei−1j−1+Aij

w8-5左孩子右兄弟遍历

cpp 复制代码
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;



int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin>>n;
    vector<int>f(n+1,-1);
    vector<vector<int>>ch(n+1);
    for(int i=2;i<=n;i++){
        cin>>f[i];
        ch[f[i]].push_back(i);
    }
    for(int i=1;i<=n;i++){
        sort(ch[i].begin(),ch[i].end());
    }
    
    queue<int>q;
    q.push(1);
    while(!q.empty()){
        int cur=q.front();q.pop();
        cout<<cur<<" ";
        if(!ch[cur].empty()){
            q.push(ch[cur][0]);
        }
        if(f[cur]!=-1){
            int bro=-1;
            for(unsigned int i=0;i<=ch[f[cur]].size();i++){
                if(ch[f[cur]][i]>cur){
                    bro=ch[f[cur]][i];
                    break;
                }
            }
            if(bro!=-1)q.push(bro);
        }
    }
}

最长交错路径

cpp 复制代码
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
//public:
    // int maxlen=0;
    // void dfs(TreeNode* root,int l,int r){
    //     if(!root)return;

    //     maxlen=max(maxlen,max(l,r));
    //     dfs(root->right,r+1,0);
    //     dfs(root->left,0,l+1);
    // }
    // int longestZigZag(TreeNode* root) {
    //     if(!root)return 0;
    //     dfs(root,0,0);
    //     return maxlen;
    // }
    int ans=0;
    void dfs(TreeNode* root,int dir,int dis){//(当前结点,左/右孩子,路径长度)
        if(!root)return;//空结点返回
        ans=max(ans,dis);//更新最大值
        if(dir){//如果当前结点是其父结点的右孩子
            dfs(root->left,0,dis+1);//搜索其左孩子时,满足ZigZig,路径长度+1
            dfs(root->right,1,1);//搜索其右孩子时,不满足ZigZig,路径长度置为1
        } 
        else{//如果当前结点是其父结点的左孩子
            dfs(root->left,0,1);//搜索其左孩子时,不满足ZigZig,路径长度置为1
            dfs(root->right,1,dis+1);//搜索其右孩子时,满足ZigZig,路径长度+1
        }       
    }
public:
    int longestZigZag(TreeNode* root) {
        dfs(root->left,0,1);//0左节点
        dfs(root->right,1,1);//1右结点
        return ans;
    }

};

w8-9 逐层删除二叉树叶子节点

思路就是按高度来划分分组

cpp 复制代码
int dfs(TreeNode* root ,vector<vector<int>>&res){
    if(!root)return -1;
    int l=dfs(root->left,res);
    int r=dfs(root->right,res);
    int height=max(l,r)+1;
    if (res.size() <= height)
            res.push_back({});

        res[height].push_back(root->val);
        return height;
}

vector<vector<int>> findLeaves(TreeNode* root){
    vector<vector<int>> res;
    dfs(root,res);
    return res;
}

小根堆(Min Heap)的定义:

小根堆是一种完全二叉树 ,并且满足下面的堆序性质:

任意一个节点的值 ≤ 它的左右孩子节点的值

也就是说:

  • 堆顶(根节点)是整个堆中最小的元素

  • 每个父节点都不大于它的子节点

    cpp 复制代码
    #include<iostream>
    #include<vector>
    #include<queue>
    using namespace std;
    
    int main(){
        int t;
        cin>>t;
        while(t--){
            int n;
            cin>>n;
            vector<int>heap(n);
            for(int i=0;i<n;i++)cin>>heap[i];
            queue<int>q;
            q.push(0);
            bool isp=true;
            while(!q.empty()){
                int cur=q.front();
                q.pop();
                int l=2*cur+1;
                int r=2*cur+2;
                if(l<n){
                    if(heap[cur]>heap[l]){
                        isp=false;
                        break;
                    }
                    else q.push(l);
                }
                if(r<n){
                    if(heap[cur]>heap[r]){
                        isp=false;
                        break;
                    }
                    else q.push(r);
                }
            }
            if(isp)cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
    }

W9-3 哈夫曼树

哈夫曼树是一棵"越重要(权值越大)的叶子,离根越近"的二叉树

思路:用一个优先级队列存储哈夫曼树里面的元素(最小的在前面),每次取两个出来,然后创建一个新的节点,权重等于二者相加,这个玩意作为根节点,再放进优先级队列

W10-4 最大对称子二叉树

cpp 复制代码
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;

vector<int> weight;
vector<pair<int,int>> child;

int n;
int ans=1;

bool ismirror(int a,int b){
    //cout<<111<<endl;
    queue<int>qa;
    queue<int>qb;
    qa.push(a);
    qb.push(b);
    while(!qa.empty()&&!qb.empty()){
        int ca=qa.front();qa.pop();
        int cb=qb.front();qb.pop();
        if (ca == -1 && cb == -1) continue;
        if (ca == -1 || cb == -1) return false;
        if (weight[ca] != weight[cb]) return false;

            qa.push(child[ca].first);
            qa.push(child[ca].second);

            qb.push(child[cb].second);
            qb.push(child[cb].first);

    }
    if(qa.empty()&&qb.empty())return true;
    else return false;
}

int count(int a){
    queue<int>q;
    q.push(a);
    int cnt=0;
    while(!q.empty()){
        int cur=q.front();
        q.pop();
        if(cur!=-1)cnt++;
        else continue;
        q.push(child[cur].first);
        q.push(child[cur].second);
    }
    return cnt;
}

void max_mirror(int root){
    if(root==-1)return;
    if(ismirror(child[root].first,child[root].second)){
        //cout<<root<<endl;
        ans=max(count(root),ans);
        return;
    }
    else{
        max_mirror(child[root].first);
        max_mirror(child[root].second);
    }
}


int main(){
    cin>>n;
    weight.resize(n+1);
    child.resize(n+1);
    for(int i=1;i<=n;i++){
        cin>>weight[i];
    }
    for(int i=1;i<=n;i++){
        cin>>child[i].first>>child[i].second;
    }
    max_mirror(1);
    //cout<<111<<endl;
    cout<<ans;
}

犯了两个致命的错误:

1、不明晰对称二叉树的定义:以根节点为轴两边对称的就是对称二叉树,子树不需要对称,被题目误导了

2、没有去关心越界的问题,这里有根节点为权值为-1的情况,导致数组下标越界,这点老是没注意

W11-1 删木成林

cpp 复制代码
#include "tree.h"
#include<algorithm>
vector<TreeNode*> ans;
unordered_set<int> del;

TreeNode* dfs(TreeNode* root) {
    if(!root){
        return nullptr;
    }
    root->left=dfs(root->left);
    root->right=dfs(root->right);
    if(del.count(root->val)){
        if (root->left) ans.push_back(root->left);
        if (root->right) ans.push_back(root->right);
        return nullptr;//!!!!!!
    }
    return root;
}

bool cmp(TreeNode* a,TreeNode* b){
    return a->val<b->val;
}

vector<TreeNode*> solve(TreeNode* root, const vector<int>& to_delete) {
   for(auto p:to_delete){
    del.insert(p);
   }
   root=dfs(root);
   if(root)ans.push_back(root);//!!!!!!
    sort(ans.begin(),ans.end(),cmp);
    return ans;
}

有两个点是要注意的

1、删掉某个点之后要妥善处理它的儿子女儿

同时这个点已经置为了nullptr,因此返回的要是nullptr

2、根节点特殊处理

W11-2 合并多棵二叉搜索树

cpp 复制代码
#include "tree.h"
#include<algorithm>
#include<climits>
    unordered_map<int,TreeNode*>treemap;
    unordered_set<int>treeset;
    void insertT(TreeNode* root,vector<TreeNode*>&trees){
        queue<TreeNode*>q;
        q.push(root);
        while(!q.empty()){
            TreeNode* cur=q.front();q.pop();
            if(cur->left){
                if(treemap[cur->left->val]){
                    cur->left=treemap[cur->left->val];
                }
                q.push(cur->left);
            }
            if(cur->right){
                if(treemap[cur->right->val]){
                    cur->right=treemap[cur->right->val];
                }
                q.push(cur->right);
            }
        }
    }

    TreeNode* canMerge(vector<TreeNode*>& trees) {
       treemap.clear();
       treeset.clear();
       for(auto t:trees){
        treemap.insert({t->val,t});
        treeset.insert(t->val);
       } 
       //找到不能当儿子,只能当爹的节点
       for(auto t:trees){
        if(t->left){
            treeset.erase(t->left->val);
        }
        if(t->right){
            treeset.erase(t->right->val);
        }
       }
       int num=treeset.size();
       if(num>=2)return nullptr;
       TreeNode* root=nullptr;
       for(auto p:treeset){
        root=treemap[p];
        break;
       }
       insertT(root,trees);
       return root;
    }

关键的关键:

找到最顶上的根节点

最大生成森林

cpp 复制代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

class Unionset{
public:
    vector<int>parent;
    vector<int>size;
    Unionset(int n){
        parent.resize(n);
        size.resize(n);
        for(int i=0;i<n;i++){
            parent[i]=i;
            size[i]=1;
        }
    }

    int find(int x){
        if(parent[x]==x)return x;
        else return parent[x]=find(parent[x]);
    }

    void unite(int x,int y){
        int rx=find(x),ry=find(y);
        if(rx==ry)return ;
        if(size[rx]>size[ry]){
            parent[ry]=rx;
        }
        else if(size[rx]<size[ry]){
            parent[rx]=ry;
        }
        else{
            parent[rx]=ry;
            size[ry]++;
        }
    }

};

struct Edge {
    int u, v, w;
};


int main(){
    int t;
    cin>>t;
    while(t--){
        int n, m;
        cin >> n >> m;
        vector<Edge> edges(m);
        for(int i = 0; i < m; i++) {
            cin >> edges[i].u >> edges[i].v >> edges[i].w;
        }

        // 按权值从大到小排序(最大生成树)
        sort(edges.begin(), edges.end(), [](const Edge &a, const Edge &b){
            return a.w > b.w;
        });
        Unionset uf(n);
        long long total=0;
        for(int i=0;i<m;i++){
            if(uf.find(edges[i].u)!=uf.find(edges[i].v)){
                uf.unite(edges[i].u,edges[i].v);
                total+=edges[i].w;
            }
            //uf.unite(edges[i].u,edges[i].v);
        }
        cout<<total<<endl;
    }
}

给定一个无向图 G,它可能是一个非连通图的,你的任务是找到最大生成森林

理解:既然是无向图,又是森林,必然想到的是并查集

首先对于一连通的树,从哪个节点开始生成最大树是无所谓的,因为这个树必然会连接这个连通分量的所有节点

那么就先对所有的边的权值进行排序,排完序再每次取最大的进行生成,并且用并查集来连接

快速排序

cpp 复制代码
void mysort(vector<int>&a,int l,int r){
    if (l >= r) return;
    int pivot = a[l];
    int i = l, j = r;
    while (i <= j) {
        while (a[i] < pivot) i++;
        while (a[j] > pivot) j--;
        if (i <= j) {
            swap(a[i], a[j]);
            i++;
            j--;
        }
    }

    if (l < j)  mysort(a,l,j);
    if (i < r)  mysort(a,i,r);
}
cpp 复制代码
#include<queue>
#include<algorithm>
using namespace std;


int main(){
    int n,m;
    cin>>n>>m;
    vector<vector<int>>g(n);
    vector<int>degin(n,0);
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        g[a].push_back(b);
        degin[b]++;
    }
    queue<int>q;
    for(int i=0;i<n;i++){
        if(degin[i]==0){
            q.push(i);
        }
    }
    while(!q.empty()){
        int cur=q.front();
        q.pop();
        for(auto p:g[cur]){
            degin[p]--;
            if(degin[p]==0){
                q.push(p);
            }
        }
    }
    for(int i=0;i<n;i++){
        if(degin[i]!=0){
            cout<<"NO"<<endl;
            return 0;
        }
    }
    cout<<"YES"<<endl;
}

求第n位数字(梦魇来了孩子)

cpp 复制代码
#include <iostream>
using namespace std;

int func1(long long n) {
    long long standnumber = 1;
    long long standnextnumber = 10;
    long long standstr = 0;
    long long standnextstr = 9;
    int slen = 1;

    while (standnextstr < n) {
        standstr = standnextstr;
        standnumber = standnextnumber;
        standnextnumber *= 10;
        slen++;
        standnextstr += (standnextnumber - standnumber) * slen;
    }

    long long num = (n - standstr - 1) / slen + standnumber;
    int numsite=(n-standstr-1)%slen;
    for(int i=0;i<slen-numsite-1;i++){
        num/=10;
    }
    return num%10;
}

int main() {
    long long n;
    cin >> n;
    cout << func1(n) << endl;
    return 0;
}

最近公共祖先

cpp 复制代码
TreeNode* lcr(int p,int q,TreeNode* root){
    if(!root)return nullptr;
    if(root->val==p||root->val==q){
        return root;
    }
    TreeNode* l=lcr(p,q,root->left);
    TreeNode* r=lcr(p,q,root->right);
    if(l&&r)return root;
    else return l?l:r;
}

DAG松弛关键路径关键实践拿下

cpp 复制代码
#include<queue>
#include<climits>
#include<algorithm>
using namespace std;

int main(){
    int n,m;
    cin>>n>>m;
    vector<vector<int>>g(n+1);
    vector<int>degin(n+1,0);
    vector<int>time(n+1,0);
    vector<int>realtime(n+1,0);
    for(int i=1;i<=n;i++){
        cin>>time[i];
        realtime[i]=time[i];
    }
    for(int i=0;i<m;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v);
        degin[v]++;
    }
    queue<int>q;
    for(int i=1;i<=n;i++){
        if(degin[i]==0){
            q.push(i);
        }
    }

    while(!q.empty()){
        int cur=q.front();q.pop();
        for(auto p:g[cur]){
            realtime[p]=max(realtime[cur]+time[p],realtime[p]);
            degin[p]--;
            if(degin[p]==0)q.push(p);
        }
    }
    int max=0;
    for(int i=1;i<=n;i++){
        if(max<realtime[i])max=realtime[i];
    }
    cout<<max;
}
相关推荐
律宏阔4 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔4 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴4 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千4 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei374 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei374 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei375 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW5 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
百万蹄蹄向前冲5 小时前
双端同步!云服务器装最新Node.js v26.10全过程追踪
服务器·人工智能·node.js
GeW5 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux