欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

lintcode Trie树、二叉树、堆等

程序员文章站 2022-03-24 17:52:24
...

前言

第四周刷的是Trie树、二叉树、堆等。

此次参与刷题的共五人(嘟嘟、琼琼、东东、大智、博主)。

正题

lintcode Trie树、二叉树、堆等

442.实现Trie树

关于Trie树,刷的第一道题是 107.单词拆分1

实现相对比较简单,实现插入、搜索和以什么开头即可。


473.单词的添加与查找

插入和普通的一样,查询的时候带有.的模糊查找,把所有的都找一遍,有一个满足条件的就返回True。


132.单词搜索2

有一个字母矩阵和一个词典。找出同时在词典和矩阵(上下左右)中出现的单词。

方法:

1.先对词典每个词建立Trie树。

2.对矩阵进行搜索,满足条件的放入map中(为了避免重复)


AC代码:

struct TrieTreeNode{
    TrieTreeNode *child[26];
    bool isEnd;
    TrieTreeNode(){
        memset(child,0,sizeof(child));
        isEnd = false;
    }
};

class Solution {
public:
    /*
     * @param board: A list of lists of character
     * @param words: A list of string
     * @return: A list of string
     */
    int dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}}; //搜索的四个方向
    int m,n;  //board的长和宽
    TrieTreeNode *root = new TrieTreeNode(); //Trie的根结点
    vector<vector<bool> > vis;  //board的访问数组
    map<string,int> mp; //结果的map
    
    void insertTrie(vector<string> &words){
        int w_cnt = words.size(), w_len; //代表word的个数和每个word的长度.
        
        for(int i=0;i<w_cnt;++i){
            w_len = words[i].length();
            TrieTreeNode *p = root; 
            for(int j=0;j<w_len;++j){
                if(p->child[words[i][j]-'a']==NULL)   
                    p->child[words[i][j]-'a'] = new TrieTreeNode();
                p = p->child[words[i][j]-'a'];
            }
            p->isEnd=true;
        }
    }
    
    void dfs(vector<vector<char> > &board, int x, int y, TrieTreeNode *p,string s){
        if(p->isEnd) mp[s]=1;
        int cx,cy;
        for(int i=0;i<4;++i){
            cx=x+dir[i][0],cy=y+dir[i][1];
            if(cx>=0&&cx<m&&cy>=0&&cy<n&&!vis[cx][cy]&&(p->child[board[cx][cy]-'a'])){
                vis[cx][cy] = true;
                string tmp=s;
                tmp.push_back(board[cx][cy]);
                dfs(board,cx,cy,p->child[board[cx][cy]-'a'],tmp);
                vis[cx][cy] = false;
            }
        }
    }
    
    vector<string> wordSearchII(vector<vector<char> > &board, vector<string> &words) {
        // write your code here
        insertTrie(words);
        
        vector<string> res;
        m = board.size();
        if(m==0) return res;
        n = board[0].size();
        
        for(int i=0;i<m;++i)
            vis.push_back(vector<bool>(n,false));
        
        mp.clear();
        for(int i=0;i<m;++i)
            for(int j=0;j<n;++j)
                if(root->child[board[i][j]-'a']){
                    vis[i][j]=true;
                    string s;
                    s.push_back(board[i][j]);
                    dfs(board,i,j,root->child[board[i][j]-'a'],s);
                    vis[i][j]=false;
                }
                
        for(map<string,int>::iterator it=mp.begin();it!=mp.end();++it)
            res.push_back(it->first);
        return res;
    }
};


97.二叉树的最大深度

简单DFS。

if(!root) return 0;

return max(maxDepth(root->left),maxDepth(root->right))+1;


175.翻转二叉树

有两种翻转方法。

