分类目录归档:算法

学习二叉树,研究数据结构的本质

二叉树的定义(递归定义)[Binary Tree]:它是n(n>=0)个节点的有限集合,当n=0时,它是空集,n<>0时,它由一个根节点和两棵互不相交的子二叉树构成,分别称为左、右子树。
二叉树有五种基本形态:(1)空集;(2)左子树为空;(3)右子树为空;(4)左右皆为空;(5)左右皆不为空。
有上面二叉树的定义知,每一个二叉树中的节点有如下特征:(1)至多两个子节点;(2)节点有左右之分;(3)有一个左节点活又节点;(4)没有子节点。
二叉树与树、有序树的不同点: 二叉树与度数不超过不超过2的树和有序树不同。在有序树中,虽然也有左右之分,但如果一个节点只有一个子节点,就无须区分左右节点。而在二叉树中,即使一个节点也是区分左右的,
二叉树遍历(Traversal) 二叉树的遍历就是沿着某条搜索路径,一次对树中的每个节点访问且仅访问一次。由二叉树的递归定义知,我们可以用递归方法遍历:
a. 访问树的根节点(N--root Node);
b. 遍历根节点的左子树(L–Left);
c.遍历根节点的右子树(R);
由于二叉树是“L/NR” 结构,所以我们可以有如下可能的遍历次序:LNR, LRN; NLR, || NRL; RLN, RNL, 对这个序列进行调整,
LNR, LRN, NLR, || RLN, NRL,RNL, 这时我们就会发现这个序列关于 "||" 轴对称,也就是一边的是另一边的遍历的倒序。所以,我们只需要研究其中的三种就可以了。依据根节点被访问的次序,我们就可以得到前序遍历 NLR(Preorder Traversal), 中序遍历LNR(Inorder Traversal), 后序遍历LRN(Postorder Traversal).
下面我们来看看二叉树的存储问题。在这里,我们现不研究它如何在硬盘/文件中的存储方式,先研究如何把它用数据结构方式存储到内存中,方便我们的操作。
二叉树的存储(内存中的结构)对于二叉树,它的结构由以下三个要素构成:(1)节点;(2)节点的连接关系;(3)父节点相同时子节点的左右次序。知道了这三个要素,我们也就知道了我们需要设计出一种结构来保存这三个元素,但此时,我们还要多想一点,这三个元素中哪哪一个是主导元素呢? 是节点?边?左右次序? 这时也许要迷惑了,为什么要想这么多呢? 请注意,虽然它自身在定义时是以节点为主体来定义的,但二叉树是一种抽象的结构,它不仅仅可以存储节点(一般被用来保存数据),同时也保存了边(一般保存了节点之间的关系,如父子关系。),所以,以何为主体完全可以由自己的实机需要来决定。
下面我们先假设我们认为主体是节点(这也是数据结构教科书中常常默认的),也就是我们要处理的数据,那我们可以设计出下面的结构,
/*****************************
* binary tree node definition
*****************************/
typedef struct binaryNode{
T *data; /* T is the type you stored in node. data is the data in the node.*/
struct binaryNode *left; /* left node of parent node. */
struct binaryNode *right; /* right node of parent node. */
}BinaryNode;
由前面遍历的递归描述我们可以得到下面的递归遍历算法,
/*********************
*binarytree.h
**********************/
#include
#include
#include

/*****************************
* binary tree node definition
*****************************/
typedef struct binaryNode{
T *data; /* T is the type you stored in node. data is the data in the node.*/
struct binaryNode *left; /* left node of root. */
struct binaryNode *right; /* right node of root. */
}BinaryNode;

/********************************
* Inorder binary tree traversal.
********************************/
void inorder(BinaryNode* root)
{
if (root != NULL) {
inorder(root -> left); /* recur left */
printf("%c ", root -> data);
inorder(root -> right); /* recur right */
}
}

/*********************************
* Preorder binary tree traversal.
**********************************/

void preorder(BinaryNode* root)
{
if (root != NULL) {
printf("%c ", root -> data);
preorder(root -> left);
preorder(root -> right);
}
}

/***********************************
* Postorder binary tree traversal.
***********************************/

void postorder(BinaryNode* root)
{
if (root != NULL) {
postorder(root -> left);
postorder(root -> right);
printf("%c ", root -> data);
}
}

这个递归遍历算法看起来很简单,但效率不会太高。因为递归调用会倒置没递归一次都需要在栈中保存一个函数指针,而系统的栈的空间大小却是非常有限的(一般是 1M到几M),所以,当数据很多时就可能出现stack over flow的错误。所以,这个递归算法通常在小数据量的情形下是可行的,太多的数据时就得考虑用非递归算法了。
接下来,我们就考虑用非递归方式实现遍历。可参考http://blog.csdn.net/rainer7/archive/2004/08/10/70671.aspx

作者:豆博草堂

