数据结构与算法:
1.矩阵:几种特殊矩阵以及矩阵的一维数组结构,这些如果考的很深入,需要熟练掌握一个思维:从二维坐标向一维坐标的映射。F从二维到一维(坐标)=行主序次序下的排行。
稀疏矩阵,包括链式结构与数组结构。答题思路类似。几个操作函数简单总结:
转置:在原矩阵中,统计出新矩阵各个行的区间长度,利用长度,确定新矩阵的一位数组中各个行的一维坐标区间分布Distr(确定了起点)。这样,原矩阵与转置矩阵的每一行的第一个元素的一维坐标就完成了映射。接下来,只需要遍历原来的矩阵,将原矩阵的各个元素的行列互换,然后通过行映射到Distr相应的起点,向该位置填充后自动把该起点位移方便下一个进行填充。以此类推完成转置。
加法:中心思想:以 F从二维到一维(坐标)=行主序次序下的排行 计算结果为距离S,在两个矩阵间进行追击——如果Sa=Sb则运算,则push_back(),否则落后的一个追击并直接入栈。最后还有劲的再跑一段把剩下的入栈。
乘法:其中一种成功AC的方法:首先将右矩阵转置,转置之后。两个矩阵的元素都会变成行主序的排列方式,首先可以利用转置中确定Distr的方法,确定各个行的分布区间。注意转置之后单个元素的计算就变成行向量之间的点乘了,而不是行列向量之间的点乘。因此有了两个矩阵中一维数组的行分布后,就可以利用加法中的追击法,对每组对应行的元素进行点乘了。代码如下(可展开,共240行):
#include<stdio.h> #include<iostream> #include<vector> using namespace std; template<class T> class matrixTerm{ public: matrixTerm(){} matrixTerm(int rw,int cl,T val):row(rw),col(cl),value(val){} int row,col; T value; bool operator==(const matrixTerm<T> &x){ return value == x.value; } T operator+(matrixTerm &x){ return value + x.value; } T operator*(matrixTerm &x){ return value * x.value; } void set(int r,int c,T v){ row=r;col=c;value=v; } }; template<class T> class sparseMatrix{ public: sparseMatrix(){} sparseMatrix(int r,int c){ rows=r;cols=c; }; sparseMatrix(sparseMatrix<T> &x){ rows=x.rows;cols=x.cols; terms=x.terms; } void copy(sparseMatrix<T> &x){ rows=x.rows;cols=x.cols; terms=x.terms; } void inputS(); void input(); void output(); void add(sparseMatrix<T> &x); void transpose(); void multiply(sparseMatrix<T> &x); private: int rows,cols; vector<matrixTerm<T> > terms; }; template<class T> void sparseMatrix<T>::inputS(){ terms.clear(); cin>>rows>>cols;T read;matrixTerm<T> term; for(int i=1;i<=rows;i++){ for(int j=1;j<=cols;j++){ cin>>read; if(read!=0){ term.row=i;term.col=j;term.value=read; terms.push_back(term); } } } } template<class T> void sparseMatrix<T>::input(){ terms.clear();int nonZero; cin>>rows>>cols>>nonZero;T read;matrixTerm<T> term; for(int i=0;i<nonZero;i++){ cin>>term.row>>term.col>>read;term.value=read; terms.push_back(term); } } template<class T> void sparseMatrix<T>::output(){ int step=0; cout<<rows<<" "<<cols<<endl; for(int i=1;i<=rows;i++){ for(int j=1;j<=cols;j++){ if(step<terms.size()&&terms[step].row==i&&terms[step].col==j){ cout<<terms[step].value<<((j==cols)?" ":" ");step++; }else cout<<"0"<<((j==cols)?" ":" "); } } } template<class T> void sparseMatrix<T>::add(sparseMatrix<T> &x){ if(rows!=x.rows||cols!=x.cols){ copy(x); cout<<-1<<endl;return; } typename vector<matrixTerm<T> >::iterator it=terms.begin(); typename vector<matrixTerm<T> >::iterator itx=x.terms.begin(); typename vector<matrixTerm<T> >::iterator itend=terms.end(); typename vector<matrixTerm<T> >::iterator itendx=x.terms.end(); typename std::vector<matrixTerm<T> > temp; matrixTerm<T> term; int idx1,idx2; while(it!=itend&&itx!=itendx){ idx1=(*it).row*cols+(*it).col;idx2=(*itx).row*cols+(*itx).col; if(idx1>idx2){ term.set((*itx).row,(*itx).col,(*itx).value); temp.push_back(term); itx++; }else if(idx1==idx2){ if((*it)+(*itx)!=0){ term.set((*itx).row,(*itx).col,(*itx)+(*it)); temp.push_back(term); } it++;itx++; }else{ term.set((*it).row,(*it).col,(*it).value); temp.push_back(term); it++; } } while(it!=itend){ term.set((*it).row,(*it).col,(*it).value); temp.push_back(term); it++; } while(itx!=itendx){ term.set((*itx).row,(*itx).col,(*itx).value); temp.push_back(term); itx++; } terms=temp; } template<class T> void sparseMatrix<T>::transpose(){ if(terms.size()==0)return; int rowsLen[cols+1]={0}; int rowsS[cols+1]={0}; int idx; typename std::vector<matrixTerm<T> > temp(terms.size()); matrixTerm<T> term; for(int i=0;i<terms.size();i++){ rowsLen[terms[i].col]++; } for(int i=1;i<=cols;i++){ rowsS[i]=rowsS[i-1]+rowsLen[i-1]; } for(int i=0;i<terms.size();i++){ term.set(terms[i].col,terms[i].row,terms[i].value); idx=rowsS[terms[i].col]++; temp[idx]=term; } terms=temp; int t=rows; rows=cols; cols=t; } template<class T> void sparseMatrix<T>::multiply(sparseMatrix<T> &x){ if(cols!=x.rows){ copy(x); cout<<-1<<endl;return; } x.transpose(); typename std::vector<matrixTerm<T> > temp;matrixTerm<T> term; int sectorLen[rows+1]={0},sectorLenx[rows+1]={0}; int partition[rows+2]={0},partitionx[rows+2]={0}; vector<int> nonZeroRows,nonZeroRowsx; for(int i=0;i<terms.size();i++){ sectorLen[terms[i].row]++; if(nonZeroRows.size()==0||nonZeroRows.back()!=terms[i].row)nonZeroRows.push_back(terms[i].row); } for(int i=0;i<x.terms.size();i++){ sectorLenx[x.terms[i].row]++; if(nonZeroRowsx.size()==0||nonZeroRowsx.back()!=x.terms[i].row)nonZeroRowsx.push_back(x.terms[i].row); } for(int i=1;i<=rows+1;i++){ partition[i]=partition[i-1]+sectorLen[i-1]; //Sign the i's element as the start of the i's sector } //and i+1's elememt as the one where the i's sector end before (if not consiting) for(int i=1;i<=x.rows+1;i++){ partitionx[i]=partitionx[i-1]+sectorLenx[i-1]; } vector<int>::iterator it=nonZeroRows.begin(); vector<int>::iterator itx=nonZeroRowsx.begin(); vector<int>::iterator itend=nonZeroRows.end(); vector<int>::iterator itendx=nonZeroRowsx.end(); T sum;int r,c; for(;it!=itend;it++){ itx=nonZeroRowsx.begin(); for(;itx!=itendx;itx++){ sum=0; int pt1=partition[*it];int pt2=partitionx[*itx]; while(pt1<partition[*it+1]&&pt2<partitionx[*itx+1]){ if(terms[pt1].col<x.terms[pt2].col){ pt1++; }else if(x.terms[pt2].col<terms[pt1].col){ pt2++; }else if(terms[pt1].col==x.terms[pt2].col){ sum+=terms[pt1]*x.terms[pt2];pt1++;pt2++; } // cout<<"pt1="<<pt1<<";pt2="<<pt2<<endl; } if(sum!=0){ term.set(*it,*itx,sum); temp.push_back(term); } } } terms=temp; cols=x.rows; } int main(){ sparseMatrix<int> x,y,z; int n; int ord; cin>>n; while(n--){ cin>>ord; switch(ord){ case 1: x.inputS(); break; case 2: y.input(); x.multiply(y); break; case 3: y.input(); x.add(y); break; case 4: x.output(); break; case 5: x.transpose(); break; } } }
2.栈:表达式的运算,由于运算都是二元的,表达式天然地对后缀表达式更亲和,但是,中缀表达式也是有办法做的。策略为:运算符不着急使用而是等待与后一个运算符进行优先级比较,到达后一个运算符时,显然两个操作数都已经入栈,如果后一个优先级低则弹出操作数与前一个操作符,进行运算。之后后一个操作符入栈。对于左括号,直接入操作符栈,每次碰到一个右括号就对左括号前的操作符全部进行弹出和运算。最后,对全部剩余的操作符进行弹出与运算。以下为表达式计算的代码(可展开共75行):
#include<stack> #include<iostream> #include<string.h> #include<stdio.h> #include<math.h> #include<iomanip> using namespace std; class algorith{ public: int level(char op){ switch(op){ case '+':case '-':return 1;break; case '*':case '/':return 2;break; } } int isn(char ch){ if(ch>='0'&&ch<='9')return 1; return 0; } void opr(char ch){ double a;a=num.top();num.pop(); switch(ch){ case '+':num.top()+=a;break; case '-':num.top()-=a;break; case '*':num.top()*=a;break; case '/':num.top()/=a;break; } operators.pop(); } void midfix(char ch){ double a; switch(ch){ case '+':case '-':case '*':case '/': while(!operators.empty()&&level(operators.top())>=level(ch)&&operators.top()!='('){ opr(operators.top()); } operators.push(ch); break; case ')': while(operators.top()!='('){ opr(operators.top()); } operators.pop(); break; case '(': operators.push('('); break; default: num.push(ch-'0');break; } } void go(){ int n; cin>>n; while(n--){ string exp; cin>>exp; int q=exp.length(); for(int i=0;i<q;i++){ midfix(exp[i]); } while(!operators.empty()){ opr(operators.top()); } cout<<fixed<<setprecision(2)<<num.top()<<endl; } } private: stack<char> operators; stack<double> num; }; int main(){ algorith al;al.go(); }
3.队列:相比于栈,多了一种火车排序的方法。可以应用在二叉树的层次遍历中(假设队列中是一个层次的节点,那么按队列访问节点的时候将左右子节点入队,在此层次遍历完成后,队列就被替换成了下一个层次的节点)。另外,对于迷宫问题的策略存储也多了一种方式。
3.有序表,散列与跳表:
有序表可以顺序插入,方法类似插入排序,形式有线性、链式。
散列表,又称哈希表,平均值公式不知道会不会考,线性散列:成功Un=1/2*(1+1/(1-a)^2) 不成功Sn=1/2*(1+1/(1-a)):链式散列:Un=1/(1-a) Sn=-1/aloga(1-a) 其中a为装载因子=装载数/桶数。
跳表。有点复杂的一种数据结构,但是还是得掌握。跳表事实上是在有序表的基础上添加了像金字塔一样数量层次的索引。每一层的高度不同,通过随机分配,将其分配给各个辖域,每个塔都可以从高到低,快速的到达塔所笼罩的阴影内。具体的实现方式是,跳表类记录链表头,尾,与各层的最后一个节点。每个节点带有元素,与指向后方所有高度低于此节点的塔的指针数组。插入时首先利用随机数确定层次,随后将前后的指针数组排线连接好。删除则将节点前后的节点排线直接对接保留交集即可。
4.二叉树与其他树:对所有树:边数+1=节点数 对完全二叉树:零度节点数=二度节点数+1 (2n-1与2n-1-1之间显然有这样的关系)前中后序遍历指的是父节点的位置。 不知道老师为什么要扣定义,但是定义上来看,树的根节点不能为空,二叉树的根节点可以为空,另外度的限制不同。
5.优先级队列:堆,是一个优先根树,同时也是一个完全二叉树。对于每一个子节点都局部性满足优先根与但不满足左高/低。区别于左高树HBLT。同时会要求每个局部都要左高。对于每个新插入的节点值需要上浮到合适位置即可,不需要考虑其他左右互换的操作。删除时,首先将根节点析构,随后将最后一个节点重新定位。过程是从根节点向下沉,等待最后一个节点位置的出现。
左高树,的合并是一切其他操作的核心,中心思想是递归式完成所有不符合左高大/小根规则子树的左右交换。
堆排序,就是不断建堆,拿走堆顶,然后再建堆,继续拿走。直到全部都拿走。
霍夫曼树,将01列序列映射到二叉树中从根节点到的某个子节点的路径。霍夫曼树通过合并节点形成树,最后所得到的每一个叶子节点的树坐标就是霍夫曼压缩方式的对应压缩代码。
数据库系统概念:
1.数据库的形式化查询代数:关系代数——δ选择、П投影、⋈自然连接、- 差集、∧且、∨或。元胞代数——目标为一个元组,最终只保留具有明确定义的属性,通过存在或任意另一个属于某一关系的元组确定自身的存在、表示形式类似于{t|ョs∈关系(利用s元胞对t元胞进行限制)。域代数——通过向量的存在性确定自身向量存在,形式类似于{<a,b>|ョc,<a,b,c>∈ 关系},其余的逻辑束缚规则同关系代数,可以直接以向量的形式规范一个或多个属性。
2.数据库的分解:五种范式的概念——数据库的组织形式,范式的分解,无关属性的去除程序与判定公式。好繁琐,真的有用吗,哎,就先硬记吧。
计算机组成原理:
1.存储器:
缓存的编码方式,没有特殊说明访存地址为字地址的情况下默认访存的地址为字节地址。一般情况下:
直接映射:
字块含字的位数+字含字节的位数=字块内地址位数
cache位数-字快内地址位数=组地址位数
主存位数-cache位数=标记位数
全相联映射:
字块内地址位数同上
直接映射的组地址位数+直接映射的标记位数=组地址位数
n路相联映射:
组地址映射和标记位数的边界向右移动相应的log2(n)位即可
存储器提高速度的方式:
缩短存储周期,增加存储体,增加存储字长。使用cache方式同时包含了左侧一种或多种的措施。
参数:命中率:h = 命中/总 平均访问时间:ta= ht命中+(1-h)t未命中 效率e=单次命中的时间/ta 一般未命中的时间用公式t未命中=nt命中便捷表示,方便计算。
2.I/O设备:
1)设备与主机信息传递的控制方法——程序查询方式特点为CPU工作效率不高,一直在串行工作状态,程序中断方式特点为相比查询方式CPU利用率提高 ,DMA方式特点为比以上两种方式CPU利用率都高,但硬件更复杂了。
2) CPU响应io的中断请求条件是允许中断触发器EINT状态为一,时间为每条指令执行阶段结束前。
3)多重中断与单重中断的区别与原因——多重中断的开中断在保护现场后执行,而单中断在中断返回前执行,两者开中断执行时间不一样,原因在于多中断为了在保护现场后能响应更高级别的请求,必须将开中断提前至保护现场之后,而在中断返回之后,执行开中断的单中断则不能实现此功能。
4) DMA的工作方式。复习的时候回来看几遍197/8页。
5)周期挪用也就是周期窃取。只有当CPU,已经处于。储存使用的时候才不会和主存抢占数据主线。
概率论与数理统计:
1.第3章联合分布概率联合分布概率密度。联合分布概率函数的概率分布。解题的大体流程。
1)对于求单变量概率密度的题目。首先。确定积分区域,也就是概率密度函数有意义的区间。根据题目要求对所需要积分的区域进行积分。分布函数的积分区域,默认为从负无穷到的函数变量。
2)对于单变量的函数的概率密度。可以直接将函数代入。但是需要在概率密度函数后面多乘上一个该函数的反函数导数的绝对值。之后再继续进行积分。
3)联合概率密度函数相当于二重积分。求联合概率密度函数的分布函数,或者说求概率。也相当于对。一个面积进行二重积分。那么首先要确定的就是可以区分的区域,也就是对于两个变量同时具有意义的区域。为了追求效率,要以最快的速度定下主次积分变量,确定积分的先后次序。
4)联合概率密度函数的概率密度。首先需要通过代换,把其中一个变量消掉。但是概率密度函数需要在后方乘上一个导函数的绝对值,这个导函数是是通过u=f(x,y)变换得到y=g(u,x), 对g函数的变量u求导得到的。具体的推到过程是一个比较简单的积分次序变换。实际应用中只需要记住公式就可以了。
得到了概率密度函数之后,也需要确定积分区域,现在的积分区域需要同时对x和g(u,x)进行束缚,得出结果之后,对u的取值进行分区间讨论,对概率密度函数进行x的实数域积分,最终得到关于单变量,u的概率密度函数。
5)联合概率密度函数的分布以及概率类似于单变量,正常二重积分即可。