王道数据结构2020版本个人学习笔记(第一轮)持续补充中……

目录

第三章   栈和队列

3.1 栈

3.2 队列

3.3 栈和队列的应用

3.4 特殊矩阵的压缩存储

3.5 算法题遇到的一些问题

第4章 树与二叉树

4.1 树的基本概念

4.2 二叉树的概念

4.3二叉树的遍历和线索二叉树

 


第三章   栈和队列

3.1 栈

存储单元?

逻辑结构?

栈的基本操作?

C语言标识符?

上溢和下溢?

卡特兰数?

存储单元:一个存储单元可以存储一个字节(Byte=8bit),计算机的存储器容量是以字节为最小单位的,比如1KB的存储他有1024个存储单元,一个字母占一个字节,一个汉字占两个字节。一个字符等于两个字节。

逻辑结构:线性结构和非线性结构,指的是数据元素之间的逻辑关系,即从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的。

栈的基本操作:

InitStack(&S),StackEmpty(S),push,pop,GetTop(S,&x),ClearStack(&S)

C语言标识符:标识符的第一个字符必须是大小写英文字母或下划线

 上溢和下溢:栈的插入和删除操作都是在栈顶进行的,只可能栈顶指针超出了最大范围,从而产生上溢,而不是下溢。

卡特兰数:对于n个不同元素进栈,出栈序列的个数为

 

3.2 队列

1.队列的顺序存储是怎么进行进队操作和出队操作的?

进队操作:队不满时,先送值到队尾元素,再将队尾指针加1(注意先后顺序,题目中着重考察)

出队操作:对不空时,先取队头元素值,再将队头指针加1

注意:循环队列的出队操作和进队操作也是如此

2.用单链表实现队列时,队头在链表的什么位置?为啥子?

最理想的情况是一个循环单链表,带头结点,带尾指针。其中头结点头指针负责删除,尾指针负责增加,那么入队和出队的时间复杂度都为O(1),这时我们再来看队头指针就应该在链头了。

 

3.3 栈和队列的应用

1.页面替换算法是否用到队列?

是的,在FIFO,先进先出中用到队列

2.栈在表达式求值中,一般是用于存放操作符的。

3.执行函数的时候,局部变量一般采用什么结构进行存储?

栈结构,因为调用函数的时候,系统会为调用者构造一个由参数表和返回地址组成的活动记录,并将记录压入系统提供的栈中,若被调用函数有局部变量,也要压入栈中。

注意:在递归调用的过程中,系统为每一层的返回点,局部变量,传入实参等开辟了递归工作栈来进行数据存储。递归调用函数时,在系统栈中保存的函数信息需满足先进后出的特点。

 

3.4 特殊矩阵的压缩存储

1.三角矩阵A中的元素按行优先存放在一维数组B中,aij元素对应B中数组下标K应当是?

K=2(i-1)+j-1

若已知K,求i和j?

i=(k+1)/3+1,j=k-2i+3

2.什么是稀疏矩阵?拿来干嘛?

矩阵元素个数S远远大于非零个数t,S>>t的矩阵称为稀疏矩阵,用来存放非零元素,节省存储空间,稀疏矩阵压缩存储后便失去了随机存取特性。

3.什么是十字链表?(这部分引用了青岛大学的课程教学资料)

概念:十字链表是有向图的另一种链式存储结构。我们也可以把他理解为有向图的邻接表和逆邻接表的结合。有向图中的每一条弧对应十字链表的一个弧节点,每一个顶点对应一十字链表的一个顶点节点。

举个例子:

如这样的有向图,我们的十字链表应该怎么画勒

先来看看邻接表是怎么画的:

邻接表描述了每个点的出度节点,但是有个缺点是,要想知道一个点的入度节点的话,则需要我们遍历整个邻接表,所以我们引入了十字链表。

现在修改表头节点的指针域,分别是数据域,入度边,出度边

修改弧节点的指针域,分别是,弧尾,弧头(带箭头的那端指向的那个节点),弧头一样的下一条弧,弧尾一样的下一条弧

画十字链表的步骤是:先把所有的出度边画出来,然后再来画入度。

王道数据结构2020版本个人学习笔记(第一轮)持续补充中……_第1张图片

 

