DS博客作业03--树

0.PTA得分截图

1.本周学习总结

1.1 总结树及串内容

1、串的基本概念
--0个或多个字符组成的有限序列
空格串--只含空格的串
空串--所含字符数为0的串
2、串的存储结构
(一)顺序存储:
顺序串:用一组地址连续的存储单元存储串值的字符序列
结构体定义:

typedef struct
{
   char data[MaxSize];
   int length;
}SqString;

(二)堆分配存储:
在程序执行过程中通过动态分配而得
结构体定义:

typedef struct
{
   char *ch;
   int length;
}HString;

(三)链式存储:
链串中的一个结点可以存储多个字符
缺点:链串没有顺序串那么自由,在操作上会相对复杂。
有两种结构体定义:

typedef struct snode
{
   char data;
   struct snode *next;
}LiString;

typedef struct chunk
{
   char ch[Max];
   struct chunk *next;
}CHunk;
typedef struct snode
{
   CHunk *head,*tail;
   int curlen;
}LString;

3、串的模式匹配
(一)Brute-Force算法
·思路:从s的每一个字符开始依次与t的字符进行匹配

·时间复杂度:
最好情况下,O(m);最坏情况下,O(mn);平均情况下:O(mn)
·优点:思路简便,易理解
·缺点:当在数据较大的情况下,算法效率极低
(二)KMP算法
①利用next数组保存部分匹配信息
next[j]是指t[j]字符前有多少个字符与t开头的字符相同
·思路:利用next数组得到数据k,得知在t[j]之前有k个字符是匹配的,接下来从t[k]进行匹配

·求next数组的思路:

·时间复杂度:O(m+n)
·优点:相比较于B-F算法而言,KMP算法大大提高了算法的效率
·缺点:若t[j]前面有多个字符是相同的,j往前慢慢一步一步移动是没必要的。只需跳到前面不同字符重新开始
②利用nextval数组保存部分匹配信息
·思路:
当t[j]=t[next[j]]时: nextval[j]=nextval[next[j]],否则: nextval[j]=next[j]
·优点:相对于next数组保存信息使KMP算法又更简便了一步,算法效率也相对提升了
4、串的拓展
C++中的字符串string
优点:C++中的string不必担心内存是否足够,字符串长度等。
关于string的集中常用用法:
(一)初始化

string str="hhhhh";
string str("hhhhh");
string str(5,'h');
以上语句,str都为"hhhhh"

(二)获取长度
①利用length()函数
注:再与其他类型作比较时,需将两者转化成同一种类型
②利用size()函数
(三)插入
①s.insert(pos,n,ch):在字符串s的pos位置上面插入n个字符ch
②s.insert(pos,str):在字符串s的pos位置插入字符串str
③s.insert(pos,str,a,n):在字符串s的pos位置插入字符串str中位置a到后面的n个字符
④s.insert(pos,cstr,n):在字符串s的pos位置插入字符数组cstr从开始到后面的n个字符
(四)find()函数
①pos=s.find("ch"):返回元素ch在s中的下标pos
②pos=s.find_first_of("ch"):返回元素ch在s中第一次出现的下标pos
③pos=s.find_last_of("ch"):返回元素ch在s中最后一次出现的下标pos
④pos=s.find_first_not_of("ch"):返回第一个不匹配下标pos

(一)树
1、定义
树型结构是一种非线性结构,描述层次结构的关系
2、存储结构
①双亲存储结构

typedef struct
{
   ElemType data;
   int parent;//表示双亲的位置
}PTree[MaxSize];

优点:寻找一个节点的双亲结点比较方便
缺点:寻找一个节点的孩子节点比较麻烦
②孩子链存储结构

typedef struct node
{
    ElemType data;
    struct node *sons[MaxSize];//指向孩子
}TsonNode;

优点:寻找一个节点的孩子节点比较方便
缺点:寻找一个节点的双亲节点比较麻烦
③孩子兄弟链存储结构

typedef struct tnode 
{      ElemType data;	
        struct tnode *hp;  	//指向兄弟
        struct tnode *vp;  	//指向孩子结点
} TSBNode;