用python写的一个判断是否为质数的函数

 

#!/usr/bin/python
# Judge whether a interger is a prime.
#isprime.py

import sys
import math

def isPrime(num):
    if not isinstance(num, int):
       return False
    anum = num
    if anum < 0:
       anum = math.fabs(anum)
    if anum == 1:
       return False
    if anum == 2 or anum == 3:
       return True
    if math.fmod(anum, 2) == 0:
       return False
    endN = math.sqrt(anum) + 1
    i = 3
    ret = True
    while i < endN:
       if math.fmod(anum, i) == 0:
          ret = False
          break;
       i += 2
    return ret

if __name__ == "__main__":
      if len(sys.argv) < 2:
         print "usage is:"
         print "python isprime.py <value1> [< <space>value2>< <space>value3>...]"
      numlist = sys.argv[1:]
      n = 0
      for num in numlist:
          try:
             n = int(num)
             if isPrime(n) :
                print num + " is a prime."
             else :
                print num + " is not a prime."
          except ValueError:
                print num + " is not a prime."

作者:豆博草堂

图灵机、工作流

        今天我开始根据需求设计我们的ER-图了。 然而我们项目里面有很多流程,为了简化开发,部门提出采用JBPM , 然而,我们以前还没有项目使用过它。为了在目前项目中迅速采用JPBM, 同时也想了解下它可以给我们带来的好处,我就开始上网调查它,发现JBPM看起来好庞大,显得有些复杂。但基本明白了一点:根据xml中定义的流程来动态调用不同的方法。然而,由于对JPBM的不熟悉,我却突然不知如何设计我们的ER-图、数据库表了,以前我们的流程我都会通过在表中加stepOwner(所属阶段)相关属性来控制流程流转的。 JPBM对我来说有些复杂,我需要一个简单的流程引擎。 细细想来,流程其实就是图灵机的变体,只不节点的粒度大了些,但都是根据当前状态来确定下一步动作,然后更新状态。鉴于这种考虑,我想设计一个简单的流程辅助工具(不能称为引擎,引擎所表达的功能太过强大。)。 下图就是我设计的简单工作流的执行过程: 执行过程基本就是:根据当前所处的处理阶段和状态码到工作流定义库中查询要执行的动作,然后执行相应动作, 执行完之后根据工作流定义更新流程相应的阶段和状态码。 工作流定义为:

REC

字段名称

数据类型

长度

可空

Key

说明

1

key

varchar2

50

NO

YES

规则键值

2

stepOwner

varchar2

50

NO

YES

所属阶段

3

currentState

varchar2

50

NO

YES

当前状态

4

action

300

50

YES

NO

在当前阶段、当前状态采取的动作。

5

priority

int

4

NO

NO

优先级:<stepOwner,currentState>下,优先级值小的优先选择。(用户在代码中可以优先选取。)

6

afterStepOwner

varchar2

50

NO

NO

动作执行之后的阶段所属角色。

7

afterState

varchar2

50

NO

NO

动作执行之后的状态标识。

 
 
 
 
 
 
 
 
 
 
 
 
 
 

 

 
 
 
 
举例来说,有下面一个流程,
要在workFollow表中描述这个流程,我们可以在表中添加下面这些记录(省略了key和priority字段):
REC stepOwner currentState action afterStepOwner afterState
1 ROLE_A EDIT_DOCUMENT SUBMIT ROLE_B WAIT_AUDIT
2 ROLE_B WAIT_AUDIT AUDIT_ACCEPT NONE NONE
3 ROLE_B WAIT_AUDIT AUDIT_REJECT ROLE_A EDIT_DOCUMENT
其中, 第一条表示:在Role A 处理时,如果处于编辑文档状态(EDIT_DOCUMENT),则执行提交动作(SUBMIT),提交后处理阶段变为 Role B, 状态变为等待审核(WAIT_AUDIT)。 第二条和第三条:在Role B处理时,如果文档处于等待审核状态(WAIT_AUDIT),Role B可以执行两种动作,审核通过(AUDIT_ACCEPT)和不通过(AUDIT_REJECT)。如果通过, 则结束,即将所处阶段置和状态为NONE。不通过时,将阶段置为Role A(ROLE_A), 将状态置为编辑文档(EDIT_DOCUMENT) 当然,为了记录驱动我们工作流的两个关键属性:所属阶段(stepOwner)和当前状态(currentState), 我们需要在我们的业务属性表中添加这两个字段。 有了这个基本模型后,我们就可以将流程分离到数据库中,用工作流定义来驱动程序走向。这就是我这两天设计的SimpleWorkFollow。这个简单的工作流我正在逐步完善中,但我绝不会让它变的复杂。希望这些思想对大家有所帮助。 我们项目是使用JBPM还是我这个简单的工作流有待评估。因为我们的流程很复杂,而且不同流程之间有很多关联。

作者:豆博草堂