# 大染志
分享编程、运维、生活与思考

Junv's blog

## linux安装配置postgresql

发表于 2012-11-09 更新于 2026-08-04

这周在公司中的一个任务是，安装postgresql 数据库，并要成功连接rails 程序，执行rake 命令。走了很多弯路吧。主要有：

- 在postgresql 中创建一个与database.yml中用户名一致的用户。
- 创建一致的用户，并赋予superuser身份。
- 在postgres中创建一个用户与当前linux用户名一致。不设置/设置密码
- 修改/etc/postgresql/9.1/main/pg_hba.conf,修改里面的验证方法。

主要出现的错误有：no password supplied.等错误。

最后，在各种操作后终于成功执行了 rake 命令，昨天晚上我自己在自己的电脑上又试了一下，现在将我们应该做的正确操作描述如下。

首先是在linux/ubuntu上安装postgres.

    1
    2
    3

    sudo apt-get install postgresql-9.1

    sudo apt-get install postgresql-server-dev-9.1

postgresql 数据库在安装的时候会默认创建postgres用户，它的身份可以简单理解成为mysql 中的root用户。并且我们现在可以这样登录postgresql

    1

    sudo -u postgres psql (注意这里是小写的u，和后面的有区别)

接下俩我们需要在postgresql 中创建一个与当前你使用的linux用户名一致，并赋予superuser.（当然这个操作需要在连接到数据库的基础上）

    1
    2

    # Create SuperUser
    create user junv with superuser (当前我的linux用户名为junv)

完成数据库操作后，使用 \q退出数据库命令行

然后，我们需要修改/etc/postgresql/9.1/main/pg_hba.conf（这里的9.1，是因为我安装的数据库版本是9.1）

    1

    sudo gedit  /etc/postgresql/9.1/main/pg_hba.conf

将里面相似的内容修改为类似如下所示：

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    \# Database administrative login by Unix domain socket
    local all postgres trust

    \# TYPE DATABASE USER ADDRESS METHOD
    \# “local” is for Unix domain socket connections only
    local all all trust
    \# IPv4 local connections:
    host all all 127.0.0.1/32 trust
    \# IPv6 local connections:
    host all all ::1/128 trust

最后这个method,改成trust,表示信任所有来地本地(localhost)的连接。这样我们就可以连上数据库了

和mysql 我们修改了配置文件之后还需要做的是 重启 数据库，让配置生效。

    1

    sudo service postgresql restart

到现在为止。我们对数据库进行的操作就差不多了。现在你应该已经可以成功脸上数据库了。

但是经过我的实验，我发现，我可以把database.yml中设置的用户名改成任意值，即使是一个在postgresql中根本不存在的用户名，也依然可以成功。所以我猜测，postgresql可能是用的我当前linux 系统的这个用户名吧。具体有待验证。

由于我们配置文件中，配置为对本地的所有连接都是信任的，这应该也是原因之一吧。

这一周在公司的生活很充实，由于使用ubuntu,代理，结对编程的缘故，基本上都没什么时间关注QQ，和浏览新闻了。。。。。。。。

开始适应在thoughtworks的生活。

## Node.js操作mongodb（2）——gridfs操作文件

发表于 2012-10-24 更新于 2026-08-04

昨天文章主要分享了node.js对mongodb数据的增删查改，由于mongodb本身操作脚本也是使用javascript写的，所有其实很多node.js中的很多操作方法和其脚本是差不多的，应该说相对比较简单入手吧，今天我要分享的是node.js使用mongodb中的gridfs操作文件的一些方法。

今天我们会主要用到两个模块：mongodb和node.js本身提供的fs模块，fs模块主要是node.js对文件处理的一些方法。首先当然是使用require。

    1
    2

    var mongo = require('mongodb');
    var fs = require('fs');

在mongodb提供的node.js API中主要有两种用于操作gridfs文件模块的对象：

    1
    2

    var gridFs = new mongo.Grid(db, 'fs');
    var gridStore = new mongo.GridStore(db, new mongo.ObjectID(), 'w', {root: 'fs'});

