2013年8月15日星期四

Implement strStr()

description:

Implement strStr().
Returns a pointer to the first occurrence of needle in haystack, or null if needle is not part of haystack.

solve:

class Solution {
public:
    char *strStr(char *haystack, char *needle) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        unsigned int len_h = strlen(haystack);
        unsigned int len_n = strlen(needle);
        if(len_n == 0) {
            return haystack;
        }
        for(unsigned int i = 0; i < len_h; ++ i) {
            unsigned int t = i, j = 0;
            while(t < len_h && j < len_n) {
                if(i + len_n > len_h) break;
                //if(len_n)
                if(*(haystack + t) != *(needle + j) ||
                *(haystack + len_n + 2 * i - t - 1) != *(needle + len_n - j - 1)) {
                    break;
                }
                ++ t;
                ++ j;
            }
            if(j == len_n) {
                return haystack + i;
            }
        }
        return NULL;
    }

};

坑:
裸暴力不让过,加个小优化(头部和尾部同时对比)。为啥不用kmp?,因为忘光了。

Word Search

decription:

Given a 2D board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.
For example,
Given board =
[
  ["ABCE"],
  ["SFCS"],
  ["ADEE"]
]
word = "ABCCED", -> returns true,
word = "SEE", -> returns true,
word = "ABCB", -> returns false.

slove:
class Solution {
public:
    bool exist(vector<vector<char> > &board, string word) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        bool got_it = false;
        vector<char> tp;
        int h = board.size();
        int w = board.at(0).size();
        bool* visit = new bool[h * w];
        for(int k = 0; k < w * h; ++ k) visit[k] = false;
        for(int i = 0; i < h; ++ i) {
            tp = board.at(i);
            for(int j = 0; j < tp.size(); ++ j) {
                if(tp.at(j) == word[0]) {
                    got_it = run(visit, board, i, j, word, 0);
                    if(got_it) {
                        return got_it;
                    } else {
                        for(int k = 0; k < w * h; ++ k) visit[k] = false;
                    }
                }
            }
        }
        return got_it;
    }
    bool run(bool visit[], const vector<vector<char> >& board, int i, int j, string word, int idx) {
        if(i >= board.size() || i < 0) {
            return false;
        }
        if(j >= board.at(i).size() || j < 0) {
            return false;
        }
        if(idx >= word.length()) {
            return false;
        }
        if(visit[i * board.at(0).size() + j] == true) {
            return false;
        }
        if(board.at(i).at(j) == word.at(idx)) {
            visit[i * board.at(0).size() + j] = true;
            if(idx == word.length() - 1) {
                return true;
            }
            return run(visit, board, i + 1, j, word, idx + 1) ||
                run(visit, board, i - 1, j, word, idx + 1) ||
                run(visit, board, i, j - 1, word, idx + 1) ||
                run(visit, board, i, j + 1, word, idx + 1);
        }
        return false;
    }

};

坑:
递归时保存访问信息的数组更新有问题

Subsets

description:
Given a set of distinct integers, S, return all possible subsets.
Note:
  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.
For example,
If S = [1,2,3], a solution is:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

slove:

通过位标记来记录某个值是否参与到此次组合。

class Solution {
public:
    vector<vector<int> > subsets(vector<int> &S) {
        vector<vector<int> > ret;
        std::sort(S.begin(), S.end());
        run(ret, S);
        return ret;
    }
    void run(vector<vector<int> >&ret, vector<int> str) {
        vector<int> res;
        ret.push_back(res);
        int len = str.size();
        for(int i = 1; i < (1<<len); ++ i) {
            for(int j = 0; j < len; ++ j) {
                if(i & (1 << j)) {
                    res.push_back(str.at(j));
                }
            }
            ret.push_back(res);
            res.clear();
        }
    }

};


坑:
输入向量不一定是有序的。

Path Sum II

description:
Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum.
For example:
Given the below binary tree and sum = 22,
              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1
return
[
   [5,4,11,2],
   [5,8,4,5]
]

