拓扑结构相同子树练习题
对于两棵彼此独立的二叉树A和B,请编写一个高效算法,检查A中是否存在一棵子树与B树的拓扑结构完全相同。
给定两棵二叉树的头结点A和B,请返回一个bool值,代表A中是否存在一棵同构于B的子树。
解题思路
- 把二叉树t1,t2序列化为字符串str1和str2
- 用KMP算法判断str1中是否含有str2
代码
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
public class IdenticalTree {
public boolean chkIdentical(TreeNode A, TreeNode B) {
String strA = getSerializeString(TreeNodeA);
String strB = getSerializeString(TreeNodeB);
return KMP(StrA, StrB);
}
// 树形结构转成字符串
public String getSerializeString(TreeNode head){
if (TreeNode == null){
return null;
}
StringBuffer sb = new StringBuffer();
sb.append(head.val).append(getSerializString(TreeNode.left)).append(getSerializeString(TreeNode.right));
return sb.toString();
}
// 两字符串匹配
public boolean KMP(String strA, String strB){
char[] chA = strA.toCharArray();
char[] chB = strB.toCharArray();
int[] next = getNextArray(chB);
int i;
int k;
for (i = 0, k = 0; i < chA.length; i++){
while (k > 0 && chA[i] != chB[k]){
k = next[k-1];
}
if (chA[i] == chB[k]){
k++;
}
if (k == chA.length){
return true;
}
}
return false;
}
public int[] getNextArray(char[] chB){
int i;
int k;
int[] next = new int[chB.length];
for (i = 0, k = 0; i < chB.lenght; i++){
while (k > 0 && chB[k] != chB[i]){
k = next[k-1];
}
if (chB[k] == chB[i]){
k++;
}
next[i] = k;
}
return next;
}
}
词语变形练习题
如果对于一个字符串A,将A的前面任意一部分挪到后边去形成的字符串称为A的旋转词。比如A=”12345”,A的旋转词有”12345”,”23451”,”34512”,”45123”和”51234”。对于两个字符串A和B,请判断A和B是否互为旋转词。
给定两个字符串A和B及他们的长度lena,lenb,请返回一个bool值,代表他们是否互为旋转词。
测试样例: “cdab”,4,”abcd”,4
返回:true
概念普及:
变形词与旋转词的区别: 变形词:字符种类形同且每种字符出现的次数相同 旋转词:前边任意一部分挪到后边去形成的字符串
例如: String A = “DAQT” String B = “QDAT” 它们的关系是变形词,但却不是旋转词。
变形词包含旋转词,旋转词不包含变形词
解题思路
- 对str1,str2做字符计数
- 对比哈希表1余哈希表2的记录是否一致
代码
public boolean chkRotation(String A, int lena, String B, int lenb) {
if (A == null && B == null && lena != lenb){
return false;
}
char[] chA = A.toCharArray();
char[] chB = B.toCharArray();
int[] map = new int[256];
for (int i = 0; i < chA.length; i++){
map[chA[i]]++;
}
for (int i = 0; i < chB.length; i++){
if (map[chB[i]]-- == 0){
return false;
}
}
return true;
}
两串旋转练习题
如果对于一个字符串A,将A的前面任意一部分挪到后边去形成的字符串称为A的旋转词。比如A=”12345”,A的旋转词有”12345”,”23451”,”34512”,”45123”和”51234”。对于两个字符串A和B,请判断A和B是否互为旋转词。
给定两个字符串A和B及他们的长度lena,lenb,请返回一个bool值,代表他们是否互为旋转词。
测试样例:
“cdab”,4,”abcd”,4
返回:true
解题思路
最优解时间复杂度为O(N)
1. 判断str1与str2是否长度相等
2. 如果长度相等,生成str1+str1的大字符串
3. 用KMP算法判断大字符串中是否含有str2
代码
public class Rotation {
public boolean chkRotation(String A, int lena, String B, int lenb) {
if (A == null || B == null || lena != lenb){
return false;
}
String strA = A + A;
return kmp(strA, B);
}
public int[] getNextArray(char[] chs){
int i;//字符数组的下标指示器
int k;//最大前后缀
int[] next = new int[chs.length];
for(i = 1,k = 0; i < chs.length; i++){
/**
* 如果 chs[i] == chs[k], 说明最大前后缀相等+1 == next[i],因为前0 ~ k-1与 i-k+1 ~ i-1 是相等的
* 如果 chs[i] != chs[k],那得算出next[k-1]的最大前后缀,而不是与chs[0~k-1]一个个的比,只要比最大相等前缀就好了
*/
while(k > 0 && chs[i] != chs[k]){
k = next[k - 1];
}
if(chs[i] == chs[k]){
k++;
}
next[i] = k;
}
return next;
}
public boolean kmp(String str1,String str2){
char[] strA = str1.toCharArray();
char[] strB = str2.toCharArray();
int[] next = getNextArray(strB); //获取需要匹配子串的next数组
int i; //主串下
int k; //最大前后缀
for(i = 0,k = 0; i < strA.length; i++){
// 与next同理,只不过上边是自身相比,这次是主串与子串相比
while(k > 0 && strA[i] != strB[k])
k = next[k-1];
if(strA[i] == strB[k]){
k++;
}
if(k == strB.length){
return true;
}
}
return false;
}
}
句子的逆序练习题
对于一个字符串,请设计一个算法,只在字符串的单词间做逆序调整,也就是说,字符串由一些由空格分隔的部分组成,你需要将这些部分逆序。
给定一个原字符串A和他的长度,请返回逆序后的字符串。
测试样例:
“dog loves pig”,13
返回:”pig loves dog”
解题思路
- 实现将字符串局部所有字符逆序的函数f
- 利用f将字符串所有字符逆序 “pig loves dog” ——> “god sevol gip”
- 找到逆序后的字符串中每一个单词的区域利用f将每一个单词的区域逆序 “gog sevol gip” ——> “dog loves pig”
代码
public class Reverse {
public String reverseSentence(String A, int n) {
if (A == null || n == 0){
return null;
}
char[] chs = A.toCharArray();
// 使句子逆序
String strReverseA = reverse(chs);
String[] strWord = strReverseA.split(" ");
StringBuffer sb = new StringBuffer();
// 使所有的单词逆序
for (int i = 0; i < strWord.length; i++){
sb.append(reverse(strWord[i].toCharArray())).append(" ");
}
return sb.toString().trim();
}
public String reverse(char[] chs){
for (int i = 0, j = chs.length-1; i < j; i++, j--){
swap(chs, i, j);
}
return String.valueOf(chs);
}
public void swap(char[] chs, int i, int j){
char temp = chs[i];
chs[i] = chs[j];
chs[j] = temp;
}
}
拼接最小字典序练习题
对于一个给定的字符串数组,请找到一种拼接顺序,使所有小字符串拼接成的大字符串是所有可能的拼接中字典序最小的。
给定一个字符串数组strs,同时给定它的大小,请返回拼接成的串。
测试样例:
[“abc”,”de”],2
“abcde”
API普及
String.compareTo()
方法比较两个字符串的字典。比较是基于字符串中的每个字符的Unicode值。此String对象表示的字符序列的参数字符串表示的字符序列进行比较字典.
● 其结果是负的整数,如果此String对象字典前面的参数字符串
● 其结果是一个正整数,如果此String对象字典如下的参数字符串
● 结果是零,如果两个字符串相等,CompareTo返回0时,equal(Object)方法将返回true。对于Arrays类,有两个sort方法,sort(Object )和sort(int)。前者使用的是归并,后者是快排。
解题思路
如果str1+str2 < str2 + str1,则str1放在前面,否则,str2放在前面
代码
public class Prior {
// 数组排序类型
public class MyComparator implements Comparator<String>{
@Override
public int compare(String str1, String str2){
return (str1+str2).compareTo(str2+str1);
}
}
public String findSmallest(String[] strs, int n) {
if (strs == null || n == 0){
return null;
}
// 数组排序,排序算法为归并排序算法
Arrays.sort(strs, new MyComparator());
StringBuffer sb = new StringBuffer();
for (String str : strs){
sb.append(str);
}
return sb.toString();
}
}
空格替换练习题
请编写一个方法,将字符串中的空格全部替换为“%20”。假定该字符串有足够的空间存放新增的字符,并且知道字符串的真实长度(小于等于1000),同时保证字符串由大小写的英文字母组成。
给定一个string iniString 为原始的串,以及串的长度 int len, 返回替换后的string。
测试样例:
“Mr John Smith”,13
返回:”Mr%20John%20Smith”
”Hello World”,12
返回:”Hello%20%20World”
解题思路
- 遍历完数组,找出空格数目,new 一个新的数组
- 再遍历一次,在空格数加上 %20
代码
public class Replacement {
public String replaceSpace(String iniString, int length) {
if (iniString == null || length == 0){
return null;
}
char[] chs = iniString.toCharArray();
int num = 0;
for (int i = 0; i < length; i++){
if (chs[i] == ' ')
num++;
}
int allLength = length + 2 * num;
char[] newChs = new char[allLength];
for (int i = 0, j = 0; i < allLength; i++, j++){
if (chs[j] == ' '){
newChs[i] = '%';
newChs[i+1] = '2';
newChs[i+2] = '0';
i += 2;
}
else{
newChs[i] = chs[j];
}
}
return String.valueOf(newChs);
}
}
合法括号序列判断练习题
对于一个字符串,请设计一个算法,判断其是否为一个合法的括号串。
给定一个字符串A和它的长度n,请返回一个bool值代表它是否为一个合法的括号串。
测试样例:
“(()())”,6
返回:true
测试样例:
“()a()()”,7
返回:false
测试样例:
“()(()()”,7
返回:false
解题思路
- 整体变量num,代表‘(’出现的此时与‘)’出现次数的差值
- 遍历的过程中如果遇到‘(’则num++
- 遍历的过程中如果遇到‘)’则num–
- 遍历的过程中如果num<0,则直接返回false
- 如果一直没有出现情况4,则一直遍历下去
- 遍历完成后,如果num == 0,则返回true,否则返回false
代码
public class Parenthesis {
public boolean chkParenthesis(String A, int n) {
if (A == null || n == 0)
return false;
int num = 0;
char[] chs = A.toCharArray();
for (int i = 0; i < n; i++){
if (chs[i] == '('){
num++;
}
if (chs[i] == ')'){
num--;
}
if (num == 0 && chs[i] != '(' && chs[i] != ')'){
return false;
}
if (num < 0){
return false;
}
}
if (num == 0)
return true;
return false;
}
}
最长无重复字符子串练习题
对于一个字符串,请设计一个高效算法,找到字符串的最长无重复字符的子串长度。
给定一个字符串A及它的长度n,请返回它的最长无重复字符子串长度。保证A中字符全部为小写英文字符,且长度小于等于500。
测试样例:
“aabcb”,5
返回:3
解题思路
- 求出 chs[i] 上一个重复字符位置+1 = posA
- 求出 chs[i-1] 最长无重复字符长度位置 posB
- 比较posA和posB,如果posB在posA后边,去前一个无重复长度+1,反则取到i到posA的长度
- 在每个位置的无重复字符长度中取出最大值
代码
public class DistinctSubstring {
public int longestSubstring(String A, int n) {
if (A == null || n == 0){
return 0;
}
char[] chs = A.toCharArray();
Map<Character, Integer> map = new HashMap<Character, Integer>();
map.put(chs[0], 0); // 第一个值为已经确定的
int posA; // 重复出现'c'上一次出现的位置+1
int posB; // s[i-1] 往左无重复最长值;
int max = 1; // 最大无重复;
int pre = 1; // 上一个无重复最大值;
for (int i = 1; i < n; i++){
if (map.containsKey(chs[i])){
posA = map.get(chs[i])+1;
}
else{
posA = 0;
}
posB = i - pre - 1; // pre的位置是s[i-1]到s[pre],-1是因为s[i] 到s[i-1]有一个距离
if (posB >= posA){
pre++;
}
else{
pre = i - posA + 1; // +1 还有包括自身,求的是长度而不是距离
}
if (pre > max){
max = pre;
}
map.put(chs[i], i);
}
return max;
}
}