简单说一下两种方式的区别：
Grid()在读写文件的时候需要将文件的所有内容读取到内存中，因此在存储大文件的时候可能会导致内存溢出。但API相对简单主要有get(),put(),和delete()方法。
GridStore()应该说是mongodb中GridFs的标准实现吧。可以防止内存溢出（后面说明）。API相对更加复杂，当然功能也更加强大。
那我们首先使用Grid进行文件的读写操作，当然在操作之前我们需要新建Server、DB对象，并打开db：

    1
    2
    3
    4
    5
    6
    7
    8
    9

    var server = new mongo.Server('localhost', 27017, {
    	auto_reconnect: true
    });
    var db = new mongo.Db('mydb', server);

    db.open(function(err, db) {
    	if(err) throw err;
            //do some actions.
    });

这部分由于前面一篇文章已经说过这里便不再赘述。下面直接操作文件。新建一个Grid对象。

    1
    2

    //第一个参数为被打开的db，第二个参数为我们希望使用的存储文件的collection名称
    var gridFs = new mongo.Grid(db, 'fs');

Grid在使用put方法向mongodb数据库写入文件的时候，传入的参数为node.js中的Buffer（）（这也正是可能会导致内存溢出的原因），因此我们需要使用fs.readFileSync()读取文件存入buffer.注意这里读取文件必须使用同步操作，否则将不能读取到文件内容。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    var buffer = fs.readFileSync('c://a.iso');
    	//该种方式存文件需要读取所有文件信息，如果文件过大可能会导致内存溢出
    	gridFs.put(buffer, {}, function(err, fileInfo) {
    		if(!err) {
    			console.log('write file success!');
    		}
    		//通过ID获取文件
    		gridFs.get(fileInfo._id, function(err, data) {
    			if(err) throw err;
    			//使用nodejs 原生fs api写入文件到硬盘
    			fs.writeFile('c:\\my.iso', data, 'utf-8', function(err) {
    				if(!err) {
    					console.log('write file to local file system succeed!');
    				}
    			});
    		})
    	});

put()方法的第一个参数为buffer，第二个参数为文件的一些属性，将保存到mongodb中，这里我们不写，系统将自动填写相关信息，第三个是回调函数，我们可以从回调函数中获取到我们刚才保存的文件的相关信息。
get()方法，即是通过mongodb中文件的_id参数去获取文件。并在回调函数中返回文件内容。
最后我们再使用fs.writeFile()，来讲获取到的数据写入外部文件即可。其中的第三个参数，通常我们可以省略。系统模式会设置为utf-8。

下面使用GridStore（）的方式来存储文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

    var gridStore = new mongo.GridStore(db, new mongo.ObjectID(), 'w', {root: 'fs'});
    ```

    解释一下这几个参数，第一个为你的DB对象，第二个需要传入一个mongodb的ObjectId对象，因此我们需要自动生成一个，第三个是模式，她有三个选择：

    > 1.  r    读模式，文件只能被读取，不能被修改
    > 2.  w  写模式，可以读写，已存在的文件会被覆盖。
    > 3.  w+ 编辑模式，这个具体我不太清楚，应该是比w更高，也就是说不仅可以去写吧。

    这里由于我们是向数据库写入文件，当然至少得w模式。

    最后一个参数你要选择用于存储文件的collection，系统默认是fs,因此这个我们也可以不写。

    ``` ruby
    gridStore.writeFile('c://a.iso', function(err, fileInfo) {
    		if(err) throw err;
    		console.log(fileInfo);
    		//新建一个gridstore用于读取文件
    		var readGrid = new mongo.GridStore(db, fileInfo._id, 'r');
    		readGrid.open(function(err, gridStore) {
    			//读取数据
    			readGrid.read(function(err, data) {
    				//写入到本地文件系统
    				fs.writeFile('c:\\myaaa.iso', data, function(err) {
    					if(!err) {
    						console.log('write file to local file system succeed!');
    					}
    				});
    				db.close();
    			});
    		});
    	});

