考试复习整理
2014-11-03 17:30
190 查看
最近数据结构课要考试了,这学期基本没去过数据结构课堂,咳咳,拿出书和课件来翻翻,不少内容都比CLRS繁琐而不清晰。
简单整理了一些要点,发现这些东西或许还是有些分享价值的。。。虽说大多非常水。
采用三元组进行存储
整体是一个数组,每个元素是一个struct,成员包括value、行、列
转置:
一般转置算法:
原矩阵为A,转置后为B:
初始化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
快速转置(类似于桶排序)
前一个转置是很慢的,时间复杂度高达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中和此元素同一列的元素时可以自动放在它的下一个位置
比如随手写一个: (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而广义表的长度则不管子表的内容,子表和原子作为同样看待,看有几个
这是一个非常经典的问题,借考试之故做个整理也是极好的
给定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数
简单整理了一些要点,发现这些东西或许还是有些分享价值的。。。虽说大多非常水。
稀疏矩阵
非零元素/(m * n) < 0.05 可认为是稀疏的,当然实际上是一个经验判断,这个数值仅供参考而已采用三元组进行存储
整体是一个数组,每个元素是一个struct,成员包括value、行、列
转置:
一般转置算法:
原矩阵为A,转置后为B:
初始化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++
快速转置(类似于桶排序)
前一个转置是很慢的,时间复杂度高达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数
相关文章推荐
- java 考试复习整理——JAVA类和类成员的修饰符
- 【复习+整理】【题解】noip集训考试1
- 2010年嵌入式系统考试复习整理
- C++考试复习整理
- oracle考试复习题目整理
- 2018版OCP 11g 052最新考试题库整理(带答案)(13)
- 《计算机组成原理》复习整理
- SCJP认证考试复习笔记系列之二
- 11.4考试整理(11.5写)T1数论线性筛
- 10-12考试整理
- 2009年软考真题与解析--自己测考顺便发给需要的人(自己从考试资源网那手动整理)
- 线程知识复习整理
- 第一章复习整理
- 网络工程师考试复习资料
- C语言考试2 题目整理
- JSP复习整理(一)表单
- Asp.net控件开发学习笔记整理篇 - 复习大纲