一种先翻转好下层的,再翻转当前的。(自底向上

一种是先翻转当前的,再翻转下层的。(自顶向下

class Solution {
public:
    /*
     * @param root: a TreeNode, the root of the binary tree
     * @return: nothing
     */
    
    //从上往下的做法
    void invertBinaryTree(TreeNode * root) {
        // write your code here
        if(!root) return;
        swap(root->left,root->right);
        invertBinaryTree(root->left);
        invertBinaryTree(root->right);
    }
    /*从下往上的做法
    TreeNode *dfs(TreeNode *p){
        if(!p) return NULL;
        TreeNode *l = dfs(p->left);
        TreeNode *r = dfs(p->right);
        p->left=r;
        p->right=l;
        return p;
    }
    
    void invertBinaryTree(TreeNode * root) {
        // write your code here
        root =  dfs(root);
    }*/
};


469.等价二叉树

简单DFS。


95.验证二叉查找树

中序遍历有序即可。不需要额外的空间。更新最小值(最开始设置为比int最小值还小的值)


88.最近公共祖先

方法是

1.找到两个结点从根来的路径。

2.从路径的尾部找到第一个公共点即可。


AC代码:

class Solution {
public:
    /*
     * @param root: The root of the binary search tree.
     * @param A: A TreeNode in a Binary.
     * @param B: A TreeNode in a Binary.
     * @return: Return the least common ancestor(LCA) of the two nodes.
     */
    
    vector<TreeNode *> FA,FB,tmp; //记录路径
    bool flag;
    void dfs(TreeNode *p, TreeNode *A, vector<TreeNode *> PA, vector<TreeNode *> &FA){
        if(flag) return; //如果已经找到路径,直接返回
        if(p==A){
            PA.push_back(p);
            FA=PA;
            flag=true;
            return;
        }
        if(p==NULL) return;
        PA.push_back(p);
        dfs(p->left, A, PA, FA);
        dfs(p->right, A, PA, FA);
    }
    
    TreeNode * lowestCommonAncestor(TreeNode * root, TreeNode * A, TreeNode * B) {
        // write your code here
        flag=false,tmp.clear();
        dfs(root,A,tmp,FA);
        flag=false,tmp.clear();
        dfs(root,B,tmp,FB);
        
        int lenA=FA.size(),lenB=FB.size();
        lenA = min(lenA,lenB);
        for(int i=lenA-1;i>=0;--i)
            if(FA[i]==FB[i])
                return FA[i];
    }
};

104.合并k个排序链表

我的做法是将队列每个头部元素放入优先队列中,出队一个,则入队一个相应的后继结点。

需要注意的是,我们需要自定义优先队列的判断条件。

第二种解法是进行归并排序!


AC代码:

struct Info{
    ListNode *p;
    Info(ListNode *p){
        this->p = p;
    }
    bool operator <(const Info a) const{
        return a.p->val<p->val;
    }
};

class Solution {
public:
    /**
     * @param lists: a list of ListNode
     * @return: The head of one sorted list.
     */
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        // write your code here
        priority_queue<Info> q;
        int len = lists.size();
        for(int i=0;i<len;++i)
            if(lists[i]) q.push(Info(lists[i]));
        ListNode *head = new ListNode(0); //带头结点
        ListNode *p = head;
        while(!q.empty()){
            Info t = q.top();
            q.pop();
            if(t.p->next) q.push(Info(t.p->next));
            t.p->next = NULL;
            p->next=t.p;
            p=p->next;
        }
        return head->next;
    }
};


518.超级丑数

第一种做法是使用优先队列(最小堆)。

class Solution {
public:
    /*
     * @param n: a positive integer
     * @param primes: the given prime list
     * @return: the nth super ugly number
     */
    int nthSuperUglyNumber(int n, vector<int> &primes) {
        // write your code here
        priority_queue<long long,vector<long long>,greater<long long>> q;
        q.push(1);
        
        int len = primes.size();
        map<long long,int> mp;  //表示是否访问过
        mp[1]=1;
        while(--n){
            long long cur = q.top();
            q.pop();
            for(int i=0;i<len;++i)
                if(!mp[cur*primes[i]]){
                    mp[cur*primes[i]] = 1;
                    q.push(cur*primes[i]);
                }
        }
        return q.top();
    }
};

第二种方法有点类似动态规划的思想。

1.我们用ugly数组来记录超级丑数。

ugly[0]=1

ugly[i]是第i+1小的超级丑数。

结果只用返回ugly[n-1]即可。

2.ugly[i]肯定是ugly[j]*某个prime(j<i)中最小的一个。

3.我们用index数组记录每个prime对应的ugly下标,如果产生的最小,则下标增加。指针游走。

我们只需要知道每个prime对应的ugly下标,如果是这个产生的,下标+1。

lintcode Trie树、二叉树、堆等

代码如下:

class Solution {
public:
    /*
     * @param n: a positive integer
     * @param primes: the given prime list
     * @return: the nth super ugly number
     */
    int nthSuperUglyNumber(int n, vector<int> &primes) {
        // write your code here
        int m = primes.size();
        vector<int> ugly(n,-1);
        vector<int> index(m,0);
        ugly[0]=1;
        
        for(int i=1;i<n;++i){
            ugly[i]=ugly[index[0]]*primes[0];
            for(int j=1;j<m;++j)
                ugly[i]=min(ugly[i], ugly[index[j]]*primes[j]);
            for(int j=0;j<m;++j)
                if(ugly[i]==ugly[index[j]]*primes[j])
                    index[j]++;
        }
        return ugly[n-1];
    }
};