这个wrtieFile（)是把外部文件存入到mongodb的数据库 中，和fs.writeFile()是相反的，这个需要注意。其本身使用是比较简单的。
当然后面我们需要从mongodb数据库中读出文件，于是新建了一个模式为“r”的GridStore。
open函数，mongodb官方的解释是：
Opens the file from the database and initialize this object. Also creates a new one if file does not exist.
这里我们当然是从数据库中打开这个文件，并初始化这个对象。
然后在使用read读取数据，并使用fs.writeFile（)写入外部函数。

完整代码下载：

https://github.com/wahyd4/node.js-mongodb-demo

参考资料：

http://nodejs.org/api/fs.html

http://mongodb.github.com/node-mongodb-native/api-generated/gridstore.html

http://mongodb.github.com/node-mongodb-native/api-articles/nodekoarticle2.html

## Node.js操作Mongodb(1)

发表于 2012-10-23 更新于 2026-08-04

今天想要分享是使用node.js来连接并操作mongodb数据库实现基本的增删查改操作。

首先我们需要在node.js下安装mongodb争对node的驱动。

npm install mongodb

下面就可以开始写代码了。首先当然是在node代码引用mongodb:

    1

    var mongodb = require("mongodb");

其次就是连接到mongodb数据库server:

    1

    var server = new mongodb.Server('localhost',27017,{auto_reconnect:true});

其中第一、二分参数分别是IP地址和端口，由于我们这里没有设计到验证，因此便不需要验证参数。最后的参数是一个对象，我们设置了一个属性auto_reconnect为true，就表示如果当程序与mongodb失去连接便回自动重连。

    1

    var server = new mongodb.Server('localhost',27017,{auto_reconnect:true},10);

通常情况下我们可以不管最后一个参数，这个参数是线程池的数量，系统默认的设置是5，也就是说可以同时支持打开5个数据库连接，并进行操作。当然我们可以根据自己需要设置希望的值。

然后就是获取或者创建一个DB实例。

    1

    var db = new mongodb.Db("mydb2",server);

第一个参数需要传入被数据库的名称，如果数据库不存在系统会创建该数据库，当时不是现在，而是当我们试图打开数据库的时候。

    1
    2
    3
    4
    5
    6
    7
    8

    db.open(function(err,db){
    if(err){
    console.log(err);
    }
    if(!err){
    console.log("we are connected!");
    }
    });

我们想要对数据库进行的相关操作都是在db.open()的回调函数内完成的。即是说我们对数据库的操作需要在数据库被打开的情况下完成。
在mongodb中collection可以看做是我们平时所用的关系型数据库中的table(表)，我们可以通过显式或者隐式的方式来创建collection：
显式即是使用db.createCollection()创建。

    1
    2
    3
    4
    5
    6
    7

    db.createCollection('test',{safe:true},function(err,collection){
    if(err){
    console.log(err);
    }else{
    console.log("created successful!");
    }
     });

safe:true如果我们使用了这个参数即表示，如果当我们想要创建的collection已经存在时，则会报错，如果去掉则不会。
隐式则是我们在使用某个collection的时候，系统发现没有该collection，则会自动创建。

    1
    2

    db.collection('fuck',function(err,collection){
    });

这个时候如果我们使用safe:true，当程序发现我们希望使用的collection不存在则会报错。通过这段代码，我们可以在该回调函数中执行所有我们对该collection的操作。

插入数据：

    1
    2
    3
    4
    5
    6
    7

    //插入单条数据
    collection.insert({'name':'tom'},function(err,result){});
    //插入数组
    var array = [{'name':'mary'},{'name':'lily','age':20}];
    collection.insert(array,{safe:true},function(err,result){
         if(err) throw err;
    });

当我们一次插入或者在以后的删除、更新多条数据时，我们都应该使用safe:true，以保证数据的完整性和一致性，如果在插入数组的过程中某些数据未能成功插入，即会报错。
更新数据：

    1
    2
    3
    4
    5
    6

    collection.update({'name':'tom'},{$set:{'name':'lily','age':100}},function(err,result){
    if(err) throw err;
    if(!err){
    console.log("update successful");
    }
     });

