Java 100道经典机试笔试题(07)——附可运行代码

导语

每篇将有两道经典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,表示该字母未曾出现过

数组下标01225
对应字母abcz
标记 falsefalsefalsefalse

这样,我们利用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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值