数据结构与算法---字符串---字符串轮转、另一个树的子树

字符串轮转

面试题01.09. 字符串轮转

给定两个字符串s1和s2,请编写代码检查s2是否为s1旋转而成
(比如,waterbottle是erbottlewat旋转后的字符串)。

示例1:

输入:s1 = “waterbottle”, s2 = “erbottlewat”
输出:True

示例2:

输入:s1 = “aa”, s2 = “aba”
输出:False

提示:
字符串长度在[0, 100000]范围内。
说明:
你能只调用一次检查子串的方法吗?


思路

这道题的意思是,给定一个字符串a,让字符串a的前面某一部分顺序放到后面,组成新的字符串b
既然是旋转(轮转),那么,新旧字符串的长度肯定不变,这也难怪示例2返回的是false

先判断两个字符串的长度是否相等,不相等返回false;相等,则进入下一步。

能否使用哈希表?

不能,虽然哈希表可以确认两个字符串是否拥有相同字符的个数,但是不能确定相互之间的顺序

那,是否可以将原来字符串分割为两部分,一部分为被旋转的a,一部分为没有被旋转的b
也就是ab与新的字符串ba做对比
如果能找到b串的开头,则拿b串的开头与ba做对比,b串比较完毕后,拿a进行剩余的对比

这个想法想起了之前判断两个链表的公共部分:相交链表
当时用的是一部分拼接到另外一部分的尾部
那么,这道题是否可以使用呢?

以s1 = “waterbottle”, s2 = "erbottlewat"为例
判断s2是否为s1的轮转,可以做两个s2:s2s2 = erbottlewaterbottlewat
在里面寻找,是否有s1(waterbottle)关键字

题目转化为,在一个字符串里面s2s2,查找是否存在s1

class Solution {
    public boolean isFlipedString(String s1, String s2) {
        if(s1 == null || s2 == null) return false;
        if(s1.length() != s2.length()) return false;

        return (s1 + s1).contains(s2);
    }
}

代码简单的有点过分。。。

有没有可能,本来s2不是s1的轮转,而s2s2一拼接,在s2s2里面找到了s1的子串?

不会
假如s1是abc,那么s1+s1 = abcabc

里面的三位数的串为:
abc bca cab abc
只要在s1+s1里面找到了与s2相等的串,那么,s2就一定是s1的轮转


另一个树的子树

572. 另一个树的子树

给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。

示例 1:
给定的树 s:


     3
    / \
   4   5
  / \
 1   2

给定的树 t:

   4 
  / \
 1   2
 

返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。

示例 2:
给定的树 s:


     3
    / \
   4   5
  / \
 1   2
    /
   0

给定的树 t:

   4
  / \
 1   2

返回 false。

思路

也就是,求t是否为s的子树,并且,t不能只是s子树的一部分

将二叉树的问题,转化为字符串的问题

使用层序遍历肯定是不行的,排除层序遍历

使用前、中、后序遍历树s,获取s的序列
再同样使用前、中、后序遍历树t,获取t的序列
如果t为s的子树,那么,t的序列为s的子序列

例子1中:
s的前序遍历:3->4->1->2->5
s的中序遍历:1->4->2->3->5
s的后序遍历:1->2->4->5->3

例子2中:
s的前序遍历:3->4->1->2->0->5
s的中序遍历:1->4->0->2->3->5
s的后序遍历:1->0->2->4->5->3

例子1和2中:
t的前序遍历:4->1->2
t的中序遍历:1->4->2
t的后序遍历:1->2->4

但,有一个问题,问题在于,只用一个前、中、后序,不能确定一个树的形状
我们知道,只有前、中 或者 中、后序遍历,两个一起能确定一棵树的形状。

那么,我们可以将空节点使用特殊符号 # 填充
另外,我们还需要特殊字符 -> 将节点之间进行标识
比如,你写123,是没法知道12是一个节点,还是1是一个节点

我们使用前序遍历,再次遍历例子1:
s的前序遍历:3->4->1->#->#->2->#->#->5->#->#->
t的前序遍历:4->1->#->#->2->#->#->

我们使用前序遍历,再次遍历例子2:
s的前序遍历:3->4->1->#->#->2->0->#->#->#->5->#->#->
t的前序遍历:4->1->#->#->2->#->#->

由一个二叉树,使用字符串表示出来的这个过程被称为序列化

class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {
        if(s == null || t == null) return false;
        //序列
        return preSerialize(s).contains(preSerialize(t));
    }

    private String preSerialize(TreeNode root){
        StringBuilder sb = new StringBuilder();
        preSerialize(root, sb);
        return sb.toString();
    }
    private void preSerialize(TreeNode node, StringBuilder sb)
    {
        if(node.left == null){
            sb.append("#->");
        }else{
            preSerialize(node.left, sb);
        }
        
        if(node.right == null){
            sb.append("#->");
        }else{
            preSerialize(node.right, sb);
        }
        
        //后序遍历
		sb.append(node.val).append("->");
    }
}

以上为后序遍历

如果上面代码直接使用,修改为前序遍历,有个坑需要注意一下
[12]与[2]
12->#->#->
2->#->#->
s包含t
因此,在使用前序遍历的时候,需要
StringBuilder sb = new StringBuilder("->");
也就是,在前面加一个特殊符号,这样
->12->#->#->
->2->#->#->
s就不包含t了

又试了下中序遍历,提交的时候,提示错误:
[3,4,5,1,2,null,null,0]
[4,1,2]

leetCode的数组是层序遍历

在这里插入图片描述
两棵树的形状:

      3
     / \
    4   5
   / \
  1   2
 /
0

   4
  / \
 1   2

s的中序遍历#->0->#->1->#->4->#->2->#->3->#->5->#->
t的中序遍历--------#->1->#->4->#->2->#->--------------
这么看来,中序遍历是不行的

为何中序遍历不行???

自己写的前序遍历解决问题,思想是一样的

class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {
        if(s == null || t == null) return false;
        return preTree(s).contains(preTree(t));
    }

    private String preTree(TreeNode node)
    {
        String s = new String();

        if(node == null) {
            return s + "#->";
        }
        
        s = s + preTree(node.left);
        s = s + preTree(node.right);
        //打印节点
        s = s + node.val + "->";
        return s;
    }
}

这样也是对的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值