第一个参数为我们定位目标的属性，这里即是我们更新所有名字为tom的数据，$set的意思则是我们将其更换其他的数据。
删除数据：

    1
    2
    3
    4
    5
    6
    7
    8
    9

    //删除用户名为tom的数据
    collection.remove({'name':'tom'},{safe:true},function(err,result){
    if(err) console.log(err);

    });

    //删除集合内的所有数据

    collection.remove();

查找数据：

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

    //查找一条符合的数据
    collection.findOne({'name':'tom'},function(err,result){
    if (err) throw err;
    console.log(result);
     });

    //查找多条数据，将结果集转换成数组。

    collection.find().toArray(function(err,items){
    if(err) throw err;

    //遍历数据

    for(item in array){
     console.log(array[item]);
     }
     });

    //另外一种查找多条数据的方式是使用流式来处理。

    var stream = collection.find().streamRecords();

    //监听获取数据
     stream.on("data",function(item){
     console.log(item);
     });

    //当数据读取完毕时，执行的方法。
     stream.on("end",function(){
     console.log("stream is end");
     });

当然我们这里使用的查找数据的方式很简单，没有涉及到高级的查询语句，比如限制返回数据的条数limit()，这些都是可以使用的。大家可以从下面的文档中获取到更多的信息。

完整代码下载：

https://github.com/wahyd4/node.js-mongodb-demo
参考文档：
http://mongodb.github.com/node-mongodb-native/api-articles/nodekoarticle1.html
http://www.mongodb.org/display/DOCS/Advanced+Queries

请阅读下一篇：

Node.js操作mongodb（2）——gridfs操作文件

## 最近一个多月找工作有感！

发表于 2012-10-15 更新于 2026-08-04

今年毕业生校招行情真不怎么好。

最遗憾的是腾讯成都没有给我笔试的机会，为了霸面等了一天，结果最终也没有给我机会霸面，前天去了重庆笔试腾讯，到了考官才说腾讯在重庆不招web前端。但是它却又给我机会了。真搞不懂这是怎么样一个公司。

百度虽然给了我笔试机会，可是遗憾的是我未能进入面试阶段。

其实更多的是，很多公司只是来打酱油，不怎么招人。比如小米、创新工场 这样的货色。

当然还有一部分公司歧视我们这些不是211、985学校的学生，当然这其中大部分是国企，不过现在连华为这样的加班公司也开始歧视人了。当时听闻华为只招重点大学的学生，我为了获得笔试机会把学校改成了川大，并顺利进入了面试（其实，华为的笔试只要做对一道大题就可以了。），顺利经过了技术面、群面。下午在门口等了2,3个小时才叫到我进去综合面试，所谓综合面试，就是一个BOSS面试三个学生，谁知道命运就是这么捉弄人，和我一起面试的两个学生，我坐在中间，他们趁面试官离开的时候，偷偷摸摸去翻看他们自己的面试成绩，最终被面试官发现了。面试官发现了，震怒。并以为我也翻看了。我告诉他我没有翻，也不知道他是否相信。不过他接下来的4,5个问题，除了让我们介绍一下自己其余的问题都是围绕着为什么要翻看简历。最终本该1个多小时的面试，只进行了15分钟。草草结束，和面试官握手的时候，我手是抖的。当机会在你面前，你却不能左右的时候，是多么的苍白。上周华为开始发offer了，我想不管是什么样的原因，总之我没有拿到华为的offer，据说华为的工资只有6K本科生。在成都这个工资也不能算很高吧。

今年的行情就这个样子，我笔试了接近10次，最终得到的面试机会不超过5次。真心伤不起。跑了好多地方，川大，电子科大，最远的是电子科大，去了就意味着晚上不能回学校。好伤！

现在我真的有点累了。几乎所有公司都跟风考算法，可是这些公司根本不像会用那么多算法的公司。为了彰显他们很了不起吗？

坑爹！

