<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>四号程序员四号程序员</title>
	<atom:link href="http://www.coder4.com/feed" rel="self" type="application/rss+xml" />
	<link>http://www.coder4.com</link>
	<description>Keep It Simple And Stupid</description>
	<lastBuildDate>Sat, 19 May 2012 08:01:01 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>数据结构重读 – 二叉树</title>
		<link>http://www.coder4.com/archives/3177</link>
		<comments>http://www.coder4.com/archives/3177#comments</comments>
		<pubDate>Sat, 19 May 2012 08:01:01 +0000</pubDate>
		<dc:creator>coder4</dc:creator>
				<category><![CDATA[算法&数据结构]]></category>
		<category><![CDATA[二叉树]]></category>
		<category><![CDATA[数据结构]]></category>
		<category><![CDATA[重读]]></category>

		<guid isPermaLink="false">http://www.coder4.com/?p=3177</guid>
		<description><![CDATA[1、<strong><span style="color: #ff0000;">二叉树</span></strong>(Binary Tree)是一种特殊的树形结构：每个结点至多有两棵子树，即二叉树中不存在度大于2的结点。

2、二叉树的子树有左右之分，次序不能任意颠倒。

3、<strong><span style="color: #ff0000;">性质1</span></strong>：二叉树的第i层上至多有2^(i-1)个结点，i从1下标开始。所有

4、<strong><span style="color: #ff0000;">性质2</span></strong>：深度为k的二叉树至多有2^k -1个结点，k也是从1下标开始。

5、<strong><span style="color: #ff0000;">性质3</span></strong>：对于任何一棵二叉树，如果终端结点数为n0，度为2的结点数为n2，则n0 = n2+1。这个很简单：

(1)设度为1的结点数为n1，则总结点数n = n0 + n1 + n2。
(2)根据先前的(任意树)上的公式：结点数量 = 所有结点度之和 + 1，即n = n0+ n1 + n2 = n1 + 2 n2 +1，两边消掉，有：

n0 = n2 + 1。

6、<strong><span style="color: #ff0000;">满二叉树</span></strong>：一棵深度为k，且有2^k -1个结点的二叉树。特点是每一层都是满的。

7、<strong><span style="color: #ff0000;">完全二叉树</span></strong>：<strong><span style="color: #ff0000;">不是满二叉树</span></strong>！它的结点可以不足2^k - 1，但是深度必须和对应的满二叉树对应，已经有的结点从1~k必须和同深度的满二叉树一一对应(即在满二叉树的第k层从右向左去掉若干个结点，至少保留一个结点)。

8、满二叉树是完全二叉树，但完全二叉树不是满二叉树！

9、<strong><span style="color: #ff0000;">性质4</span></strong>：具有n个结点的完全二叉树，深度为[ log(n)] + 1，其中[]是向下取整。

10、<strong><span style="color: #ff0000;">性质5</span></strong>：如果对一棵有n个结点的完全二叉树的结点按层次编号，每层从左到右，层次从上到下。对任意结点i，
(1) 如果i=1，结点i是二叉树的根，无双亲。i&#62;1，则双亲PARENT(i)是结点[i/2]，还是向下取整。
(2) 如果2i&#62;n，则结点无左孩子，否则左孩子是2i。
(3)如果2i+1&#62;n，无右孩子，否则右孩子是2i+1。

11、二叉树的顺序存储，利用上述性质5，可以把任何一个二叉树映射到完全二叉树上，从而映射到数组上。当然这个数组的中间某些位置可能是“空的”。

12、

&#160;

&#160;

&#160;]]></description>
		<wfw:commentRss>http://www.coder4.com/archives/3177/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>数据结构重读 – 树的定义和基本术语</title>
		<link>http://www.coder4.com/archives/3173</link>
		<comments>http://www.coder4.com/archives/3173#comments</comments>
		<pubDate>Thu, 17 May 2012 14:21:25 +0000</pubDate>
		<dc:creator>coder4</dc:creator>
				<category><![CDATA[算法&数据结构]]></category>
		<category><![CDATA[定义]]></category>
		<category><![CDATA[数据结构]]></category>
		<category><![CDATA[树]]></category>
		<category><![CDATA[重读]]></category>

		<guid isPermaLink="false">http://www.coder4.com/?p=3173</guid>
		<description><![CDATA[1、树是n(n&#62;=0)个结点的有限集合。树中有且仅有一个结点为根(Root)。

2、当定义1中的n&#62;1时，其余结点可以分为m个互不相交的有限集合T1、T2。。。每一个子集都是一颗树，并且是根的子树。

3、树中结点的<strong><span style="color: #ff0000;">度</span></strong>：结点拥有子树的个数(分叉数)称为结点的<strong><span style="color: #ff0000;">度(Degree)</span></strong>。

4、度为0的结点称为<strong><span style="color: #ff0000;">叶子(Leaf)</span></strong>结点。度非0的结点是<strong><span style="color: #ff0000;">分支结点</span></strong>或非终端结点。

5、<strong><span style="color: #ff0000;">公式：树中结点的数量 = 所有结点的度之和 + 1</span></strong>。

6、结点的子树的根称为该结点的孩子。该结点称为孩子的双亲。

7、<strong><span style="color: #ff0000;">祖先</span></strong>：从根到该结点所经过的分支上的所有结点。

8、<strong><span style="color: #ff0000;">层次</span></strong>(Level)：从根开始定义为第一层，根的孩子是第2层。。。以此类推。

9、树中结点最大的层次称为<strong><span style="color: #ff0000;">深度</span></strong>(Depth)。

10、如果树中结点的各个子树看成从左到右是有次序的，则称为<strong><span style="color: #ff0000;">有序树</span></strong>，否则是<strong><span style="color: #ff0000;">无序树</span></strong>。

11、<strong><span style="color: #ff0000;">森林</span></strong>是m棵互不相交的树构成的集合。

&#160;]]></description>
		<wfw:commentRss>http://www.coder4.com/archives/3173/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>数据结构重读 &#8211; 矩阵乘法</title>
		<link>http://www.coder4.com/archives/3170</link>
		<comments>http://www.coder4.com/archives/3170#comments</comments>
		<pubDate>Thu, 17 May 2012 06:05:50 +0000</pubDate>
		<dc:creator>coder4</dc:creator>
				<category><![CDATA[算法&数据结构]]></category>
		<category><![CDATA[数据结构]]></category>
		<category><![CDATA[矩阵]]></category>
		<category><![CDATA[重读]]></category>

		<guid isPermaLink="false">http://www.coder4.com/?p=3170</guid>
		<description><![CDATA[矩阵乘法最naive的版本，自己数学弱爆了，矩阵乘法已经不知道怎么算了……

先科普下吧。

3   0   0   5
(A) 0  -1   0   0
2   0   0   0

乘

0   2
(B)  1   0
-2  4
0   0

首先乘出来的结果是，新矩阵的行是A的行，新矩阵的列是B的列。

计算方法是，首先A的第1行点乘(每个位置上分别乘)B第1列的元素，做为结果矩阵(1, 1)上的元素。然后A的第1行点乘第2列，做为结果矩阵(1, 2)。。。依次类推。

算法如下：
<pre class="brush: c; gutter: true">#include &#60;stdio.h&#62;
#include &#60;string.h&#62;
#define MAX 100

struct Matrix
{
	int data[MAX][MAX];
	int row, col;
};

void matrix_init(struct Matrix* matrix, int row, int col)
{
	matrix-&#62;row = row;
	matrix-&#62;col = col;	
	memset(matrix-&#62;data, 0, sizeof(int)*MAX*MAX);
}

void matrix_print(struct Matrix* matrix)
{
	int i,j;
	for(i=1;i&#60;=matrix-&#62;row;i++)
	{
		for(j=1;j&#60;=matrix-&#62;col;j++)
		{
			printf(&#34;%3d &#34;, matrix-&#62;data[i][j]);
		}
		printf(&#34;\n&#34;);
	}
	printf(&#34;\n&#34;);
}

// Multiply matrix a * b =&#62; c
void matrix_mult(struct Matrix* a, struct Matrix* b, struct Matrix* c)
{
	int i,j,k;
	// C&#039;s row is a&#039;s row, C&#039;s col is b&#039;s col
	matrix_init(c, a-&#62;row, b-&#62;col);
	for(i=1;i&#60;=a-&#62;row;i++)
	{
		for(j=1;j&#60;=b-&#62;col;j++)
		{
			c-&#62;data[i][j] = 0;
			for(k=1;k&#60;=a-&#62;col;k++)
			{
				c-&#62;data[i][j] += a-&#62;data[i][k] * b-&#62;data[k][j];
			}
		}
	}
}

int main()
{
	struct Matrix m, n, r;

	// Set matrix m
	matrix_init(&#38;m, 3, 4);
	m.data[1][1] = 3;
	m.data[1][4] = 5;
	m.data[2][2] = -1;
	m.data[3][1] = 2;

	// Set matrix n
	matrix_init(&#38;n, 4, 2);
	n.data[1][2] = 2;
	n.data[2][1] = 1;
	n.data[3][1] = -2;
	n.data[3][2] = 4;

	// Multi m*n =&#62; r
	matrix_mult(&#38;m, &#38;n, &#38;r);

	// Print
	matrix_print(&#38;m);
	matrix_print(&#38;n);
	matrix_print(&#38;r);
	return 0;
}</pre>]]></description>
		<wfw:commentRss>http://www.coder4.com/archives/3170/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>数据结构  &#8211; 树的度和结点数的关系</title>
		<link>http://www.coder4.com/archives/3168</link>
		<comments>http://www.coder4.com/archives/3168#comments</comments>
		<pubDate>Thu, 17 May 2012 05:34:01 +0000</pubDate>
		<dc:creator>coder4</dc:creator>
				<category><![CDATA[算法&数据结构]]></category>
		<category><![CDATA[分叉]]></category>
		<category><![CDATA[度]]></category>
		<category><![CDATA[树]]></category>
		<category><![CDATA[结点]]></category>

		<guid isPermaLink="false">http://www.coder4.com/?p=3168</guid>
		<description><![CDATA[<strong>一、概念</strong>

与图论中的“度”不同，树的度是如下定义的：有根树T中，结点x的子女数目称为x的度。也就是：<strong><span style="color: #ff0000;">在树中，结点有几个分叉，度就是几。</span></strong>

一个有用的小公式：<strong><span style="color: #ff0000;">树中结点数 = 总分叉数 +1</span></strong>。(这里的分叉数就是所有结点的度之和)

<strong>二、度的计算</strong>

1.设树T的度为4，其中度为1，2，3，4的节点个数分别为4，2，1，1，则T中的叶子数为？

<strong>解：</strong>

叶子的度数为0；那么设叶子数为x，则此树的总分叉数为1*4+2*2+3*1+4*1=15；此树的节点个数为16（此处涉及到一个公式;节点数=分叉数+1，由图形便可以观察出来）。又根据题目可以知道顶点数目还可以列出一个式子：4+2+1+1+x便可以得到等式：4+2+1+1+x=16；x=8为叶子数。

因为此题是数据结构中的问题：一般情况下都是有向树，所以叶子节点的度数为0，要区分于离散数学中的无向树叶子节点度为一。在数据结构中一般常用的公式为：二叉树：度为0的节点数=度为2的节点数+1（n0=n2+1）此公式可由上述计算思想推导（一般在二叉树那里的公式多一些，树中只要你明确定义，画出图来，便可以根据图形寻找出规律来）]]></description>
		<wfw:commentRss>http://www.coder4.com/archives/3168/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Zookeeper中几种数据事件的触发、设置条件</title>
		<link>http://www.coder4.com/archives/3164</link>
		<comments>http://www.coder4.com/archives/3164#comments</comments>
		<pubDate>Wed, 16 May 2012 12:21:42 +0000</pubDate>
		<dc:creator>coder4</dc:creator>
				<category><![CDATA[Hadoop && 分布式存储 && RPC]]></category>
		<category><![CDATA[ZooKeeper]]></category>

		<guid isPermaLink="false">http://www.coder4.com/?p=3164</guid>
		<description><![CDATA[本文小结下Zookeeper中4种数据事件(ZOO_CHANGED_EVENT, ZOO_CHILD_EVENT, ZOO_CREATED_EVENT, ZOO_DELETED_EVENT)的触发条件。

如下表所示：
<table border="1">
<tbody>
<tr>
<td> 事件</td>
<td>触发条件</td>
<td> Watcher设置函数</td>
</tr>
<tr>
<td>ZOO_CHANGED_EVENT</td>
<td>only generated by watches on nodes</td>
<td>zoo_exists / zoo_get</td>
</tr>
<tr>
<td>ZOO_CHILD_EVENT</td>
<td>only generated by watches on the child list of a node</td>
<td>zoo_get_children / zoo_get_children2</td>
</tr>
<tr>
<td>ZOO_CREATED_EVENT</td>
<td>only generated by watches on the child list of a node</td>
<td>zoo_exists</td>
</tr>
<tr>
<td>ZOO_DELETED_EVENT</td>
<td>only generated by watches on nodes</td>
<td>zoo_exists / zoo_get</td>
</tr>
</tbody>
</table>]]></description>
		<wfw:commentRss>http://www.coder4.com/archives/3164/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[转载]SO YOU WANT TO BE A ZOOKEEPER?</title>
		<link>http://www.coder4.com/archives/3161</link>
		<comments>http://www.coder4.com/archives/3161#comments</comments>
		<pubDate>Wed, 16 May 2012 10:06:47 +0000</pubDate>
		<dc:creator>coder4</dc:creator>
				<category><![CDATA[Hadoop && 分布式存储 && RPC]]></category>
		<category><![CDATA[ZooKeeper]]></category>

		<guid isPermaLink="false">http://www.coder4.com/?p=3161</guid>
		<description><![CDATA[这篇文章写的很好，主要是使用Zookeeper开发应用程序中遇到的一些很实际的问题。

转载自：<a href="http://outerthought.org/blog/435-ot.html">《SO YOU WANT TO BE A ZOOKEEPER?》</a>

This article is about some specific things I learned about Apache ZooKeeper. Apache ZooKeeper is useful if you are writing a distributed application and need coordination between processes for things like configuration, group membership, leader election, locking and the like.

ZooKeeper has quite good documentation, and I have provided pointers where applicable, but maybe writing from my own experience can help shorten someone else's learning curve.

<strong>Connection loss and session expiration</strong>

A central issue you need to think about when implementing ZooKeeper is how to deal with connection loss and session expiration. Since ZooKeeper is the central coordination service, once an application loses its connection with ZooKeeper it is left in an uncertain situation.

To survive temporary network hickups, or the death of the particular ZooKeeper server you are connected to, ZooKeeper has the notion of a session timeout. If you are able to reconnect to one of the servers in the ZooKeeper ensemble within this timeout, the ephemeral nodes and watches created by the application will not be lost. Various reasons for connection problems are described in the Troubleshooting page on the wiki.

ZooKeeper reports the loss of connection to the application as soon as it happens, through a Disconnected event sent to all watchers. Once the connection is reestablished, it reports a SyncConnected event. Even if the connection would only be lost for a very small amount of time, you will get these two events.

If an application only succeeds to reconnect to a ZooKeeper server after the session timeout passed, it will get an Expired event. The important thing to note here is that this event is produced by the server. If the ZooKeeper client cannot connect to the server for any amount of time, even if it is much longer than the session timeout (like hours), your application will not get any event besides the initial Disconnected event. At first I did not understand why the ZooKeeper client cannot produce this event locally, if it has not been able to reestablish the connection within the session timeout. Turns out there is a reason for it (same here).

How to deal with connection loss is pretty much a decision to be made for each application. If the application is just some sort of 'worker' it could pause until the connection is back. If the application is elected as leader, can it continue to assume it is the leader when it is not longer connect to ZooKeeper? See this post on active &#38; passive leaders and also this one for some guidelines. If the application is a server, should it continue to accept client requests knowing it might be partitioned from other servers or not have the latest configuration information from ZooKeeper? These are the sorts of things you need to decide on. Here is another post suggesting some best practices. Once you delve into the mailing list archives you will find plenty of discussions related to dealing with connection loss.

Session expiration is less common than connection loss and harder to recover from, as you need to handle reconnection yourself by creating a new ZooKeeper handle. An easy solution is to simply restart the application at this point.

<strong>Dealing with ConnectionLoss exceptions</strong>

When connection loss occurs, any running Zookeeper operation (like getChildren, get/setData, create, delete) will throw a ConnectionLoss exception. Somehow you have to deal with these. One way is to retry the operation until it succeeds (= until the connection is back). For example in the lock recipe found in the ZooKeeper source tree, they use this pattern, see the ProtocolSupport.retryOperation method. This technique is also present in the ZKClient library.

When an operation throws a ConnectionLoss exception, you are not sure if it succeeded or not. The connection might have failed before or after it got through to the server. So you have to consider this when retrying the operation. A create might fail with a NodeExists exception, a delete might fail with a NoNode exception.

Suppose you implement a ZooKeeper-based lock using the creation of ephemeral sequence nodes. If you retry the operation when it fails, you could have created two ephemeral nodes, but you will not know the name of the first. You can solve this by looping over the znodes and checking the Stat.ephemeralOwner field. If you would not retry the operation, and e.g. simply exit the lock-taking procedure, you might have left an epehemeral node and hence make it impossible for others to obtain the lock.

The ErrorHandling page on the wiki contains some more detail on this topic.

<strong>Storing multiple properties in the data of one node</strong>

Given ZooKeeper's tree model, it might be tempting to store an entity as a znode with subnodes for each of the properties of the entity. This allows for fine-grained reading, writing, and watching.

But with this approach you cannot create the entity atomically. As a consequence, clients can see it in a partially-created, partially-updated or partially-deleted state. If you want to watch the complete state of the entity, you would need to install watchers on all of the individual nodes. So at second sight, this becomes quite complex.

Therefore, I found it easier to store certain configuration entities in their entirety in the data of one node. In my case, I stored it as JSON.

Here is a mail about this topic.

If you are in a situation where storing it all in one znode is not desirable, another technique is the one of the ready znode described in the ZooKeeper paper.

The single event thread

Each ZooKeeper handle (= ZooKeeper object instance) has one thread for dispatching the events to all the watchers. So watchers are called in sequence. If you perform some time consuming action in a watcher, all the other watchers will have to wait before they get a chance to process their event.

Thus: do not do anything time-consuming in a watcher, such as IO, waiting on a lock, etc. One case to watch out for is not to do something which in itself might again wait for a ZooKeeper event. I made for example this mistake trying to take a ZooKeeper-based lock (of which the implementation details where in another class) in a watcher callback. In this particular situation, it was easy enough to push work on a queue that is processed by a different thread.

If you use multiple ZooKeeper handles in your application, each has its own event dispatching thread, so you only have to worry about this for all the users on one particular ZooKeeper handle. It seems common practice to have just one ZooKeeper handle though.

The event thread is also described in the Programmer's Guide.

<strong>Registering the same watcher object multiple times for the same event</strong>

If you register the same watcher object (= the exact same instance) for the same event multiple times, it will be called only once. Thus if you do:

Watcher w = ...

zooKeeper.getChildren(“/foo”, w);

zooKeeper.getChildren(“/foo”, w);

The watcher w will be called only once when the children change. See the Programmer's Guide where it mentions "A watch object, or function/context pair, will only be triggered once for a given notification.".

I found this very handy in the following situation. Suppose you install a watch, it gets triggered, and in the Watcher callback you reinstall the same watch. Since watchers are one-time triggers this is a common thing to do. Now it could be that reinstalling the watch fails due to a ConnectionLoss exception. As mentioned earlier, if you get a ConnectionLoss exception, you do not know if the operation succeeded, so you do not know if installing the watch succeeded. And since you are in a watcher callback, doing something blocking like retrying the operation until it succeeds is not a good idea.

Therefore, in such case you can simply install the watcher everytime you get a SyncConnected event, without worrying that the watcher will then be called twice when an event occurs.

<strong>InterruptedException</strong>

Many ZooKeeper's API methods throw InterruptedException. This is a checked exception, so you are forced to do something with it. This might seem annoying at first, but InterruptedException is actually a very useful exception: when a method throws InterruptedException, it tells you there is a possibility to interrupt it.

What is the best way to handle InterruptedException? Well, there is a nice article about this by Brian Goetz. Basically, you should make sure not to stop the interruption of the thread. Either by throwing on the InterruptedException, or if this is not possible, by re-enabling the interrupted flag (call Thread.currentThread().interrupt).

<strong>Miscellaneous</strong>

To locally test your application against a ZooKeeper ensemble, for example to test how it reacts to session moves, be sure to check out zkconf: it enables to set up an ensemble in seconds. Do not forget to use odd numbers of servers (see ZooKeeper admin guide).

As developer, be sure to also read the ZooKeeper Administrator's Guide. There is useful information in there such as the “four letter word” commands you can use to query some information and metrics. Also check out zktop.]]></description>
		<wfw:commentRss>http://www.coder4.com/archives/3161/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>数据结构重读 &#8211; 矩阵的压缩和存储</title>
		<link>http://www.coder4.com/archives/3156</link>
		<comments>http://www.coder4.com/archives/3156#comments</comments>
		<pubDate>Tue, 15 May 2012 02:01:19 +0000</pubDate>
		<dc:creator>coder4</dc:creator>
				<category><![CDATA[算法&数据结构]]></category>
		<category><![CDATA[压缩]]></category>
		<category><![CDATA[存储]]></category>
		<category><![CDATA[快速转置]]></category>
		<category><![CDATA[数据结构]]></category>
		<category><![CDATA[矩阵]]></category>
		<category><![CDATA[重读]]></category>

		<guid isPermaLink="false">http://www.coder4.com/?p=3156</guid>
		<description><![CDATA[<strong><span style="color: #ff0000;">矩阵</span></strong>在数值运算中很常见，本节关注如何存储矩阵的元，从而使矩阵的各种运算能有效进行。

如果矩阵中有许多相同值的元素或者很多零元素。有时为了节省存储空间，可以对这类矩阵进行存储压缩，称为<strong><span style="color: #ff0000;">稀疏矩阵</span></strong>。更进一步的，如果稀疏矩阵的相同值或零元素分布还是有规律的，我们可以称他们为<strong><span style="color: #ff0000;">特殊矩阵</span></strong>。

<strong>对称矩阵</strong>

例如：

1 2 4
2 3 5
4 5 6

我们可以为每一对称元分配一个存储空间，即可以将n^2个元压缩存储到n*(n+1)/2个空间中。

假设在线性(一元)数组中存储，下标是k。m[i][j]是矩阵中第i行第j列的元素，则：

当i&#62;=j时，a[k] = m[i][j]，k=i*(i-1)/2 + j -1

当i&#60;j时，a[k] = m[i][j]，k = j*(j-1)/2 +i -1

<strong>稀疏矩阵</strong>

设在m*n的矩阵中，有t个元素非零，则a=t/(m*n)称为稀疏因子，当a&#60;=0.05，<strong><span style="color: #ff0000;">即只有不到5%的元素非零时，称为稀疏矩阵</span></strong>。

在很多问题中，实际都是稀疏矩阵。

压缩存储可以只存储非零元，例如用一个三元组表示：(i, j, val)，i和j是行、列，val是矩阵位置的元素值。

注意：在如下三元组存储中，data是从下标0开始的。但是矩阵的行、列是从1开始的！

下面是数据结构定义、初始化、打印等操作：
<pre class="brush: c; gutter: true">#include &#60;stdio.h&#62;
#include &#60;memory.h&#62;

#define MAX 1024

struct Triple
{
	int i,j; // row and column
	int value;
};

struct TMatrix
{
	struct Triple data[MAX]; // Triple
	int ndata; // number of data
	int rows; // row nums
	int cols; // col nums
};

void tmatrix_init(struct TMatrix* matrix)
{
	matrix-&#62;ndata = matrix-&#62;rows = matrix-&#62;cols = 0;
}

void tmatrix_print(struct TMatrix* matrix)
{
	int i,j;
	int k = 0;
	for(i=1;i&#60;=matrix-&#62;rows;i++)
	{
		for(j=1;j&#60;=matrix-&#62;cols;j++)
		{
			if(k&#60;matrix-&#62;ndata &#38;&#38; matrix-&#62;data[k].i==i &#38;&#38; matrix-&#62;data[k].j==j)
			{
				printf(&#34;%3d &#34;, matrix-&#62;data[k].value);
				k++;
			}else
			{
				printf(&#34;%3d &#34;, 0);
			}
		}
		printf(&#34;\n&#34;);
	}
}

int main()
{
	// Init matrix m
	struct TMatrix m, t;
	tmatrix_init(&#38;m);
	// Set m
	m.rows = 6;
	m.cols = 7;
	m.ndata = 8;
	m.data[0].i = 1;
	m.data[0].j = 2;
	m.data[0].value = 12;
	m.data[1].i = 1;
	m.data[1].j = 3;
	m.data[1].value = 9;
	m.data[2].i = 3;
	m.data[2].j = 1;
	m.data[2].value = -3;
	m.data[3].i = 3;
	m.data[3].j = 6;
	m.data[3].value = 14;
	m.data[4].i = 4;
	m.data[4].j = 3;
	m.data[4].value = 24;
	m.data[5].i = 5;
	m.data[5].j = 2;
	m.data[5].value = 18;
	m.data[6].i = 6;
	m.data[6].j = 1;
	m.data[6].value = 15;
	m.data[7].i = 6;
	m.data[7].j = 4;
	m.data[7].value = -7;
	// Fast Trans
	tmatrix_fast_transpose(&#38;m, &#38;t);
	// Print m
	//tmatrix_print(&#38;m);
	tmatrix_print(&#38;t);
	return 0;
}</pre>
<strong><span style="color: #ff0000;">快速转置算法</span></strong>

这样的存储虽然省了不少空间，但是在一些操作如转置时算法复杂度会上升很多。因为你无法再随机访问m[i][j]了。针对这种情况，有了如下的快速转置算法。它使用两个辅助向量，nrows和frows。这里的rows都是针对转置结果矩阵T而言的。（我们假设从矩阵M转置称为矩阵T）

首先计算nrows，它表示矩阵T中每一行有多少个非空元素即三元组需要存储。实际上也就是M中列j为对应nrows下标的。于是我们扫描一遍M.data的所有元素，对应j在nrows下标中++即可。

然后计算frows，就是T矩阵每一行第一个元素应该位于T的data的哪个位置。初始的第一个frows[1] = 0。这个需要解释下，因为矩阵行列开始都是1,所以frows里面是1,但是data存储是从0开始，所以起始是0。

最后就是快速转置算法，利用上面两个向量就很简单了，直接上代码：
<pre class="brush: c; gutter: true">// Transpose m to t
void tmatrix_fast_transpose(struct TMatrix* m, struct TMatrix* t)
{
	// Vars
	int nrows[MAX]; // Number of triple in new matrix t each row(t)
	int frows[MAX]; // First position in new matrix t each row(t)
	int i,j;
	// Basic dat
	t-&#62;rows = m-&#62;cols;
	t-&#62;cols = m-&#62;rows;
	t-&#62;ndata = m-&#62;ndata;
	// Calculate nrows(t), num of data same col in m
	memset(nrows, 0, MAX*sizeof(int));
	j=0;
	for(i=0;i&#60;m-&#62;ndata;i++)
	{
		nrows[m-&#62;data[i].j]++;
	}
	// Calculate frows
	memset(frows, 0, MAX*sizeof(int));
	frows[1] = 0; // Matrix&#039;s row / col is 1/1, but in data, stores begin at [0]
	for(i=2;i&#60;=m-&#62;ndata;i++)
	{
		frows[i] = frows[i-1] + nrows[i-1];
	}
	// Transpose
	for(i=0;i&#60;m-&#62;ndata;i++)
	{
		t-&#62;data[frows[m-&#62;data[i].j]].j = m-&#62;data[i].i;
		t-&#62;data[frows[m-&#62;data[i].j]].i = m-&#62;data[i].j;
		t-&#62;data[frows[m-&#62;data[i].j]].value = m-&#62;data[i].value;
		frows[m-&#62;data[i].j]++;
	}

}</pre>
&#160;

&#160;

&#160;]]></description>
		<wfw:commentRss>http://www.coder4.com/archives/3156/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>C系语言中多维数组的理解</title>
		<link>http://www.coder4.com/archives/3153</link>
		<comments>http://www.coder4.com/archives/3153#comments</comments>
		<pubDate>Mon, 14 May 2012 14:29:53 +0000</pubDate>
		<dc:creator>coder4</dc:creator>
				<category><![CDATA[C && C++]]></category>
		<category><![CDATA[c语言]]></category>
		<category><![CDATA[多维数组]]></category>

		<guid isPermaLink="false">http://www.coder4.com/?p=3153</guid>
		<description><![CDATA[int array[m][n];

这个二维数组，可以堪称是m个长度为n的一维数组。

在内存中排列的方式是[0][1]..[n-1]  [0][1]...[n-1]....一共m组这样的。

在访问时，array[m][n] = *(*(array+m)+n)，对N维的数组取值时要用到N个*。

参考了这个：http://blog.csdn.net/hai836045106/article/details/6729756]]></description>
		<wfw:commentRss>http://www.coder4.com/archives/3153/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>数据结构重读 &#8211; KMP串匹配算法</title>
		<link>http://www.coder4.com/archives/3150</link>
		<comments>http://www.coder4.com/archives/3150#comments</comments>
		<pubDate>Mon, 14 May 2012 08:34:03 +0000</pubDate>
		<dc:creator>coder4</dc:creator>
				<category><![CDATA[算法&数据结构]]></category>
		<category><![CDATA[KMP]]></category>
		<category><![CDATA[字符串]]></category>
		<category><![CDATA[数据结构]]></category>
		<category><![CDATA[重读]]></category>

		<guid isPermaLink="false">http://www.coder4.com/?p=3150</guid>
		<description><![CDATA[设：m是模式串pattern的长度，n是主串长度

传统的字符串匹配(暴力法)的时间复杂度是O(n*m)。

而KMP串匹配算法可以将时间复杂度降为O(n+m)，这需要一个额外的预处理O(m)。

KMP优化的地方在于：当出现字符失配的情况时，无需回溯i指针，而是利用已经匹配的部分，将模式串尽可能向右滑动一部分。

实际上：KMP的预处理本身就是一个模式串pattern“自我匹配”的过程。因此，预处理和kmp算法主体非常神似。

预处理过程：
<pre class="brush: c; gutter: true">int get_next(char* p, int* next)
{
	int len = strlen(p);	
	int i = 0, k = -1;
	next[0] = -1;
	while(i&#60;len)
	{
		if(k==-1 &#124;&#124; p[i]==p[k])
		{
			i++;
			k++;
			next[i] = k;
		}else
		{
			k = next[k];
		}
	}
}</pre>
KMP匹配算法，这里我们和书上的做了一点改进，加入了一个start，即可以支持在主串中查找多次模式串。代码如下：
<pre class="brush: c; gutter: true">int kmp_index(char* s, char* p, int* next, int start)
{
	int i=start,j=0;
	int slen = strlen(s);
	int plen = strlen(p);
	while(i&#60;slen &#38;&#38; j&#60;plen)
	{
		if(j==-1 &#124;&#124; s[i]==p[j])
		{
			i++;
			j++;
		}else
		{
			j = next[j];
		}
	}
	if(j&#62;=plen)
	{
		return i-plen;
	}else
	{
		return -1;
	}
}</pre>
测试驱动如下：
<pre class="brush: c; gutter: true">int main()
{
	char* str = &#34;xxabaabcacxxabaabcacdabaabcac99&#34;;
	char* pattern = &#34;abaabcac&#34;;
	int next[MAX];
	int plen = strlen(pattern);
	int i;
	// Get next value
	get_next(pattern, next);
	for(i=0;i&#60;plen;i++)
	{
		printf(&#34;%d &#34;, next[i]);
	}
	printf(&#34;\n&#34;);
	// KMP Index
	i=0;
	while(i!=-1)
	{
		i = kmp_index(str, pattern, next, i);
		if(i!=-1)
		{
			printf(&#34;&#039;%s&#039; @ %dth of &#039;%s&#039;\n&#34;, pattern, i, str);
			i++;
		}
	}
	return 0;
}</pre>
&#160;]]></description>
		<wfw:commentRss>http://www.coder4.com/archives/3150/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>数据结构重读 &#8211; 字符串基本操作</title>
		<link>http://www.coder4.com/archives/3147</link>
		<comments>http://www.coder4.com/archives/3147#comments</comments>
		<pubDate>Sun, 13 May 2012 13:39:00 +0000</pubDate>
		<dc:creator>coder4</dc:creator>
				<category><![CDATA[算法&数据结构]]></category>
		<category><![CDATA[基本操作]]></category>
		<category><![CDATA[字符串]]></category>
		<category><![CDATA[数据结构]]></category>

		<guid isPermaLink="false">http://www.coder4.com/?p=3147</guid>
		<description><![CDATA[<strong><span style="color: #ff0000;">字符串</span></strong>(string)是由零个或者多个字符串组成的有限序列。

字符串中字符的数目称为字符串的<strong><span style="color: #ff0000;">长度</span></strong>。

串中任意个连续字符组成的子序列称为改串的<strong><span style="color: #ff0000;">子串</span></strong>。包含子串的串相应地称为<strong><span style="color: #ff0000;">主串</span></strong>。

<strong><span style="color: #ff0000;">串相等</span></strong>：当且仅当两个串的长度相等，并且各个对应位置的字符都相等时。

由一个或者多个空格组成的串'  '称为<strong><span style="color: #ff0000;">空格串</span></strong>，非空字符串！

C语言中的字符串最末尾是'\0'，这个不用解释了。

串赋值StrAssign、串比较StrCompare、串求长StrLength、串连接StrConcat以及求子串SubString构成了字符串操作的最小操作子集。

字符串操作strcmp、strcmp、strcat、strlen操作的实现(不带n的非安全版本)。
<pre class="brush: c; gutter: true">#include &#60;stdio.h&#62;

int string_comp(char* s1, char* s2)
{
	char* p1 = s1;
	char* p2 = s2;	
	while(*p1!=&#039;\0&#039; &#38;&#38; *p2!=&#039;\0&#039;)
	{
		if(*p1&#62;*p2)
		{
			return 1;
		}else if(*p1&#60;*p2)
		{
			return -1;
		}else
		{
			p1++;
			p2++;
		}
	}
	// Length
	if(*p1==&#039;\0&#039; &#38;&#38; *p2!=&#039;\0&#039;)
	{
		return -1;	
	}else if(*p1!=&#039;\0&#039; &#38;&#38; *p2==&#039;\0&#039;)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

void string_copy(char* src, char* dst)
{
	while(*src!=&#039;\0&#039;)
	{
		*dst++ = *src++;
	}
	*dst = &#039;\0&#039;;
}

// Append s2 to s1
void string_concat(char* s1, char* s2)
{
	// Go to end of s1
	while(*s1!=&#039;\0&#039;)
	{
		s1++;
	}
	// Copy s2
	while(*s2!=&#039;\0&#039;)
	{
		*s1++ = *s2++;
	}
	// End
	*s1 = &#039;\0&#039;;
}

int string_length(char* s)
{
	int len = 0;
	while(*s!=&#039;\0&#039;)
	{
		s++;
		len++;
	}
	return len;
}

int main()
{
	char* s1 = &#34;Liheyuanxx&#34;;
	char* s2 = &#34;liheyuan&#34;;
	char buf[1024];
	printf(&#34;string_comp:%d\n&#34;, string_comp(s1, s2));
	string_copy(s1, buf);
	printf(&#34;copy result:%s\n&#34;, buf);
	string_concat(buf, s2);
	printf(&#34;concat result:%s\n&#34;, buf);
	printf(&#34;length:%d\n&#34;, string_length(buf));
	return 0;
}</pre>]]></description>
		<wfw:commentRss>http://www.coder4.com/archives/3147/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