solve:

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int> > pathSum(TreeNode *root, int sum) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        vector<vector<int> > res;
        vector<int> fk;
        //string tmp;
        //ostringstream ret;
        run(root, res, fk, 0, sum);

        return res;
    }
    void run(TreeNode* root, vector<vector<int> >& res, vector<int> ret, int total, int sum) {
        if(root == NULL) {
            return;
        }
        total += root->val;
        ret.push_back(root->val);
        if(root->right == NULL && root->left == NULL) {
            if(sum == total) {
                if(ret.size() > 0) {
                    res.push_back(ret);
                }
            }
            return;
        }
        //ret << root->val << ",";
        run(root->right, res, ret, total, sum);
        run(root->left, res, ret, total, sum);
    }
};

Sum Root to Leaf Numbers

[leetcode不能查看ac代码,开贴暂存]
description:
Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.
An example is the root-to-leaf path 1->2->3 which represents the number 123.
Find the total sum of all root-to-leaf numbers.
For example,
    1
   / \
  2   3
The root-to-leaf path 1->2 represents the number 12.
The root-to-leaf path 1->3 represents the number 13.
Return the sum = 12 + 13 = 25.
solve:
/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int sumNumbers(TreeNode *root) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int res = 0;
        run(root, 0, res);
        return res;
     
    }
    void run(TreeNode* root, int out, int& res) {
        if(root == NULL) {
            return;
        }
        out = out * 10 + root->val;
     
        if(root->right == NULL && root->left == NULL) {
            res += out;
        } else {
            run(root->right, out, res);
            run(root->left, out, res);
        }
    }   return v;
};

2013年5月10日星期五

一次多进程冲突引发僵尸【defunct】进程的排错经历

背景

公司某个服务迁移到另一台服务器上,迁移过程中部分数据要进行恢复。属于日志类数据,按天分割。于是乎改造了一下入库脚本为多进程模式,按照每天起一个进程的节奏来恢复。

表象

一般脚本起来后我都习惯性的ps一下看看脚本是否正常运行。当在启动多进程恢复脚本之后,随手一ps,结果发现进程列表出现一坨Z+的defunct进程,也就是僵尸进程,当时我整个人就思密达了。我们知道僵尸进程出现的原因是在父进程没对自己fork出来的子进程进行善后处理的情况下,子进程先于父进程一步挂了(白发人送黑发人,此时子进程就成了僵尸进程,除非父进程挂掉,此时init进程接管这些僵尸进程,替父进程进行善后的清理)。而对于我的脚本,子进程绝逼不会比父进程结束的早,因为父进程只是按照日期个数挨个起一个进程然后就退出,而子进程要进行日志分析处理等一系列复杂操作,并且每天日志文件数量在6k左右。代码如下:
if __name__ == "__main__":
      dates = []
      InitDate(dates)
      for i in dates:
           p = multiprocessing.Process(target=test.run, args=(i, ))
           p.start()
      # comment it to detect zombine process
      #signal.signal(signal.SIGCHLD, signal.SIG_IGN)

其中dates是一个日期的列表,用膝盖想也是父进程应该先退出。看了一下脚本运行log,发现提前结束的子进程获取到的文件路径信息很诡异,不是完整路径信息。既然和预期不符,那就是代码的问题了。

解决过程

首先我把主要的逻辑代码单独抠出来进行测试,成功复现这一问题,于是怀疑是多进程造成并发冲突,因为诡异的结果往往是内存冲突或者进程冲突造成的。我在可能冲突的地方放一个sleep函数,每个进程随机sleep个几秒。嗯,结果正常了,进一步排查,发现是test文件中有一个全局的变量,脑残了。到这就豁然开朗了,应该是全局变量出现并发访问,导致A进程的结果被B进程给拿到,B进程的结果不知道被谁拿到等等。

结果

把全局变量改为局部变量,然后作为参数传递给用到的函数,这样保证了此变量只存在每个进程的堆栈空间中,防止出现并发冲突。然后实验一下,嗯,妥妥好使。

2013年3月2日星期六

那些天,我给hadoop跪过的坑

大背景:

使用hadoop自带的datajoing软件包进行多个文件的数据连接(约等于实现一个sql的select多表连接功能)

坑1-编译期:

其实这个不是hadoop的坑,应该是shell或者javac的坑。
编辑完一个java文件后,当我用javac编译时,引用了多于一个的jar文件,起初我是这么写的:

javac -classpath hadoop/hadoop-*-core.jar:hadoop/contrib/datajoin/hadoop-*-datajoin.jar -d arvin ReduceDataJoin.java