如果我没有经济上的担忧，我恐怕已经去创业了！！

尼玛，伤不起的是还要被新电信、symbio这样的外包公司鄙视，面试后不发offer。难道是我工资要高了吗？？？

伤不起。

再坚持一段时间吧，希望thoughtworks能够给力啊！！

现在还是很想留到成都啊！！

## 给你的Rails 项目配Travis 持续集成工具

发表于 2012-10-06 更新于 2026-08-04

可能很多人都在github的某些开源项目上看到过类似这样的图标吧！

它是Travis-CI,一个免费向开源项目提供持续集成服务的网站，它可以在你每次向仓库提交更新之后，编译你的代码，实时告诉你的项目现在的编译状态。

下面分享一下如何给一个开源的rails 项目配这样一个持续集成服务。首先你需要去 https://travis-ci.org 通过你的github帐号登录，导入你需要编译的项目。

其实这个时候，Travis-CI已经开始工作了。已经可以编译你代码了。

但是你的Rails里面有测试，并且需要使用像 连接数据库的操作，我们就需要Travis-CI在执行编译之前，进行创建数据库的操作。或者指定我们的编译环境。Travis-CI是通过.travis.yml识别的。

因此我们需要在Rails项目的跟目录创建一个.travis.yml 的文件。文件的内容格式大致如下

    1
    2
    3
    4
    5
    6
    7
    8

    rvm:
      - 1.9.3
    env:
      - DB=postgresql
    script:
      - RAILS_ENV=test bundle exec rake ci --trace db:migrate
    before_script:
      - psql -c 'create database ocelots_test' -U postgres

大致解释一下，这里我们指定了我们使用的编译环境是 ruby 1.9.3,指定了我们使用的数据库，因为我希望travis 可以帮我创建测试数据库。
script 标签里指定了我们运行测试的命令。
而before_script 将在我们测试之前，运行，在这里即是帮我创建测试数据库。
类似的Travis还有after_script，用于指定在测试之后所做的动作。
更多的使用说明可以通过 http://about.travis-ci.org/docs/user/build-configuration/ 了解。
最后我们需要做的当然是把这个.travis.yml文件 push 到github 中，这样travis就可以帮助你编译了。

## 华为一个字符串处理的上机题

发表于 2012-09-16 更新于 2026-08-04

昨天下午参加了华为的上机笔试，3道题，但是答得不是很理想，回来有好好思考了一些，现在这里把相对较难的那道题分享一下。题目是这样的：

问题描述：

> 在给定字符串中找出单词（ “单词”由大写字母和小写字母字符构成，其他非字母字符视为单词的间隔，如空格、问号、数字等等；另外单个字母不算单词）；找到单词后，按照长度进行降序排序，（排序时如果长度相同，则按出现的顺序进行排列），然后输出到一个新的字符串中；如果某个单词重复出现多次，则只输出一次；如果整个输入的字符串中没有找到单词，请输出空串。输出的单词之间使用一个“空格”隔开，最后一个单词后不加空格。

> 要求实现函数：

> void my_word(charinput[], char output[])

> 【输入】 char input[], 输入的字符串

> 【输出】 char output[]，输出的字符串

> 【返回】 无

> 示例

> 输入：charinput[]=”some local buses, some1234123drivers” ，

> 输出：charoutput[]=”drivers local buses some”

> 输入：charinput[]=”%A^123 t 3453i*()” ，

> 输出：charoutput[]=””

乍一看，题目的确不怎么难，但是其中隐藏着很多细节。我的思考是这样的：

1. 首先分离其中的字符串（单词），需要注意的是单个字母不能成为单词，当到达字符串末尾时，即使没有符号，也需要分离单词。
2. 其次是排除其中的重复单词（java里使用 Set实现，set中不能出现重复的内容）
3. 对单词进行排序，按照单词长度逆序，如果单词的长度相同，则按照单词出现的先后顺序排序。在项目中我使用的是选择排序，时间复杂度略小于o(n*n)
4. 输出数据，如果没有单词输出””

