导语
每篇将有两道经典Java机试题,每道题后面均为大家附上代码,每一道题目力求:
- 能够在JDK11环境下编译
- 在Eclipse JavaIDE中运行通过
- 思路易想易懂易学
- 重点代码有注释
第013题 删除重复字符(难度:★★☆☆☆)
题目描述:
有一个由小写字母组成的字符串s,长度length(1 ≤ length ≤ 1000),s中的每个字符都是小写的英文字母('a' - 'z')。在s中可能有一些字母重复出现。比如在"banana"中,字母'a'和字母'n'分别出现了三次和两次。现在只想保留第一次出现并删除掉后面出现的字母。banana中删除重复的字母后,变成:ban。
输入示例:
banana
输出示例:
ban
思路
最直接的想法是:一个for循环,每个字母分别与前面的字母比较,如果前面已经出现了该字母,则把该字母删除。但是简单算一算就知道这种算法的时间复杂度至少是o(n²)的。有没有一种算法的时间复杂度稍微低一点呢?
我们可以开辟一个大小为26的数组arr[26],下标为0的对应a,下标为1的对应b……下标为25的对应z,并且每个数组带有一个标记falg,初始为false,表示该字母未曾出现过
数组下标 0 1 2 … 25 对应字母 a b c … z 标记 false false false … false 这样,我们利用ASCII码可以直接计算出对应下标(a的ASCII码是97),找到对应字母是否出现过,假如false为false,说明还未曾出现过,将其保存到String型变量wordString中,倘若该字母的flag为true,则表示已经出现过,则无需添加到wordString中。
for (int i = 0; i < letter.length; i++) { //计算出该字母对应的数组下标 int temp = letter[i] - 97; //计算出对应的下标(a的ASCII是97) if(arr[temp].getFlag() == false) { //如果该字母的flag是false wordString = wordString + letter[i];//说明是第一次出现,则添加到wordString中(wordString初始化是“”,空) arr[temp].setFlag(true); //并把该字母的flag改为true } }
上述算法的时间复杂度可以降到O(n)。
代码
import java.util.Scanner;
class Arr{
private char letter;
private boolean flag;
void Arr() {
flag = false;
}
public void setLetter (char letter) {
this.letter = letter;
}
public void setFlag (boolean flag) {
this.flag = flag;
}
char getLetter() {
return letter;
}
boolean getFlag() {
return flag;
}
}
public class Test{
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String s = input.next();
Arr arr [] = new Arr [26];
//初始化类数组
for (int i = 0; i < arr.length; i++) {
arr[i] = new Arr();
}
//将输入的字符串转换成字符数组
char letter [] = s.toCharArray();
//将对应的标识改成true
String wordString = "";
for (int i = 0; i < letter.length; i++) {
//计算出该字母对应的数组下标
int temp = letter[i] - 97; //计算出对应的下标(a的ASCII是97)
if(arr[temp].getFlag() == false) { //如果该字母的flag是false
wordString = wordString + letter[i];//说明是第一次出现,则添加到wordString中
arr[temp].setFlag(true); //并把该字母的flag改为true
}
}
System.out.println(wordString);
}
}
运行结果
第014题 满二叉树转换为求和树(难度:★☆☆☆☆)
题目描述:
给满出二叉树,编写算法将其转化为求和树
什么是求和树:二叉树的求和树, 是一颗同样结构的二叉树,其树中的每个节点将包含原始树中的左子树和右子树的和。
二叉树:
10
/ \
-2 6
/ \ / \
8 -4 7 5
求和树:
20(4-2+12+6)
/ \
4(8-4) 12(7+5)
/ \ / \
0 0 0 0
二叉树给出前序和中序输入,求和树要求中序输出;
所有处理数据不会大于int;
输入描述:
2行整数,第1行表示二叉树的前序遍历,第2行表示二叉树的中序遍历,以空格分割
输出描述:
1行整数,表示求和树的中序遍历,以空格分割
输入示例:
10 -2 8 -4 6 7 5 8 -2 -4 10 7 6 5
输出示例:
0 4 0 20 0 12 0
思路
这道题难度星级是按照默认你学过数据结构中树的相关知识标明的,因为解决这道题需要有一定的数据结构基础,学过数据结构的伙伴对这道题可以马上写出解题程序。倘若你没有学过也是比较数据结构,那么这道题对你而言难度就不小了。解决这道题你需要学习什么是树,什么是二叉树,什么又是满二叉树,二叉树的前序、中序遍历。本篇文章默认你已经学过,那么解决本题只需以下几步:
- 根据前序序列和中序序列构建二叉树
- 更新二叉树的值(求和,值等于左右孩子结点值之和)
- 中序遍历输出新的二叉树,即求和树
下面便叙述每一步的做法:
1、前序序列中,第一个序列一定是二叉树的根,即10。这样,我们利用这个根,在中序序列中找到它的位置,那么10左边便是10的左子树,右边便是10的右子树,
与此同时在前序中找到10对应的位置进行左右子树划分:
画成图就如下:
由于每个左子树和右子树本身也是一棵二叉树,所以分别在左子树(8、-2、-4)和右子树(7、6、5)中重复上述步骤:
画成图如下:
这就完成二叉树构建了。
2、既然树已经建成功,那么求和就不难了:某一节点的值=其左孩子结点值 + 其右孩子结点值。不过要考虑某些情况下,孩子结点为空的情况。
static void sumNode(Node node) { //左右孩子为空:值和为0 if (node.lChildNode == null && node.rChildNode == null) { node.sum = 0; } //左孩子为空,右孩子不空:值和为右孩子结点值 else if (node.lChildNode == null) { sumNode(node.rChildNode); node.sum = node.rChildNode.sum + node.rChildNode.data; } //左孩子不空,右孩子为空:值和为左孩子结点值 else if (node.rChildNode == null) { sumNode(node.lChildNode); node.sum = node.lChildNode.sum + node.lChildNode.data; } //左孩子不空,右孩子不空:值和为左右孩子结点值和 else { sumNode(node.lChildNode); sumNode(node.rChildNode); node.sum = node.lChildNode.sum + node.lChildNode.data + node.rChildNode.sum + node.rChildNode.data; } }
3、中序遍历输出
所谓中序,就是:左孩子(左子树)->根节点->右孩子(右子树),顺序输出一棵树的值,根结点在中间
比如上面建立的那棵树:首先是根结点10:(左:-2/8/-4)10(右;6/7/5);
那么每一棵子树又可以看成一颗树,那么左子树:根结点:-2,则:(左:8)-2(右:-4);
右子树:根结点6:,则:(左:7)6(右:5);
如此反复,直到左子树和右子树为空。
上述的树中序输出结果为:((8)、-2、(4))、10、((7)、6、(5))
//中序遍历:使用递归 static void inOrder(Node node) { if (node == null) return; inOrder(node.lChildNode); ans.add(node.sum); inOrder(node.rChildNode); }
完成。
代码
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
//二叉树数据结构
class Node{
int data; //数据
int sum; //和
Node lChildNode; //左孩子
Node rChildNode; //右孩子
//结点关系
public Node(int data, Node lChildNode, Node rChildNode) {
this.data = data;
this.lChildNode = lChildNode;
this.rChildNode = rChildNode;
}
}
public class Test{
static int preOrder [];
static int inOrder [];
static List<Integer> ans; //存和的中序遍历
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String PreString [] = input.nextLine().split(" ");//先接收一行前序序列,以空格分割
String InString [] = input.nextLine().split(" "); //再接收一行中序序列,以空格分割
int length = PreString.length;
preOrder = new int [length];
inOrder = new int [length];
//将接收的先序中序序列字符转换成数值
for (int i = 0; i < length; i++) {
preOrder [i] = Integer.valueOf(PreString[i]);
inOrder [i] = Integer.valueOf(InString[i]);
}
/**
* 解题三步骤:
* 1、根据前序序列和中序序列构建二叉树
*/
//递归入口:先序的第一个值
Node root = creatTree(0, 0, length - 1);
/**
* 解题三步骤:
* 2、更新二叉树的值(求和,值等于左右孩子结点值之和)
*/
sumNode(root);
//保存更新后的二叉树值
ans = new ArrayList<>();
/**
* 解题三步骤:
* 3、中序遍历输出新的二叉树,即求和树
*/
inOrder(root);
for (int i: ans) {
System.out.print(i + " ");
}
System.out.println();
}
//根据先序和中序构建二叉树。
static Node creatTree(int root, int begin, int end) {
//递归出口:当begin > end时,结束递归
if (begin > end) return null;
//创建该结点
Node node = new Node(preOrder[root], null, null);
//从中序的第一个位置开始搜寻
int location = 0;
//记录该结点在中序序列中的位置
int cnt = 0;
//在中序序列中找到该结点的位置
for (location = begin; location <= end; location++) {
cnt++;
if (preOrder[root] == inOrder[location])
break;
}
//为结点添加左孩子
node.lChildNode = creatTree(root + 1, begin, location - 1);
//为结点添加右孩子
node.rChildNode = creatTree(root + cnt, location + 1, end);
return node;
}
//更新二叉树结点值:值等于左右孩子结点值之和。
static void sumNode(Node node) {
//左右孩子为空:值和为0
if (node.lChildNode == null && node.rChildNode == null) {
node.sum = 0;
}
//左孩子为空,右孩子不空:值和为右孩子结点值
else if (node.lChildNode == null) {
sumNode(node.rChildNode);
node.sum = node.rChildNode.sum + node.rChildNode.data;
}
//左孩子不空,右孩子为空:值和为左孩子结点值
else if (node.rChildNode == null) {
sumNode(node.lChildNode);
node.sum = node.lChildNode.sum + node.lChildNode.data;
}
//左孩子不空,右孩子不空:值和为左右孩子结点值和
else {
sumNode(node.lChildNode);
sumNode(node.rChildNode);
node.sum = node.lChildNode.sum + node.lChildNode.data + node.rChildNode.sum + node.rChildNode.data;
}
}
//中序遍历
static void inOrder(Node node) {
if (node == null) return;
inOrder(node.lChildNode);
ans.add(node.sum);
inOrder(node.rChildNode);
}
}
运行结果
以上是本次两道Java机试题
如有不足,欢迎批评指正
欢迎阅读上一篇:Java 100道典型机试笔试题(06)
欢迎阅读下一篇:Java 100道典型机试笔试题(08)
作者:小南瓜
日期:2019年10月19日10:50