构建_构建二叉树 - CSDN
精华内容
参与话题
  • 平时在刷leetcode题目中的链表和树时,难免会出现调试不方便的问题。因为习惯了IDEA中debug来进行错误分析,所以本文将实现leetcode刷题中链表和数在IDEA中的调试。 链表 直接上代码。 public static class ListNode...

    第一部分:

    链表

    直接上代码。

    // Definition for singly-linked list.
    public static class ListNode {
        int val;
        ListNode next;
    
        ListNode(int x) {
            val = x;
        }
    }
    
        private static ListNode createLinkedList(int[] arr) {// 将输入的数组输入到链表中
            if (arr.length == 0) {
                return null;
            }
            ListNode head = new ListNode(arr[0]);
            ListNode current = head;
            for (int i = 1; i < arr.length; i++) {// 过程
                current.next = new ListNode(arr[i]);
                current = current.next;
            }
            return head;
        }
    
        private static void printLinkedList(ListNode head) {// 将链表结果打印
            ListNode current = head;
            while (current != null) {
                System.out.printf("%d -> ", current.val);
                current = current.next;
            }
            System.out.println("NULL");
        }
    
        public static void main(String[] args) {
            int[] x = { 1, 2, 3, 4, 5, 6 };
            ListNode list = createLinkedList(x);
            printLinkedList(list);    
        }
    
    

    打印出来的结果是:
    在这里插入图片描述

    直接上代码。

    // Definition for a binary tree node.
    public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
    
        TreeNode(int x) {
            val = x;
        }
    }
        
        public static TreeNode constructTree(Integer[] nums){
            if (nums.length == 0) return new TreeNode(0);
            Deque<TreeNode> nodeQueue = new LinkedList<>();
    		// 创建一个根节点
            TreeNode root = new TreeNode(nums[0]);
            nodeQueue.offer(root);
            TreeNode cur;
            // 记录当前行节点的数量(注意不一定是2的幂,而是上一行中非空节点的数量乘2)
            int lineNodeNum = 2;
            // 记录当前行中数字在数组中的开始位置
            int startIndex = 1;
            // 记录数组中剩余的元素的数量
            int restLength = nums.length - 1;
    
            while(restLength > 0) {
                // 只有最后一行可以不满,其余行必须是满的
    //            // 若输入的数组的数量是错误的,直接跳出程序
    //            if (restLength < lineNodeNum) {
    //                System.out.println("Wrong Input!");
    //                return new TreeNode(0);
    //            }
                for (int i = startIndex; i < startIndex + lineNodeNum; i = i + 2) {
                    // 说明已经将nums中的数字用完,此时应停止遍历,并可以直接返回root
                    if (i == nums.length) return root;
                    cur = nodeQueue.poll();
                    if (nums[i] != null) {
                        cur.left = new TreeNode(nums[i]);
                        nodeQueue.offer(cur.left);
                    }
                    // 同上,说明已经将nums中的数字用完,此时应停止遍历,并可以直接返回root
                    if (i + 1 == nums.length) return root;
                    if (nums[i + 1] != null) {
                        cur.right = new TreeNode(nums[i + 1]);
                        nodeQueue.offer(cur.right);
                    }
                }
                startIndex += lineNodeNum;
                restLength -= lineNodeNum;
                lineNodeNum = nodeQueue.size() * 2;
            }
    
            return root;
        }
        
        public static void preOrder(TreeNode root) {//前序排列
            if (root == null) return;
            System.out.print(root.val + " ");
            preOrder(root.left);
            preOrder(root.right);
        }
        
        public static void midOrder(TreeNode root) {//中序排列
            if (root == null) return;
            midOrder(root.left);
            System.out.print(root.val + " ");
            midOrder(root.right);
        }
        
        public static void aftOrder(TreeNode root) {//后序排列
            if (root == null) return;
            aftOrder(root.left);
            aftOrder(root.right);
            System.out.print(root.val + " ");
        }
    
        public static void main(String[] args) {
            Integer[] nums = {1,2,2,3,3,3,3};
            TreeNode tree=constructTree(nums);
            System.out.println("先序遍历:");
            preOrder(tree);
            System.out.println();
            System.out.println("中序遍历:");
            midOrder(tree);
            System.out.println();
            System.out.println("后序遍历:");
            aftOrder(tree);
            System.out.println();        
        }
    
    

    打印出来的结果是:
    在这里插入图片描述

    第二部分:

    这段时间时不时地会在leetcode上做些题,最近做到的大部分是与树相关的题。由于是在本地的IDE上码代码,如果每次测试都要到leetcode上来测的话,不仅要选中复制粘贴一遍,而且每次测试还要读一会条,一次两次还好,次数多了还是比较烦。而如果在本地的类内的main()函数里测试的话,每次构建一个树也比较麻烦,而且仅仅更换数值还好,若要更换树的结构,那工程量就有点大了。

    所以在这里准备写一个创建树的函数来解决这个问题,后面做起题来效率也能高一些。

    话不多说,下面先直接放上所用的代码:

    本例中所构建的树的结构
          5
         / \
        4   8
       /   / \
      11  13  4
     /  \      \
    7    2      1
    
    package learning_java;
    
    import LeetCode.TreeNode;
    import java.util.Deque;
    import java.util.LinkedList;
    
    public class ConstructTree {
        public static TreeNode constructTree(Integer[] nums){
            if (nums.length == 0) return new TreeNode(0);
            Deque<TreeNode> nodeQueue = new LinkedList<>();
            // 创建一个根节点
            TreeNode root = new TreeNode(nums[0]);
            nodeQueue.offer(root);
            TreeNode cur;
            // 记录当前行节点的数量(注意不一定是2的幂,而是上一行中非空节点的数量乘2)
            int lineNodeNum = 2;
            // 记录当前行中数字在数组中的开始位置
            int startIndex = 1;
            // 记录数组中剩余的元素的数量
            int restLength = nums.length - 1;
    
            while(restLength > 0) {
                // 只有最后一行可以不满,其余行必须是满的
    //            // 若输入的数组的数量是错误的,直接跳出程序
    //            if (restLength < lineNodeNum) {
    //                System.out.println("Wrong Input!");
    //                return new TreeNode(0);
    //            }
                for (int i = startIndex; i < startIndex + lineNodeNum; i = i + 2) {
                    // 说明已经将nums中的数字用完,此时应停止遍历,并可以直接返回root
                    if (i == nums.length) return root;
                    cur = nodeQueue.poll();
                    if (nums[i] != null) {
                        cur.left = new TreeNode(nums[i]);
                        nodeQueue.offer(cur.left);
                    }
                    // 同上,说明已经将nums中的数字用完,此时应停止遍历,并可以直接返回root
                    if (i + 1 == nums.length) return root;
                    if (nums[i + 1] != null) {
                        cur.right = new TreeNode(nums[i + 1]);
                        nodeQueue.offer(cur.right);
                    }
                }
                startIndex += lineNodeNum;
                restLength -= lineNodeNum;
                lineNodeNum = nodeQueue.size() * 2;
            }
    
            return root;
        }
    
        public static void main(String[] args) {
            Integer[] nums = {5,4,8,11,null,13,4,7,2,null,null,null,1};
            TreeNode root = ConstructTree.constructTree(nums);
            System.out.println(root);
        }
    }
    

    使用时,像上面main()方法中一样,只需要调用类内的静态方法constructTree(Integer[] nums),输入的参量为一个整型的数组,数组中的元素是按层次遍历的二叉树的值(若某节点在下一层中的某个儿子或两个儿子为空,则在下一层的这一个或两个位置填null),与Leetcode中树的表示方法相同,即可以直接把Leetcode上的测试用例按它的形式拖过来直接使用。

    简单解释一下所用的方法,核心思想是在每一层,用一个队列nodeQueue来存储该层的所有节点,然后用父节点的数量的两倍来遍历输入的数组(从上一层结束的地方开始),并从队列中取出(位于上一层的)对应的父节点(此时已从队列中删去,因为用的方法为poll()而不是peek()),对于每一个值,创建相应的子节点链接到父节点,并加入到队列中,依次不断循环,直到遍历完整个数组。

    这里一个踩过的坑是,其中因为一部分数值为null,而如果用int基本类型的数组的话,数组内是不能用null的,因此这里用了int的包装类Integer的数组来作为传入的参数的声明。

    最后,让我们测试一下我们的代码,测试用的树与上面一样,测试中我们采用先序遍历来进行输出,查看结果是否正确:

    /*
    
    用于测试的树,与上例中相同
          5
         / \
        4   8
       /   / \
      11  13  4
     /  \      \
    7    2      1
    
    */
    package learning_java.sortTry;
    
    import LeetCode.TreeNode;
    import learning_java.ConstructTree;
    
    public class ConstructTreeTest {
        public void preOrder(TreeNode root) {
            if (root == null) return;
            System.out.print(root.val + " ");
            preOrder(root.left);
            preOrder(root.right);
        }
    
        public static void main(String[] args) {
            Integer[] nums = {5,4,8,11,null,13,4,7,2,null,null,null,1};
            TreeNode root = ConstructTree.constructTree(nums);
            new ConstructTreeTest().preOrder(root);
        }
    }
    

    测试结果:

    5 4 11 7 2 8 13 4 1
    

    可以看到,我们得到了一棵所需要的树。

    更新

    在实际使用中发现,leetcode中所给的case经常并不是标准的个数,最后一行往往是不满的,如

          5
         / 
        4   
    

    这样一棵树,给出的数组中仅有两个数字:[5, 4],即最后一行中的末尾的null会被舍弃,因此,在我们的程序中,应在遍历过程中加入停止条件(更严谨的方式是保存判定错误输入的条件,并仅在树的最后一行的遍历过程中进行停止判定)。

    第三部分:

    平时无论是工作还是学习中,在写代码时,树总是一个非常常见的数据结构。在我们完成一棵树的构建之后,如果我们想要看这棵树的结构,不像数组或者List等数据结构,我们可以非常方便地用各种方式将其中的所有元素打印出来,对于树而言,这个过程要麻烦得多,我们可以用各种遍历方式得到这棵树的结构,但是终究还是不够直观。

    不知大家有没有想过,如果我们可以按照树的结构,将其打印出来就好了,那么本文就是一种实现这个目标的思路以供参考。

    引言

    树的结构

    在本文中所用的树的结构是leetcode上所用的树的结构,其定义如下:

    public class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode(int x) { val = x; }
    }
    

    如何方便地创建一个树的数据结构?

    在下面贴的这篇博客中中有详细的讲解,基于Leetcode中树的编码方式,由一个数组直接得到一棵树。

    如何创建一棵树


    如何打印一棵树

    在这里,我的总体思路是,用一个二维的字符串数组来储存每个位置应该打印什么样的输出。

    首先,先确定树的形状。为了美观,我设定在最后一行的每个数字之间的间隔为3个空格,而在之上的每一层的间隔,有兴趣的同学可以自己推算一下,总之,越往上,间隔是越大的,而且是一个简单的线性增加的关系。

    为了绘制出这样的形状,首先,我们需要获得树的层数(用一个简单的递归即可得到),根据树的层数,确定我们的二维数组的大小,即高度和宽度。之后,用先序遍历的方式,遍历树的每个节点,并进行相对应的写入操作。

    更详细的解释可以看代码中的注释,当然,也可以根据下面TreeOperationTest.java中的demo直接在自己的代码中调用这个方法来检查自己的树。

    话不多说,直接贴上所用的代码:

    // TreeOperation.java
    public class TreeOperation {
        /*
        树的结构示例:
                  1
                /   \
              2       3
             / \     / \
            4   5   6   7
        */
    
        // 用于获得树的层数
        public static int getTreeDepth(TreeNode root) {
            return root == null ? 0 : (1 + Math.max(getTreeDepth(root.left), getTreeDepth(root.right)));
        }
    
        private static void writeArray(TreeNode currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) {
            // 保证输入的树不为空
            if (currNode == null) return;
            // 先将当前节点保存到二维数组中
            res[rowIndex][columnIndex] = String.valueOf(currNode.val);
    
            // 计算当前位于树的第几层
            int currLevel = ((rowIndex + 1) / 2);
            // 若到了最后一层,则返回
            if (currLevel == treeDepth) return;
            // 计算当前行到下一行,每个元素之间的间隔(下一行的列索引与当前元素的列索引之间的间隔)
            int gap = treeDepth - currLevel - 1;
    
            // 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值
            if (currNode.left != null) {
                res[rowIndex + 1][columnIndex - gap] = "/";
                writeArray(currNode.left, rowIndex + 2, columnIndex - gap * 2, res, treeDepth);
            }
    
            // 对右儿子进行判断,若有右儿子,则记录相应的"\"与右儿子的值
            if (currNode.right != null) {
                res[rowIndex + 1][columnIndex + gap] = "\\";
                writeArray(currNode.right, rowIndex + 2, columnIndex + gap * 2, res, treeDepth);
            }
        }
    
        public static void show(TreeNode root) {
            if (root == null) System.out.println("EMPTY!");
            // 得到树的深度
            int treeDepth = getTreeDepth(root);
    
            // 最后一行的宽度为2的(n - 1)次方乘3,再加1
            // 作为整个二维数组的宽度
            int arrayHeight = treeDepth * 2 - 1;
            int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1;
            // 用一个字符串数组来存储每个位置应显示的元素
            String[][] res = new String[arrayHeight][arrayWidth];
            // 对数组进行初始化,默认为一个空格
            for (int i = 0; i < arrayHeight; i ++) {
                for (int j = 0; j < arrayWidth; j ++) {
                    res[i][j] = " ";
                }
            }
    
            // 从根节点开始,递归处理整个树
            // res[0][(arrayWidth + 1)/ 2] = (char)(root.val + '0');
            writeArray(root, 0, arrayWidth/ 2, res, treeDepth);
    
            // 此时,已经将所有需要显示的元素储存到了二维数组中,将其拼接并打印即可
            for (String[] line: res) {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < line.length; i ++) {
                    sb.append(line[i]);
                    if (line[i].length() > 1 && i <= line.length - 1) {
                        i += line[i].length() > 4 ? 2: line[i].length() - 1;
                    }
                }
                System.out.println(sb.toString());
            }
        }
    }
    
    

    接下来,是用于测试的程序,如果需要调用上面的方法,以下面的代码为demo即可。需要注意的一点是,其中构建树的代码为如何创建一棵树中的方法,需要将这些.java文件放在个包中(包括TreeNode.java),才可以正常使用。

    // // TreeOperationTest.java
    public class TreeOperatinTest {
        public static void main(String[] args) {
        	Integer nums[] = { 1, 2, 3, 4, 5 ,6, 7 };
        	// 根据给定的数组创建一棵树
            TreeNode root = ConstructTree.constructTree(nums);
            // 将刚刚创建的树打印出来
            TreeOperation.show(root);
        }
    }
    

    输出:

          1
        /   \
      2       3
     / \     / \
    4   5   6   7
    

    可以看到,这里我们用了两行代码,便创建出了一棵我们需要的树,并且按照树的格式将一棵完整的树打印了出来。

    当然,我们也可以换一棵树再来测试,我们就使用在这篇如何创建一棵树中的曾经使用过的例子再进行一次测试:

    public class TreeOperatinTest {
        public static void main(String[] args) {
        	Integer nums[] = { 5,4,8,11,null,13,4,7,2,null,null,null,1 };
            TreeNode root = ConstructTree.constructTree(nums);
            TreeOperation.show(root);
        }
    }
    

    输出:

                5
             /     \
          4           8
        /           /   \
      11          13      4
     / \                   \
    7   2                   1
    

    可以看到,即使树并不完整, 且其中有超过1位的数字,依然可以正确地输出。


    一点问题

    由于本方法的思路是基于字符串的数组的,所以并不可能完美适配所有情况,比如当树的高度很高以后,可能看起来会很奇怪(?)。

    还有一个问题就是,虽然已经做了自适应处理,但是,如果出现超过5位的数字(比如123123),其所在的行可能会有一点向右的偏移,若偏的不多,是不影响观察的,但若偏的多了就。。。。不过这里已经做了处理,所以出现三位或者四位数的时候是没有问题的。

    不过,在日常的应用中,应该是完全够用的,希望这段代码能为大家带来便利。

    参考:

    1. Leetcode链表和树在IDEA中测试用例
    2. 从数组形式创建一棵树(用于leetcode测试)
    3. 按照树形结构直观地打印出一棵二叉树(Java)
    4. 按照树形结构直观地打印出一棵二叉树(Java)
    展开全文
  • 构建一个maven项目的时候首先你需要你需要检查以下几个方面的信息是否配置好首先你需要有一个eclipse或者是myeclipse,然后将一个svn配置到你的环境中(配置的步骤很简单一般就是直接把下载的svn的文件直接放到环境...

    在构建一个maven项目的时候首先你需要你需要检查以下几个方面的信息是否配置好

    首先你需要有一个eclipse或者是myeclipse,然后将一个svn配置到你的环境中(配置的步骤很简单一般就是直接把下载的svn的文件直接放到环境当中,也就是eclipse中的目录下,网上有好多),之后你就需要将项目从svn上面检出到本地,检出来后你就需要将运行的环境搭建好,咱们使用的是maven进行管理(maven作用其实就是一个一个的jar包,以前我们比如需要使用到Struts2或者jdbc或者是jquery都需要将功能所对应的jar包导入进来这个maven就减少了这个功能,它相当于一个管理器,他管理着很多的jar包,如果你需要什么jar包,他就会将相应的jar包导入供用户使用),这个时候你知道maven的作用了,这个时候就需要配置maven,并且你需要对应的maven资源包。

       首先你需要知道的是你的项目需要的maven管理的文件有两个


    这两个都需要在实际的项目配置中配置好,首先说下这两个文件的功能,第一个Apache——maven-3.3.9这个文件的作用是管理器,他将整个eclipse环境和第二个Maven联系在一起,二Maven里面放的就是所有的jar包,这个时候你就需要通过Apache-maven-3.3.9里面的一个文件和下面的这个Maven关联,在Apache-maven-3.3.9中有一个文件setting.xml


    在这个文件中有一处就是用来配置这个管理器和Maven联系的。

    当你准备好了这两个工具,并且你也把setting.xml中的关联Maven路径配置好后就可以将其配置到eclipse的环境中。

             这个就是在eclipse中去配置这个maven环境,在配置前你需要检查的有几个东西,首先就是你需要检查这个jre的使用jdk是否是你需要用到的,这个一般是在你安装好jdk并且装好了eclipse后你会更改的。


    之后检查Maven选项中的Installations和和user Settings选项如下所示


    在这里配置也就这么多,这个时候就是在你运行前需要做得一些工作

    首先你需要右键项目—Debug as —run configurations

    首先是在这个Main选项框中你需要将项目的名称更改,然后将这个Base directory选择到你当前环境中的那个项目,Goals必须是tomcat:run,  User settings这个就需要的就是那个maveng管理器中带的那个配置文件,第一次运行的时候需要将Offline选上,之后就不用了,因为这个可以将你环境中缺少的jar包给关联进来。


    接着就是这个JRE选项框这个就需要你使用的是你在properties中设置jre的那个jdk

    在Source这个选项框中你就需要将你的项目的加进来让maven进行管理,这个时候如果显示的其他的项目,这个时候你就需要先将那个项目Remove掉,然后在add中选中从svn上检出的项目就好了。


    最后就是直接run,但是不知道为什么每次我在这里run的时候项目就会跑不起来这里有两个原因第一就是你在运行的时候回去检查数据库的连接地址这个时候你需要先配置好数据库的连接。

    但是有时候我配置好数据库的连接数据的时候也会出现项目跑不起来,这个时候有这么几个步骤需要做。

    第一:你可以右键项目—maven—Update Project(update project本身是更新项目的意思。
           用在mave中是指pom.xml文件改动之后,需要执行Mavne/Update Project来更新外部依赖的jar包。
      Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。
      Maven 除了以程序构建能力为特色之外,还提供高级项目管理工具。由于 Maven 的缺省构建规则有较高的可重用性,所以常常用两三行 Maven 构建脚本就可以构建简单的项目。由于 Maven 的面向项目的方法,许多 Apache Jakarta 项目发文时使用 Maven,而且公司项目采用 Maven 的比例在持续增长。
      Maven这个单词来自于意第绪语,意为知识的积累,最早在Jakata Turbine项目中它开始被用来试图简化构建过程。当时有很多项目,它们的Ant build文件仅有细微的差别,而JAR文件都由CVS来维护。于是Maven创始者开始了Maven这个项目,该项目的清晰定义包括,一种很方便的发布项目信息的方式,以及一种在多个项目中共享JAR的方式。

    第二步:你需要使用validate(validate  验证项目中的文件中代码有没有不规范的地方,有的话会显示红叉。可以校验代码中不规范的地方,消除红叉的作用)但是在validate前你需要将你的项目选择Project—build automatically(build automatically 是指你的java代码改了后,eclipse会自动把改过的java代码编译成class)

    但是有时候这个时候你也会出现validate不通过,有很多文件报错,但是其实这些文件是没有错误的这个时候你就需要设置validate检查的文件类型。这个将所有勾选去掉。

    右键—properties—Validation—Disable All这个时候你再去validate

    如果这个时候还是不行,那么就就有一个办法,就是在这个项目刚刚单击validate的时候你就点击停止validate,这个时候validate就不能检查出任何错误。就不会有哪个文件会报错,这样一来就可以运行了,记住运行的时候是



    展开全文
  • 数仓构建步骤

    千次阅读 2019-03-23 15:29:41
    数据仓库是面向主题的、集成的、不可更新的、随时间的变化而不断变化的,这些特点决定了数据仓库的系统设计不能采用同开发传统的OLTP数据库一样的设计方法。 数据仓库系统的原始需求不明确,且不断变化与增加,...

    数据仓库是面向主题的、集成的、不可更新的、随时间的变化而不断变化的,这些特点决定了数据仓库的系统设计不能采用同开发传统的OLTP数据库一样的设计方法。
    数据仓库系统的原始需求不明确,且不断变化与增加,开发者最初不能确切了解到用户的明确而详细的需求,用户所能提供的无非是需求的大的方向以及部分需求, 更不能较准确地预见到以后的需求。因此,采用原型法来进行数据仓库的开发是比较合适的,因为原型法的思想是从构建系统的简单的基本框架着手,不断丰富与完 善整个系统。但是,数据仓库的设计开发又不同于一般意义上的原型法,数据仓库的设计是数据驱动的。这是因为数据仓库是在现存数据库系统基础上进行开发,它 着眼于有效地抽取、综合、集成和挖掘已有数据库的数据资源,服务于企业高层领导管理决策分析的需要。但需要说明的是,数据仓库系统开发是一个经过不断循 环、反馈而使系统不断增长与完善的过程,这也是原型法区别于系统生命周期法的主要特点。因此,在数据仓库的开发的整个过程中,自始至终要求决策人员和开发 者的共同参与和密切协作,要求保持灵活的头脑,不做或尽量少做无效工作或重复工作。
    数据仓库的设计大体上可以分为以下几个步骤:

    1. 概念模型设计;
    2. 技术准备工作;
    3. 逻辑模型设计;
    4. 物理模型设计;
    5. 数据仓库生成;
    6. 数据仓库运行与维护。

    下面我们六个主要设计步骤为主线,介绍在各个设计步骤中设计的基本内容。

    第一节 概念模型设计
    进行概念模型设计所要完成的工作是:

    <1>界定系统边界

    <2>确定主要的主题域及其内容
    概念模型设计的成果是,在原有的数据库的基础上建立了一个较为稳固的概念模型。因为数据仓库是对原有数据库系统中的数据进行集成和重组而形成的数据集合, 所以数据仓库的概念模型设计,首先要对原有数据库系统加以分析理解,看在原有的数据库系统中“有什么”、“怎样组织的”和“如何分布的”等,然后再来考虑 应当如何建立数据仓库系统的概念模型。一方面,通过原有的数据库的设计文档以及在数据字典中的数据库关系模式,可以对企业现有的数据库中的内容有一个完整 而清晰的认识;另一方面,数据仓库的概念模型是面向企业全局建立的,它为集成来自各个面向应用的数据库的数据提供了统一的概念视图。

    概念模型的设计是在较高的抽象层次上的设计,因此建立概念模型时不用考虑具体技术条件的限制。

    1. 界定系统的边界

      数据仓库是面向决策分析的数据库,我们无法在数据仓库设计的最初就得到详细而明确的需求,但是一些基本的方向性的需求还是摆在了设计人员的面前:

    要做的决策类型有哪些?

    决策者感兴趣的是什么问题?

    这些问题需要什么样的信息?

    要得到这些信息需要包含原有数据库系统的哪些部分的数据?
    这样,我们可以划定一个当前的大致的系统边界,集中精力进行最需要的部分的开发。因而,从某种意义上讲,界定系统边界的工作也可以看作是数据仓库系统设计的需求分析,因为它将决策者的数据分析的需求用系统边界的定义形式反映出来。

    1. 确定主要的主题域

      在这一步中,要确定系统所包含的主题域,然后对每个主题域的内容进行较明确的描述,描述的内容包括:

    l 主题域的公共码键;

    l 主题域之间的联系;

    l 充分代表主题的属性组。

    第二节 技术准备工作
    这一阶段的工作包括:技术评估,技术环境准备。
    这一阶段的成果是:技术评估报告、软硬件配置方案、系统(软、硬件)总体设计方案。管理数据仓库的技术要求与管理操作型环境中的数据与处理的技术要求区别 很大,两者所考虑的方面也不同。我们之所以在一般情况下总是将分析型数据与操作型数据分离开来,将分析型数据单独集中存放,也就是用数据仓库来存放,技术 要求上的差异是一个重要原因。

    1. 技术评估

      进行技术评估,就是确定数据仓库的各项性能指标。一般情况下,需要在这一步里确定的性能指标包括:

    管理大数据量数据的能力;
    进行灵活数据存取的能力;
    根据数据模型重组数据的能力;
    透明的数据发送和接收能力;
    周期性成批装载数据的能力;
    可设定完成时间的作业管理能力。

    1. 技术环境准备

      一旦数据仓库的体系化结构的模型大体建好后,下一步的工作就是确定我们应该怎样来装配这个体系化结构模型,主要是确定对软硬件配置的要求;我们主要考虑相关的问题:

    预期在数据仓库上分析处理的数据量有多大?

    如何减少或减轻竞争性存取程序的冲突?

    数据仓库的数据量有多大?

    进出数据仓库的数据通信量有多大?等等。
    根据这些考虑,我们就可以确定各项软硬件的配备要求,并且在这一步工作结束时各项技术准备工作应已就绪,可以装载数据了。这些配备有:

    l 直接存取设备(DASD);

    l 网络;

    l 管理直接存取设备(DASD)的操作系统;

    l 进出数据仓库的界面(主要是数据查询和分析工具);

    管理数据仓库的软件,目前即选用数据库管理系统及有关的选件,购买的DBMS产品不能满足管理数据仓库需要的,还应考虑自己或软件集成商开发有关模块等等。

    第三节 逻辑模型设计
    在这一步里进行的工作主要有:

    • 分析主题域,确定当前要装载的主题;
    • 确定粒度层次划分;
    • 确定数据分割策略;
    • 关系模式定义;
    • 记录系统定义

    逻辑模型设计的成果是,对每个当前要装载的主题的逻辑实现进行定义,并将相关内容记录在数据仓库的元数据中,包括:

    • 适当的粒度划分;

    • 合理的数据分割策略;

    • 适当的表划分;

    • 定义合适的数据来源等。

    • 分析主题域

      在概念模型设计中,我们确定了几个基本的主题域,但是,数据仓库的设计方法是一个逐步求精的过程,在进行设计时,一般是一次一个主题或一次若干个主题地逐 步完成的。所以,我们必须对概念模型设计步骤中确定的几个基本主题域进行分析,并选择首先要实施的主题域。选择第一个主题域所要考虑的是它要足够大,以便 使得该主题域能建设成为一个可应用的系统;它还要足够小,以便于开发和较快地实施。如果所选择的主题域很大并且很复杂,我们甚至可以针对它的一个有意义的 子集来进行开发。在每一次的反馈过程中,都要进行主题域的分析。

    • 粒度层次划分

      数据仓库逻辑设计中要解决的一个重要问题是决定数据仓库的粒度划分层次,粒度层次划分适当与否直接影响到数据仓库中的数据量和所适合的查询类型。确定数据 仓库的粒度划分,可以使用在粒度划分一节中介绍的方法,通过估算数据行数和所需的DASD数,来确定是采用单一粒度还是多重粒度,以及粒度划分的层次。

    • 确定数据分割策略

      在这一步里,要选择适当的数据分割的标准,一般要考虑以下几方面因素:数据量(而非记录行数)、数据分析处理的实际情况、简单易行以及粒度划分策略等。数 据量的大小是决定是否进行数据分割和如何分割的主要因素;数据分析处理的要求是选择数据分割标准的一个主要依据,因为数据分割是跟数据分析处理的对象紧密 联系的;我们还要考虑到所选择的数据分割标准应是自然的、易于实施的:同时也要考虑数据分割的标准与粒度划分层次是适应的。

    • 关系模式定义

      数据仓库的每个主题都是由多个表来实现的,这些表之间依靠主题的公共码键联系在一起,形成一个完整的主题。在概念模型设计时,我们就确定了数据仓库的基本 主题,并对每个主题的公共码键、基本内容等做了描述在这一步里,我们将要对选定的当前实施的主题进行模式划分,形成多个表,并确定各个表的关系模式。

    第四节 物理模型设计
    这一步所做的工作是确定数据的存储结构,确定索引策略,确定数据存放位置,确定存储分配。

    确定数据仓库实现的物理模型,要求设计人员必须做到以下几方面:

    要全面了解所选用的数据库管理系统,特别是存储结构和存取方法。

    了解数据环境、数据的使用频度、使用方式、数据规模以及响应时间要求等,这些是对时间和空间效率进行平衡和优化的重要依据。

    了解外部存储设备的特性,如分块原则,块大小的规定,设备的I/O特性等。

    1. 确定数据的存储结构

      一个数据库管理系统往往都提供多种存储结构供设计人员选用,不同的存储结构有不同的实现方式,各有各的适用范围和优缺点,设计人员在选择合适的存储结构时应该权衡三个方面的主要因素:存取时间、存储空间利用率和维护代价。

    2. 确定索引策略

      数据仓库的数据量很大,因而需要对数据的存取路径进行仔细的设计和选择。由于数据仓库的数据都是不常更新的,因而可以设计多种多样的索引结构来提高数据存取效率。

    在数据仓库中,设计人员可以考虑对各个数据存储建立专用的、复杂的索引,以获得最高的存取效率,因为在数据仓库中的数据是不常更新的,也就是说每个数据存储是稳定的,因而虽然建立专用的、复杂的索引有一定的代价,但一旦建立就几乎不需维护索引的代价。

    1. 确定数据存放位置

      我们说过,同一个主题的数据并不要求存放在相同的介质上。在物理设计时,我们常常要按数据的重要程度、使用频率以及对响应时间的要求进行分类,并将不同类 的数据分别存储在不同的存储设备中。重要程度高、经常存取并对响应时间要求高的数据就存放在高速存储设备上,如硬盘;存取频率低或对存取响应时间要求低的 数据则可以放在低速存储设备上,如磁盘或磁带。

    数据存放位置的确定还要考虑到其它一些方法,如:决定是否进行合并表;是否对一些经常性的应用建立数据序列;对常用的、不常修改的表或属性是否冗余存储。如果采用了这些技术,就要记入元数据。

    1. 确定存储分配

      许多数据库管理系统提供了一些存储分配的参数供设计者进行物理优化处理,如:块的尺寸、缓冲区的大小和个数等等,它们都要在物理设计时确定。这同创建数据库系统时的考虑是一样的。

    第五节 数据仓库的生成
    在这一步里所要做的工作是接口编程,数据装入。

    这一步工作的成果是,数据已经装入到数据仓库中,可以在其上建立数据仓库的应用,即DSS应用。

    1. 设计接口

      将操作型环境下的数据装载进入数据仓库环境,需要在两个不同环境的记录系统之间建立一个接口。乍一看,建立和设计这个接口,似乎只要编制一个抽取程序就可 以了,事实上,在这一阶段的工作中,的确对数据进行了抽取,但抽取并不是全部的工作,这一接口还应具有以下的功能:

    l 从面向应用和操作的环境生成完整的数据;

    l 数据的基于时间的转换;

    l 数据的凝聚;

    l 对现有记录系统的有效扫描,以便以后进行追加。

    当然,考虑这些因素的同时,还要考虑到物理设计的一些因素和技术条件限制,根据这些内容,严格地制定规格说明,然后根据规格说明,进行接口编程。从操作型 环境到数据仓库环境的数据接口编程的过程和一般的编程过程并无区别,它也包括伪码开发、编码、编译、检错、测试等步骤。

    在接口编程中,要注意:

    l 保持高效性,这也是一般的编程所要求的;

    l 要保存完整的文档记录;

    l 要灵活,易于改动;

    l 要能完整、准确地完成从操作型环境到数据仓库环境的数据抽取、转换与集成。

    1. 数据装入

    在这一步里所进行的就是运行接口程序,将数据装入到数据仓库中。主要的工作是:

    l 确定数据装入的次序;

    l 清除无效或错误数据;

    l 数据“老化” ;

    l 数据粒度管理;

    l 数据刷新等。

    最初只使用一部分数据来生成第一个主题域,使得设计人员能够轻易且迅速地对已做工作进行调整,而且能够尽早地提交到下一步骤,即数据仓库的使用和维护。这 样既可以在经济上最快地得到回报,又能够通过最终用户的使用、尽早发现一些问题并提出新的需求,然后反馈给设计人员,设计人员继续对系统改进、扩展。

    第六节 数据仓库的使用和维护
    在这一步中所要做的工作有建立DSS应用,即使用数据仓库理解需求,调整和完善系统,维护数据仓库。

    建立企业的体系化环境,不仅包括建立起操作型和分析型的数据环境,还应包括在这一数据环境中建立起企业的各种应用。数据仓库装入数据之后,下一步工作是: 一方面,使用数据仓库中的数据服务于决策分析的目的,也就是在数据仓库中建立起DSS应用;另一方面,根据用户使用情况和反馈来的新的需求,开发人员进一 步完善系统,并管理数据仓库的一些日常活动,如刷新数据仓库的当前详细数据、将过时的数据转化成历史数据、清除不再使用的数据、调整粒度级别等。我们把这 一步骤称为数据仓库的使用与维护。

    1. 建立DSS应用

      使用数据仓库,即开发DSS应用,与在操作型环境中的应用开发有着本质区别,开发DSS应用不同于联机事务处理应用开发的显著特点在于:

    l DSS应用开发是从数据出发的;

    l DSS应用的需求不能在开发初期明确了解;

    l DSS应用开发是一个不断循环的过程,是启发式的开发。

    DSS应用主要可分为两类:例行分析处理和启发式分析处理。例行分析处理是指那些重复进行的分析处理,它通常是属于部门级的应用,如部门统计分析,报表分 析等等;而个人级的分析应用经常是随机性很大的,企业经营者受到某种信息启发而进行的一些即席的分析处理,所以我们称之为启发式的分析处理。

    DSS应用开发的大致步骤如下:

    步骤l——确定所需的数据。为满足DSS应用的要求,我们必须从数据仓库中确定一个可能用到的数据范围。这是一个试探的过程。
    步骤2——编程抽取数据。根据上面得到的数据范围,编写一个抽取程序来获得这些数据。为适应分析需求多变的特点,要求所编写的抽取程序应该通用,易于修改。
    步骤3——合并数据。如果有多个数据抽取源,要将抽取来的数据进行合并、提炼,使数据符合分析处理的要求。
    步骤4——分析数据。在上步准备好的数据基础上进行分析处理,并看所得的结果是否满足了原始的要求,如果不能满足,则返回步骤1,开始新的一次循环,否则就准备最终分析结果报告。
    步骤5——回答问题。生成最终分析结果报告。—般情况下,最终的分析结果报告是在许多次的循环后得到的,因为一次分析处理很少是在一次循环后就完成的。
    步骤6——例行化、一次分析处理的最后、我们要决定是否将在上面已经建立的分析处理例行化。如果建立的分析处理是重复进行的部门级的DSS应用,那么最好 是将它例行化,这样在进行下一次同样的分析处理时,不必再重复上述六步的循环过程。而且,不断地积累这种例行处理,形成一个集合,我们就可以通过组合这些 已有的处理来生成新的一个较大的复杂处理,或完成一个复杂处理的一部分。

    1. 理解需求,改善和完善系统,维护数据仓库

      数据仓库的开发是逐步完善的原型法的开发方法,它要求:要尽快地让系统运行起来,尽早产生效益;要在系统运行或使用中,不断地理解需求,改善系统;不断地考虑新的需求,完善系统。

    维护数据仓库的工作主要是管理日常数据装入的工作,包括刷新数据仓库的当前详细数据,将过时的数据转化成历史数据.清除不再使用的数据,管理元数据,等等;另外,如何利用接口定期从操作型环境向数据仓库追加数据,确定数据仓库的数据刷新频率,等等。

    展开全文
  • 最小堆 构建、插入、删除的过程图解

    万次阅读 多人点赞 2016-05-21 00:47:02
    最小堆的构建、插入、删除的过程。搞懂最小堆的相应知识后,最大堆与此类似。 2.最小堆示例 3.最小堆的构建  初始数组为:9,3,7,6,5,1,10,2  按照完全二叉树,将数字依次填入。  填入后,找到最后一个...

    1.简介

           最小堆是一棵完全二叉树,非叶子结点的值不大于左孩子和右孩子的值。本文以图解的方式,说明

    最小堆的构建、插入、删除的过程。搞懂最小堆的相应知识后,最大堆与此类似。

    2.最小堆示例


    3.最小堆的构建

          初始数组为:9,3,7,6,5,1,10,2

          按照完全二叉树,将数字依次填入。

          填入后,找到最后一个结点(本示例为数字2的节点),从它的父节点(本示例为数字6的节点)

    开始调整。根据性质,小的数字往上移动;至此,第1次调整完成。

          注意,被调整的节点,还有子节点的情况,需要递归进行调整。

          第二次调整,是数字6的节点数组下标小1的节点(比数字6的下标小1的节点是数字7的节点),

    用刚才的规则进行调整。以此类推,直到调整到根节点。

          以下是本示例的图解:





    注意:数字9的节点 将和 数字1的节点 发生对调,对调后,需要递归进行调整,请一定注意。





    4.最小堆的元素插入

           以上个最小堆为例,插入数字0。

           数字0的节点首先加入到该二叉树最后的一个节点,依据最小堆的定义,自底向上,递归调整。

           以下是插入操作的图解:

     




    5.最小堆的节点删除

           对于最小堆和最大堆而言,删除是针对于根节点而言。

           对于删除操作,将二叉树的最后一个节点替换到根节点,然后自顶向下,递归调整。

           以下是图解:







            注意:
            如您发现本文档中有明显错误的地方,
            或者您发现本文档中引用了他人的资料而未进行说明时,请联系我进行更正。
            转载或使用本文档时,请作醒目说明。
            必要时请联系作者,否则将追究相应的法律责任。

            note:
            If you find this document with any error ,
            Or if you find any illegal citations , please contact me correct.
            Reprint or use of this document,Please explain for striking. 
            Please contact the author if necessary, or they will pursue the corresponding legal responsibility.


    展开全文
  • 数仓构建流程

    2020-10-20 09:55:13
    维度模型主张从分析决策的需求出发构建模型,为分析需求服务。维度是度量的环境,是我们观察业务的角度,用来反映业务的一类属性 。属性的集合构成维度 ,也可以称为实体对象。例如, 在分析交易过程时,可以通过...
  • 软件构建(转)

    千次阅读 2018-11-02 09:34:14
    1、什么是构建 你一定知道“构建(construction)”一次在软件开发领域以外的含义。“构建”就是“建筑工人(construction workers)”在建设一栋房屋,一所学校,乃至一座摩天大楼时所做的工作。在你年轻时,可能...
  • Jenkins之定时构建

    万次阅读 2017-08-24 09:49:11
    Jenkins之定时构建
  • Qt构建、运行、qmake的区别

    万次阅读 2017-12-16 16:06:00
    构建构建是增量编译,只编译有变化部分 重新构建: 是把所有部分都重新编译 运行: 有改动则根据已有的Makefile进行编译,执行 构建和重新构建时如果没有Makefile,会根据.pro文件等生成Makefile后再编译
  • 构建工程时提示“:-1: 警告:构建目录必须和源文件目录为同级目录.” 解决方法如下: 删掉 xxx.pro.user文件 重新打开.pro文件 自动弹出重新进行目标设置 设置完成后构建成功。 注意: 工程...
  • Jenkins 构建完成后,在构建页面显示构建产生的文件,方便在需要时直接下载。这是分发最新应用程序的一个简便方式。 1.配置构建后操作 进入 Jenkins job 进入 job 配置 前往 “构建后操作” 点击 “增加构建...
  • iOS:删除itunsconnect的构建版本

    万次阅读 2015-05-27 17:18:06
    但是这是,在构建一个新版本在验证过程中,会提示说已存在一个构建版本,把build改了,version不用改就可以,重新打包上传吧。 要注意的是你的APP里如果有用到APP版本号,要用CFBundleShortVersionString字段。官方...
  • 每一个项目配置都会有构建构建可以是windows命令行,shell脚本等等... jenkins是如何判断本次构建是失败还是成功了呢(源码这个地方肯定是做了一些类似于if esle或者 返回值的判断的),这部分判断的源码是怎样的...
  • 在Myeclipse中将一个Android程序代码引入到项目中时,在构建项目过程中提示的错误: 1 由于项目的构建路径不完整,所以未构建该项目。找不到 java.lang.Object 的类文件。修正构建路径,然后尝试构建此项目 2 ...
  •  Qt报错: -1: 错误:Qt Creator 需要设置一个编译器来构建。在构建套件选项里设置一个编译器。 vs2010报错:error MSB6006: “cmd.exe”已退出,代码为 -1073741515。 有木有??  哈哈,关键是解决办法,,再...
  • Jenkins学习(3)——Jenkins构建产物归档

    万次阅读 2018-08-31 14:35:22
    Jenkins的构建产物就是指Jenkins在每一次任务构建成功后所产生的war包,或者jar包。通常这个构建产物会保存在当前构建任务的工作空间下的target目录下。Jenkins可以存储构建生成的二进制构建产物副本。并允许你从...
  • 前几天想上线一款App,但是发现构建版本一直处于正在处理的界面,很是费解,记得当初上线上传二进制文件之后,十几分钟就可以选择构建版本了,这次却迟迟不行,所以上网查阅了相关资料. App上传二进制的流程可以分为两种,...
  • 关于Codeblocks无法构建的解决办法

    万次阅读 2018-03-10 16:46:32
    关于Codeblocks无法构建的解决办法很多小伙伴在使用codeblocks时,都会遇到无法构建的问题,下面从几个方面来解决这个问题。原因一:未终止上次运行的程序。在上次运行的程序没有关闭时,是不能构建下一个程序的,...
  • 前面一篇,我们大致走完了一个Project的创建过程和简单介绍了周期性构建的例子。这篇我们继续来介绍关于构建触发器的其他构建,先来看看远程构建。 1.直接上图,什么是远程构建  我们这里勾选第一项,什么是...
  • Jenkins自动化构建普通Android项目配置

    万次阅读 2018-11-12 15:49:07
    1,新建任务,构建自由风格的软件项目 2,配置信息 描述信息随意填写 3,选择参数化构建过程,添加一个选项参数BUILD_TYPE,用来区分编译环境 4,源码管理中,添加GIT仓库地址,并选择构建分支 5,构建...
  • Jenkins构建触发器(定时构建项目)

    万次阅读 2016-10-25 11:29:07
    Poll SCM和Build periodically来进行定时自动构建项目; 在“配置”——》“构建触发器”中,如下图所示: Build after other projects are built:在其他项目触发的时候触发,里面有分为三种情况...
1 2 3 4 5 ... 20
收藏数 1,899,760
精华内容 759,904
关键字:

构建