zoukankan      html  css  js  c++  java
  • 考试复习整理

    最近数据结构课要考试了,这学期基本没去过数据结构课堂,咳咳,拿出书和课件来翻翻,不少内容都比CLRS繁琐而不清晰。
    简单整理了一些要点,发现这些东西或许还是有些分享价值的。。。虽说大多非常水。

    稀疏矩阵

    • 非零元素/(m * n) < 0.05 可认为是稀疏的,当然实际上是一个经验判断,这个数值仅供参考而已

    • 采用三元组进行存储
      整体是一个数组,每个元素是一个struct,成员包括value、行、列

    • 转置:
      一般转置算法:

      原矩阵为A,转置后为B:

      1. 初始化B,B.rows=A.cols;B.cols=A.rows;

      j=0 //用来扫描B的index
      for(k : 0 to A.cols) //按照原矩阵的列扫描
      for(i : 0 to terms) //terms是矩阵中非零元素的个数
      if(A.matrix[i].col == k)
      B.matrix[j].row=A.matrix[i].col
      B.matrix[j].col=A.matrix[i].row
      B.matrix[j].value=A.matrix[i].value
      j++

      1. 快速转置(类似于桶排序)
        前一个转置是很慢的,时间复杂度高达O(cols*terms)
        我们可以先扫描A,确定A中每一列有几个元素,
        如: col 0 1 2 3 4 5
        元素个数 1 1 1 3 0 2

        则此时可以计算出一个数组help,第i个元素代表A中第i列的元素放在B的help[i]位置上
        help 0 1 2 3 6 6

        for(i : 0 to terms)
        pos=help[ A.matrix[i].col ]
        B.matrix[pos].col = A.matrix[i].row
        B.matrix[pos].row = A.matrix[i].col
        B.matrix[pos].value = A.matrix[i].value
        help[A.matrix[i].col]++
        //这里让help数组中对应元素自增;这样遇到原A中和此元素同一列的元素时可以自动放在它的下一个位置

    广义表

    • 这个数据结构在C++ 中用的不多,但是在scheme中已经见识到了这样一个 闭包结构的威力,看过SICP的孩子们看到广义表肯定倍感亲切呀。
      比如随手写一个: (cons a (cons b (cons c nil))) 就是一个链表, (cons (list a b ) (list c d)) 就是一个二叉树

    • 深度:括号的嵌套层次

    • 长度:最外层的子表个数

    • example: depth length

      • A () 1 0
      • B (a b) 1 2
      • C (a b (d e f) ) 2 3
      • D (A B C) 3 3
      • E (B D) 4 2
      • F (h F) 乄 2
        *也就是说,一个广义表的深度=它的所有子表里面深度最大的一个的深度+1而广义表的长度则不管子表的内容,子表和原子作为同样看待,看有几个

    利用栈模拟实现二叉树的非递归遍历

    这是一个非常经典的问题,借考试之故做个整理也是极好的

    前序遍历

    前序的改写最为容易,因为前序的右子树遍历是尾递归,而左子树遍历也接近尾递归,可以轻易的写为迭代而不借助栈
    比如:
    void preorder(treenode *node){
        while(node!=NULL){
            if(node->left!=NULL){
             node=node->left;
             break;
         }
            node=node->right;
    }
    这样的代码对于我们理解栈模拟没有任何用处,它只是用到了尾递归优化技巧,并且对于我们写出中序和后序也没有帮助
    思考下面的方法:
    void stack_preorder(treenode *node){
        stack  s;
        while(true){
             while(node!=NULL){
                visit(node);
                s.push(node->right);
                node=node->left;
            }
            if(s is empty) break;
            node=s.pop();
        }
    }
    当然,这里的两侧while可以写成一层:
    void stack_preorder(treenode *node){
        stack  s;
        while(true){
             if(node!=NULL){
                visit(node);
                s.push(node->right);
                node=node->left;
            }
            else if(s is empty) break;
            else node=s.pop();
        }
    }
    

    中序遍历

    前序的后一种方法很容易让我们写出利用栈模拟的中序遍历算法:
    void stack_inorder(treenode *node){
        stack s;
        while(true){
            if(node != NULL){
                s.push(node);
                node=node->left;
            }
            else if(s is empty)
                break;
            else {
                treenode *p=s.pop();
                visit(p);
                 node=node->right;
             }
        }
    }
    到这里我们需要思考下一个问题,栈模拟是在帮助我们记住返回的路径,有没有办法不用栈呢?
    (之前有篇模版二叉树的博文里面的operator<< 实际上就是一个非递归不用栈的二叉树遍历)
    void inorder(treenode *node){
        
        while(true){
            if(node->left!=NULL){
                node=node->left;
            }
            else {
                visit(p);
               while(node->right==NULL)
                {
                    node=node->succ();
                    if(node==NULL)
                       break;
                    else visit(node);
                   
               }
               node=node->right;
            }
            
        }
    }
    
     解释一下,首先循环开始:
     从当前开始不断的向左,直至最左结点;
     访问最左结点;
     最左的结点没有右孩子:
         访问它中序下的后继;
         循环这一过程
     访问它的右孩子(此时将右孩子作为和根结点一样的地位,进行遍历) (实际上中序的右子树遍历是直接尾递归优化来的,很容易理解)
    

    后序遍历

    后序遍历就稍微麻烦一些,因为它的左子树和右子树遍历均不是尾递归。
    这时候我们要解决一个问题,从栈中退出的时候我们接下来要访问什么?
    我们无法确定是原递归的左子树调用还是右子树调用,因此需要添加一个标记。
    标记取为enum 的两个常量:from_left_to_right  from_right_to_root分别对应当前结点是从左子树遍历中退出应该去右子树、以及遍历完右子树应该访问它自身两种情况
    
    void stack_postorder(treenode *node){
        stack s;
        s.push(node);
        while(s is not empty){
            while(node->left != NULL){
                node=node->left;
                s.push(node);
            }
           
            while(true){
                node=s.pop();
                if(node.tag== from_left_to_rigth){
                    s.push(node);
                    node.tag=frome_right_to_root;
                    node=node->right;
                    break;
                }
                else if(node.tag==frome_right_to_root){
                    visit(node);
                }
            }
        }
    }
    

    二叉树计数和退栈可能数

    • n个元素能够组成多少个不同的二叉树?
    • 给定n个元素的进栈顺序,有多少种合法的出栈顺序?

    上面两个问题实际上是等价的,先来看第二个问题:

    • 首先我们要明确什么样的出栈顺序不合法:

    如入栈顺序为i,j,k,出栈顺序为pk < pi < pj,则不合法,
    比如 123 入栈,则312不合法

    • 考虑n个元素 1 2 3 ……n 进栈,合法的出栈顺序数满足下列递推:

    f(n)= f(n-1) * f(0) + f(n-2) * f(1) + f(n-3) * f(2) + …… + f(0)f(n-1)
    解释一下:
    左边:f(n)表示1 2 3 …… n的入栈顺序下的出栈可能的数目
    右边: 我们按照最后一个出栈元素的不同来分类:
    1. 最后一个出栈的是n ,则此时出栈的种类数=f(n-1)
    2. 最后一个出栈的是n-1,则此时出栈的种类数等于f(n-1)
    f(1)
    3. 依次类推,直至f(0)*f(n-1)

    上述的f(n)实际上就是catalan数,其通项公式为C(n,2n)/(n+1)

    而二叉树的种类数也是上面的catalan数

  • 相关阅读:
    python中的quopri模块
    使用ant来压缩js代码,这个很有用
    js 压缩工具 google closure compiler
    web中,canvas render 跟 webgl render 的区别
    越南unicode范围
    复杂度分析 数据结构
    svn bat批处理
    游戏 有限状态机参考:
    python list排序
    游戏 AOI相关
  • 原文地址:https://www.cnblogs.com/gaoduan/p/4071739.html
Copyright © 2011-2022 走看看