提示:windows下多个jar包用分号;隔开,linux用冒号:

然后报错提示找不到一些基础的hadoop类,而从前只用一个jar包的时光一切都是ok的:
javac -classpath hadoop/hadoop-*-core.jar -d arvin ReduceDataJoin.java

于是加上-verbose选项查看编译过程,发现:

嗯,问题出在通配符*没有被展开,然后引起各种基础类找不到。修改成指定版本,问题解决。通配符未展开的原因未知,而如果只指定一个jar包,通配符可以顺利展开的,推测是:让shell变节了。后来拉light求证了一下,确实是shell展开通配符的规则所限,当shell碰到正则表达式需要展开匹配时,会首先对非通配符的字符进行匹配,匹配之后才会展开通配符进一步进行匹配,否则会放弃匹配(简而言之就是a*会匹配所有a打头的文件,而b*一开始就会忽略a打头的文件,因为扫描到a打头的文件名时,跟b匹配不上,所以后面的*不再进一步匹配),上面的:和其后的部分在shell里被当成了和前面字符串连在一起的整体的一个文件名,shell自然找不到目标文件,于是放弃展开直接传递给javac了,javac收到的就是一个包含通配符的文件名参数了,这个文件自然不存在。(shell通配符展开的问题已经被王垠在文章《unix设计缺陷》中深度吐槽过)

坑2-jar包生成期:

没经验的同学第一次引用第三方jar包基本都会碰到这个问题,NoClassDefFound Exception,奇葩的是别人基本都是忘记把datajoin包分发到datanode上了,而我没有忘记做分发,但是编译时却临时起意把生成的字节码文件放到了一个单独的子目录arvin中,然后生成jar包时却又把jar文件生成在了当前目录。结果运行时提示找不到入口类,unzip jar包后发现,主类名前多了个arvin的包,改成在当前目录生成解决。此属自作虐不可活,自己刨坑埋自己的半吊子行为。

附分发jar包的官方用法
hadoop jar ReduceDataJoin.jar ReduceDataJoin -libjars hadoop/contrib/datajoin/hadoop-0.20.2-datajoin.jar inpath outpath


坑3-运行期:

运行中报异常:NoSuchMethodException
答案在这里。这真真的必须是个坑,不过说起来也不是hadoop的坑,而是《hadoop in action》没说明白,给的代码中没说要默认无参构造函数。人家自带的例子中是给出了用法示例的有木有,例子可在这里看到:
hadoop-0.20.2/src/contrib/data_join/src/examples/org/apache/hadoop/contrib/utils/join/SampleTaggedMapOutput.java

搜索上述坑的解决方案时,还碰到了一个比较大众的,不过我没踩到,应该是新版本做了改进。

最后,这些坑或多或少都有自己出力刨土的地方,自己给自己呵呵一个。

2013年1月9日星期三

hadoop求文件交集

一,理论

   neil同学昨天抛给我一个小问题:利用hadoop从2个文件中提取出相同的条目。文件格式如下:
input1.txt
aaaa
bbbb
cc
11

input2.txt
aaa
bbbb
ccc
22

2013年1月6日星期日

hadoop蒙特卡洛算法续集

这个版本是参考自带samples实现。相比上个野生版本,增加修改如下特性:
old/new
1:生成点数由输入文本文件决定/生成点数可由命令行参数指定,据此生成相应二进制文件

2:文件中存在冗余列/二进制输入输出文件中不再有冗余列,程序中通过NullWritable来对原本冗余列的位置占位


2013年1月5日星期六

hadoop处女秀之蒙特卡洛算法

序,

   《Hadoop In Action》里开始就推荐从观摩hadoop自带的example起步,于是走马观花的看一遍sample,里面竟然有个Dancing Link的分布式版本。当看到有一个MonteCarlo求Pi的源文件时,觉得有必要去复习一下MonteCarlo的原理。于是跑去看了一下这个随机算法的思路,看完觉着学习还是自己动手来的好,于是先不去看sample自己试着实现一下,因为这个算法很简单。不过,由于不熟悉Hadoop编程模型和IO套路甚至数据类型,又是自己操刀的第一个hadoop程序,就这样摸黑上路了,中间走了很多弯路,摘记如下。