优点:方便把树转换为一个二叉树的形式去存储
3、遍历
①先根遍历(递归
根结点->左子树->右子树
②后根遍历(递归
左子树->右子树->根结点
③层次遍历
自上而下,自左而右的访问
4、应用:html文档树,window目录树
(二)二叉树
1、定义
·是n(n>=0)个结点的有限集合,它或为空树(n=0),或由一个根结点和至多两棵称为根的左子树和右子树的互不相交的二叉树组成
·五种形态结构:

·满二叉树&完全二叉树
满二叉树:如果所有分支结点都有双分结点
完全二叉树:没有单独右分支结点,底层却允许在右边缺少连续若干个结点。
2、性质
①非空二叉树上叶节点数等于双分支节点数加1
②在二叉树的第 i 层上至多有2^i个结点
③高度为h的二叉树至多有2^h-1个节点
④具有n个结点的完全二叉树的深度必为 [log2n] + 1
3、存储结构
(一)顺序存储结构
利用数组进行存储

缺点:查找,插入,删除等操作很不方便
(二)链式存储结构
结构体定义:

typedef struct node
{     ElemType data;
       struct node *lchild, *rchild;
}   BTNode;

4、创建与遍历
·二叉树的创建
将二叉树的空余部分用‘#’代替补成完成二叉树,利用递归进行创建。
①如果数组从0开始建树
伪代码:

if (i > len - 1) return NULL;
if (str[i] == '#') return NULL;
创建根节点:
      bt->data = str[i];
创建左子树:
      bt->lchild = CreatTree(str, ++i);
创建右子树:
      bt->rchild = CreatTree(str, ++i);

①如果数组从1开始建树
伪代码:

if (i > len - 1) return NULL;
if (str[i] == '#') return NULL;
创建根结点bt,bt->data=str[i]
创建左子树:
        bt->lchild =CreateBTree(str,2*i); 
创建右子树:
        bt->rchild =CreateBTree(str,2*i+1);

·二叉树的遍历
先中后序遍历都是是用递归的方法进行遍历,而层次遍历利用了队列来帮助进行。

~ 遍历顺序
先序遍历 根结点->左子树->右子树
中序遍历 左子树->根结点->右子树
后序遍历 左子树->右子树->根结点
层次遍历 一层一层从上往下,从左至右
5、应用
·用二叉树表示算术表达式
伪代码:
void InitExpTree(BTree& T, string str)
{
     定义一个树栈st;
     定义一个运算符栈exp;
     定义一个结构体树a,b;
     len=str.length();
     for i=0 to len
     {
          if str[i]为操作数
              为T申请空间
              CreateExpTree(T, NULL, NULL, str[i]);
              T入栈st;
          end if
           if str[i]为操作符
               if 运算符栈为空 then
                   str[i]入栈exp;
               end if
               else
                   {
                       op=exp.top();
                       if str[i]为')' then
                            while str[i]!='('
                                    取st的栈顶两个元素进行建树处理
                       end if
                       if op<str[i] then
                           str[i]入栈exp;
                       end if
                       if op>str[i] then
                          取st的栈顶两个元素进行建树处理
                          如果被除数为0,需要除0警告并结束进程
                       end if
                   }
     }
     while exp栈不为空
     {
         取st的栈顶两个元素进行建树处理
         如果被除数为0,需要除0警告并结束进程
     }
}

5、线索二叉树

结构体定义:

 typedef struct node 
  {      ElemType data;		
         int ltag,rtag;      		
         struct node *lchild;		
         struct node *rchild;		
  }  TBTNode;	

利用线索二叉树可以找到结点的前继结点和后继结点。
(三)哈夫曼树
1、定义
·具有最小带权路径长度的二叉树
·原则:
权值越大的叶结点越靠近根结点。
权值越小的叶结点越远离根结点。
2、存储结构
(一)顺序结构存储
结构体定义:

typedef struct
{	char data;		//节点值
	float weight;	
	int parent,lchild,rchild;
} HTNode;

3、创建
具体代码:

4、应用
哈夫曼编码:利用建好的哈夫曼树,左枝为0,右枝为1按照路径来进行编码。
(四)并查集
1、定义
按一定顺序将属于同一组的元素所在的集合合并,反复查找一个元素在哪个集合
2、存储结构
(一)顺序存储结构:
结构体定义:

typedef struct node
{      int data;		
        int rank;  
        int parent;		
} UFSTree;	

3、应用
朋友圈,亲戚关系……

1.2.谈谈你对树的认识及学习体会。

树这个结构,真的不好理解……刚开始学习的时候,比如去创建遍历二叉树利用递归算法(可能上学期的递归掌握的不扎实)我就挺糟心,还好老师给的PPT中有一张很详细的递归过程,我才慢慢明白创建是怎么创建出来的。这是我们学习的第一个非线性结构,多多少少还是会有思维上的问题。在刷pta这方面还是有比较大的困难的,难一些的题目我无法独立的完成,我会去问问同学啊,上网找找资料啊啥的。害……

2.阅读代码

2.1 打家劫舍III

题目:

解题代码:

2.1.1 该题的设计思路


时间复杂度:O(n)
空间复杂度:O(n)

2.1.2 该题的伪代码

int rob(TreeNode root)
{ 
    计算结点偷与不偷所能获得的收益;
    存储结果在result数组中;
}
int[] countSum(TreeNode root)
{
    定义一个数组result;
    if(root为空)
          return result;
    利用递归计算当前结点左儿子偷与不偷所能获得的收益
    利用递归计算当前结点右儿子偷与不偷所能获得的收益
    result[0]=左儿子所能获得的最大收益 + 右儿子所能获得的最大收益
    result[1]=偷当前节点的钱 + 不偷左儿子所获得的钱 +不偷右儿子所获得的钱
    return result;
}

2.1.3 运行结果

2.1.4分析该题目解题优势及难点。

解题优势:对每一个结点都设置了一个长度为2的数组并利用数组下标1或0来表示偷与不偷,再利用递归去分别计算结点的左右儿子如果被偷能获取的利益
难点:这道题涉及了树形的动态规划,需要去进行分析。我一开始看题目的时候其实没明白,后来看了题解的分析,我才知道说其实只要去将解题思路中的两种情况进行比较然后比较出最佳利益就可以知道答案了。

2.2 找树左下角的值

题目:

解题代码:

2.2.1 该题的设计思路

时间复杂度:O(n)
空间复杂度:O(n)

2.2.2 该题的伪代码

 int findBottomLeftValue(TreeNode* root)
{
      建立队列Qtree;
      队列中放入根节点
      while(队列不为空)
      {
          node = Qtree.front();
          删除队头结点;
          若右子树不为空,入队
          若左子树不为空,入队
      }
      return node->val;
}

2.2.3 运行结果

2.2.4分析该题目解题优势及难点。

解题优势:我看了力扣上有很多题解,用了广度搜索啊之类的方法,但我觉得这个解法会相对简便一些。先让整棵树入队列,然后树从右向左遍历的同时删除队头已经遍历完左右孩子的结点。
难点:就是关于这个怎么去取最尾端的结点,比如说这个结点可能在右子树在整个树的最后一层这些需要思考。

2.3 题目及解题代码

题目:

解题代码:

2.3.1 该题的设计思路


时间复杂度:O(n)
空间复杂度:O(n2)

2.3.2 该题的伪代码

void  cBST(struct TreeNode*root, struct TreeNode* arr[],int *count)
{
    if(root不为空)
    {
      中序遍历获取结点并有序的存入一个指针数组里;
    }
}
struct TreeNode* convertBST(struct TreeNode* root)
{
     cBST(root,arr,&count)
     for i=count-1 to 0
     {
         arr[i]->val=自身+后一个的值
     }
}

2.3.3分析该题目解题优势及难点。

解题优势:这道题抓住一个点,就是左子树的值要比右子树的值来得小。使用了中序遍历存入指针数组后再进行从后往前的遍历。
难点:这道题需要我们去观察题目给定的这棵树的特点,把他的跟和左右子树进行比较,最后根据特点才选择的利用中序遍历存入指针数组中。

2.4 寻找重复的子树

题目:

解题代码:

2.4.1 该题的设计思路


时间复杂度:O(n)
空间复杂度:O(n2)

2.4.2 该题的伪代码

char* Dfs(struct TreeNode* root, Hash** hashObj)
{
      if(结点为空)
          return "#";//空结点用#号表示
      将根结点放入string 中;
      将左孩子放入string 中;
      将右孩子放入string 中;
}
void AddHash(char* key, struct TreeNode* data, Hash** hashObj)
{
      查找hashObj中的key;
      if(node为空)
      {
          对结构体tmp进行赋值处理;
          在hashObj中加入key;
      }
      else
          node->cnt++;
}

2.4.3分析该题目解题优势及难点。

解题优势:利用了一个哈希数组去判断这棵树是否重复。
难点:其实这道题如果是我在做的话,不容易去想到哈希数组的解决方法。还有这道题他把树的形式去进行转化,转化为我们熟悉的字符串然后再进行操作等。

posted @ 2020-04-12 21:05  林浈  阅读(200)  评论(0编辑  收藏  举报