下面贴出我实现的代码，如发现有什么问题，请指出：

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64

    package com.toozhao.test;

    import java.util.HashSet;
    import java.util.Set;

    /**
     * {@link} http://toozhao.com
     * @author Junv
     *
     */
    public class MyWord {

        /**
         * @param args
         */
        public static void main(String[] args) {
            MyWord test = new MyWord();
            System.out
                    .println(test.process("some local buses, some1234123drivers"));
        }

        /**
         * 将输入信息通过处理，使其满足要求。并返回
         *
         * @param input
         * @return
         */
        public String process(String input) {
            char[] array = input.toCharArray();

            Set list = new HashSet();
            int mark = 0;// 用于标记当前截断位置

            boolean formerIsChar = false; // 设置上一个是否是字符
            for (int i = 0; i  122
                        || (array[i] > 90 && array[i]
         * 使用的是选择排序
         * @param original
         * @return
         */
        public String[] sort(String[] original) {

            for (int i = 0; i  original[i].length()) {
                        temp = j;
                    }

                    // 如果长度相同，则看谁出现得早
                    if (original[j].length() == original[i].length()) {
                        if (j > i) {
                            temp = j;
                        }
                    }
                }

                if (temp != i) {
                    String tempStr = original[i];
                    original[i] = original[temp];
                    original[temp] = tempStr;
                }
            }
            return original;
        }

    }

## java实现选择排序

发表于 2012-09-12 更新于 2026-08-04

选择排序大概的思想就是，每一次循环找出需要排序的部分中最小的那个数，找出只有再将最小的那个数移动到它应该放置的那个位置。因为虽然他的遍历次数也是1+2+3+…+n-1,不过它每次循环只交换一次，总的来说效率还是比直接插入排序好一些。更详细的解释参考百度百科和维基百科。

下面上代码吧：

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38

    package com.toozhao.sort;

    /**
     *
     * @author Junv
     *
     */
    public class SelectSort {
        // 定义需要排序的数
        private static int[] array = { 10, 50, 8, 29, 30, 17, 12, 40, 32, 7, 4, 22 };

        public static void main(String args[]) {
            sort(array);
            for (int flag : array) {
                System.out.print(flag + " ");
            }
        }

        public static void sort(int[] data) {
            // 外层循环一次，找到i to (data.length-1)这个数组中最小的一个数。
            for (int i = 0; i < (data.length - 1); i++) {
                // temp用来标注值最小的那个数。
                int temp = i;
                for (int j = i + 1; j < data.length; j++) {
                    // 将temp 始终标记为最小那个数。
                    if (data[j] < data[temp]) {
                        temp = j;
                    }
                }
                // 如果i != temp 说明，最小那个数为data[temp],则交换。
                if (i != temp) {
                    int t = data[i];
                    data[i] = data[temp];
                    data[temp] = t;
                }
            }
        }
    }

如需下载完整代码，以及其他排序代码，请到：

    1

## Java 实现直接插入排序

发表于 2012-09-11 更新于 2026-08-04

> 项目代码已经发布到github.欢迎各位下载：https://github.com/wahyd4/java-sorting

快到校招了，我还是把以前的数据结构的书拿出来复习了，不过这次我将用java 来实现数据结构中的一些算法。当然还是希望能够顺利通过这些公司的笔试。下面分享的是直接插入排序。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

    public class InsertionSort {

    // 定义需要排序的数组
    private static int[] array = { 1, 20, 6, 3, 19, 7, 14, 12, 10 };

    public static void main(String args[]) {

    for (int outer = 1; outer = 0 && temp < array[inner]; inner--) {

        /**
        * 将最大的赋值给目前比较的数组尾部 由于这里的数组下标需要
        *  跟随循环而变化，所以只能使用 j来表示
        */
        array[inner + 1] = array[inner];
        // 重新赋值第二大的数
        array[inner] = temp;
        // 重新赋值参考值，接着下标-1
        temp = array[inner];
      }
    }
       // 便利排序后的数组
       for (int flag : array) {
         System.out.println(flag);
       }
     }
    }

算法的时间复杂度：