3.5 算法题遇到的一些问题

1.为什么在关于一些栈,队列,链表函数传递参数要用到&,而有些又不用?什么时候用?

取地址,我们就可以对这个变量进行操作,改变这个变量。不用&,就只能得到副本,可以看,但是不能动。一句话来说,当你要改变这个变量,就加取地址,不想改就不加。

2.数据结构里面的基本操作一般是可以直接用的,下面罗列一下:

(1)栈的基本操作:                                                (2)队列的基本操作

InitStack(&S)                                                                InitQueue(&Q)

StackEmpty(S)                                                              QueueEmpty(Q)

Push(&S,x)                                                                    EnQueue(&Q,x)

Pop(&S,&x)                                                                  DeQueue(&Q,&x)

GetTop(S,&x)                                                                GetHead(Q,&x)

ClearStack(&S)

 

3.!是取非运算,都快忘了。比如stackEmpty(S)是判空,假设是true,那么!stackEmpty(S)之后就是false了。

4.指针的问题:

算法题不建议用指针,首先王道书上的算法答案本身就不一定是可运行的代码,保险起见能用数组就用数组吧。

char *str=“abc”和char str[]="abc"有什么区别嗷?

前面只能看,不能改。后面可以改。

 

5.算法题更注重的是逻辑清晰,而不是代码一定要可运行。一定要把备注写清楚。因为题目问也只是问个逻辑,而不是编程比赛。


第4章 树与二叉树

4.1 树的基本概念

1.树的定义是递归的,是一种递归的数据结构。树作为逻辑结构,同时也是一种分层结构。

 

2.树的路径长度怎么求?

是从树根到每个结点的路径长度的总和

 

3.常用于求解树结点与度之间关系的有:

总结点数=n0+n1+n2+……+nm

总分支点数=1n1+2n2+……+mnm

总结点数=总分支数+1

 

4.2 二叉树的概念

题目中涉及到的知识点(主要是我个人不熟悉知识点)

1.二叉排序树的插入和删除?

插入:

若二叉排序树为空,则插入结点作为根节点插入到空树中

否则,继续在左右子树中查找

树中已有,则不再插入

树中没有,则查找至某个叶子节点的左子树或右子树为空为止,则插入。结点应为该叶子结点的左孩子或右孩子

删除:

(1)被删除的结点是叶结点时,直接删除该结点

(2)被删除的结点只有左子树或者右子树,用其左子树或者右子树的结点替代他即可

举例:王道数据结构2020版本个人学习笔记(第一轮)持续补充中……_第2张图片

王道数据结构2020版本个人学习笔记(第一轮)持续补充中……_第3张图片

(3)被删除的结点既有左子树又有右子树的时候

A.以其中序前驱值替换之(值替换),然后再删除该前驱结点。前驱是左子树中最大的结点

王道数据结构2020版本个人学习笔记(第一轮)持续补充中……_第4张图片

王道数据结构2020版本个人学习笔记(第一轮)持续补充中……_第5张图片

王道数据结构2020版本个人学习笔记(第一轮)持续补充中……_第6张图片

B.也可以用后继替换之,然后再删除该后继结点。后继是右子树中最小的结点。

选择AB的策略,主要是看树的高度,选择变换后树的高度最小的那一个。

 

2.树如何转化为二叉树?

将树转换成二叉树的步骤是:
(1)加线。就是在所有兄弟结点之间加一条连线;
(2)抹线。就是对树中的每个结点,只保留他与第一个孩子结点之间的连线,删除它与其它孩子结点之间的连线;
(3)旋转。就是以树的根结点为轴心,将整棵树顺时针旋转一定角度,使之结构层次分明。

 

3.为什么在m叉树的情形下,结点i的第一个子女编号为j=(i-1)*m+2

王道数据结构2020版本个人学习笔记(第一轮)持续补充中……_第7张图片

 

 

4.3二叉树的遍历和线索二叉树

1.先序遍历的非递归算法?

思想:边遍历边打印,结点顺序是中左右

代码:

void PreOrder(BiTree T){
  InitStack(S);BiTree p=T;//初始化栈,构造遍历指针P
  while(p||!IsEmpty(S)){//当p不为空,S不为空时
    if(p){//如果p不为空,就将该点输出,且拜访他的左子树
         visit(p);
         push(S,p);
         p=p->lchid;
         }
    else{//如果p的左子树遍历完了,就开始回访以前的结点中哪些右子树是还没输出的,依次输出
        pop(S,p);
        p=p->rchild;
         }
                         }
                         }

2.中序遍历的非递归算法:

思想:找到左子树最下边的结点,并同时要用栈保存根节点,因为要通过根节点来进入右子树。结点访问顺序,左中右。

代码:

void InOrder(BiTree T){
  InitStack(S);BiTree p=T;
  while(p||!IsEmpty(S)){
    if(p){
      push(S,p);
      p=p->lchild;  
      }
    else{
      pop(S,p);
      visit(p);
      p=p->rchild;
      }

  }
}

3.后序遍历的非递归算法:(等题目具体涉及到了再来更新)

思想:要先访问完左孩子和右孩子之后才能访问根结点。如果根结点没有孩子,当然就直接输出;如果有,并且已经访问过左孩子和右孩子结点就可以访问根结点;如果根结点有孩子,但是孩子还没访问,就依次让右孩子和左孩子入栈,这样就保证了左孩子在右孩子之前访问。

代码:

void PostOrder(BiTree T){

InitStack(S);
BiTree p;       //当前结点
BiTree pre=null;//前驱结点

while(!IsEmpty(S)){
p=S.top();//p的值为当前栈顶元素
if((p->lchild==null&&p->rchild==null)||
   (pre!=null&&(pre==p->lchild||pre==p->rchild)){//如果p没有孩子结点,或者p的孩子结点已经访问过
    pop(S,P);
    visit(p);
    pre=p;
                                                 }
else{
    if(p->rchild!=null)push(S,p->rchild);//让右孩子先入栈
    if(p->lchild!=null)push(S,p->lchild);
}
}
}





}



}

4.层次遍历:

思想:先进先出,先把根节点入队,然后出队并访问该结点,再把该结点的左子树和右子树结点入队。然后出队,对出队结点访问,如此反复。

代码:

void LeverOrder(BiTree T){
  InitQueue(Q);
  BiTree p;
  EnQueue(Q,T);//将根结点入队
  while(!IsEmpty(Q)){
    DeQueue(Q,p);
    visit(p);
    if(p->lchild!=null){//如果左子树不为空,将其入队
    EnQueue(Q,p->lchild);
    }
    if(p->rchild!=null){
    EnQueue(Q,p->rchild);
    }
}

}

 

5.可以唯一地确定一棵二叉树是哪些序列?

先序中序,中序后序,层序中序。

6.通过中序遍历对二叉树线索化的递归算法

思想:对二叉树的线索化,实质上就是遍历一次二叉树,只是在遍历的过程中,检查当前结点左右指针域是否为空,若为空,将它们改为指向前驱结点或后继结点的线索。

代码:

void InThread(ThreadTree &p,ThreadTree &pre){//p是当前结点,pre是上一个结点
    if(p!=null){
    InThread(p->lchild,pre);//递归访问左子树

    if(p->lchild==null)//如果左孩子为空,就指向前驱结点
        p->lchild=pre;

    if(pre!=null&&pre->rchild==null)//如果前驱结点不为空,且前驱结点右孩子为空
        pre-rchild=p;//就让前驱结点指向后继结点,即pre指向了p
  
    pre=p;//记录前驱结点
    InThread(p->rchild,pre);//递归访问右子树
    }
}

7.线索二叉树的遍历

void InOrder(ThreadNode *T){//线索二叉树的中序遍历
for(ThreadNode *p=Firstnode(T);p!=null;p=Nextnode(p))
    visit(p);
}

ThreadNode * Firstnode(ThreadNode *p){
while(p->Itag==0)p=p->lchild;//如果有左孩子,就一直找到最左的哪一个,也就是中序的第一个结点

return p;
}

ThreadNode * Nextnode(ThreadNode *p){
if(p->rtag==0) return FirstNode(p->rchild);//如果没有线索,就找到右孩子树的最左结点

else return p->rchild;//否则就是有线索,返回后继元素即可 
}

 

你可能感兴趣的:(王道数据结构2020版本个人学习笔记(第一轮)持续补充中……)