数据结构非线性部分(二)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;
    }
}

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

pre[i][j]=pre[i−1][j]+pre[i][j−1]−pre[i−1][j−1]+A[i][j]

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;
}
相关推荐
wang6021252182 小时前
Git部署项目配置密钥-Linux系统
linux·运维·git
菜鸟233号2 小时前
力扣474 一和零 java实现
java·数据结构·算法·leetcode·动态规划
xlp666hub2 小时前
链表与它在 Linux 内核中的实现
linux·数据结构
克里斯蒂亚诺更新2 小时前
宝塔 服务器一个端口页面访问另外一个服务器的端口页面
运维·服务器
翼龙云_cloud2 小时前
阿里云渠道商:如何使用弹性伸缩同时管理实例和托管实例?
服务器·阿里云·云计算
super杨某人2 小时前
算法十日谈:双指针
数据结构·算法
倔强的石头1062 小时前
【Linux指南】进程控制系列(四)进程替换 ——exec 系列函数全解析与应用
linux·运维·bash
悾说2 小时前
xRDP实现Linux图形化通过Windows RDP访问Linux远程桌面
linux·运维·windows
LeetCode天天刷2 小时前
1348 推文计数【区间】
java·服务器·windows