直接排序最糟糕时需要比较的次数为（若数组长度为n）:1+2+…+n-1=n(n-1)/2,复杂度为O（n2），一般情况下，复杂度也是如此。

## Java实现Shell(希尔)排序

发表于 2012-09-11 更新于 2026-08-04

> 如需下载源代码，请到github：https://github.com/wahyd4/java-sorting

Shell(希尔)排序是建立在直接插入排序基础上之上的，只是它会先将这个数组按照一定的间隔分成若干个小数组先进行直接插入排序。以先比较较远的元素，再比较较近的元素，最后比较相邻的元素。可以逐步减小比较的次数。其时间复杂度优于直接插入排序。

更多的信息请查看百度百科或者维基百科的介绍。下面上希尔排序的代码：

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

    public class ShellSort {

        // 定义需要排序的数组
        private static int[] data = { 16, 20, 1, 6, 3, 19, 7, 14, 5, 60, 29, 40 };

        public static void shellSort(int[] data) {
            //获取数组长度
            int length = data.length;
            // 首先进行分组,每次组中元素为之前1/2。
            for (int gap = length / 2; gap > 0; gap = gap / 2) {
                // 对每个组进行插入比较
                for (int i = gap; i = gap && data[j] < data[j - gap]; j = j- gap) {
                        //缓存下表最大的那个数
                        int temp = data[j];
                        //将两数交换位置，将较大数保存到下标最大的位置。
                        data[j] = data[j - gap];
                        data[j - gap] = temp;
                    }

                }
            }
        }

        public static void main(String[] args) {
            shellSort(data);
            for (int flag : data) {
                System.out.print(flag + " ");
            }
        }

    }

虽然代码里面希尔排序是三层循环，但是只有中间层循环才是n数量级，外层循环为lbn,最里程循环也远小于n.平均时间复杂度还优于直接插入排序。

## Ubuntu安装 GTK主题 Elementary 3

发表于 2012-09-07 更新于 2026-08-04

不知道大家有没有这样的感受，Ubuntu桌面环境虽然已经很不错了，但是与Windows 和OS X 系统相比，我个人觉得还是有不小差距的。今天我要分享的就是在Ubuntu 12.04LTS下安装一款非常漂亮的GTK主题 Elementary 3。我觉得这是我用过的最漂亮的主题。

首先我们需要安装Gnome 3桌面环境，来替换掉Ubuntu 默认的Unity桌面环境。我们使用命令行来完成如下这些操作。

> sudo apt-get install gnome-shell

为了配置gnome3 更加方便和快捷，我们需要安装另外一个小工具gnome-tweak-tool

> sudo apt-get install gnome-tweak-tool

待系统安装好后，重启，在登录界面选择gnome 桌面环境，你就可以看见新的桌面了。现在我们使用还是默认的gnome主题。接下来安装GTK 主题 Elementary 3。

Elementary 3 使用了新的 Unico 引擎。因此我们当然也需要安装。

> sudo apt-get install gtk3-engines-unico

由于Elementary 主题文件，不在ubuntu默认的软件源仓库里面，因为我们需要首先添加软件源仓库，然后在安装：

> sudo add-apt-repository ppa:noobslab/themes

> sudo apt-get update

> sudo apt-get install elementary-theme elementary-icon-theme

安装好后，打开gnome-tweak-tool(也可以在命令行直接输入改命令即可打开)，在Theme选项中的GTK+ Theme中就可以选择Elementary主题了。

Elementary 还有个黑色版本可以通过下面的方式安装：

> sudo apt-get install elementary-dark-theme

给大家秀秀这个主题：

由于我截图的时候，保存的时候是jpg，因为图片有失真，不过真的是相当惊艳。各位可以安装试试！

我安装的时候，并不是严格按照这个路线，如果大家发现这种路线不能正常安装，请留言。

资料：官方网站 提供基本Elementary优化的linux下载 ：http://elementaryos.org/

gnome主题页面： http://gnome-look.org/content/show.php/elementary+GTK+theme?content=149900

0%