面试题37 序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树
测试用例
- 功能测试(树有多个节点;树只有一个节点;每个节点只有左子树或者右子树;反序列化时序列为空,或者null)
- 特殊值测试(树为空)
实现代码
//一个全局的计数变量,用于反序列化计数
private static int index=0;
/**
* 序列化方法
* @param treeNode 要序列化的二叉树根节点
* @param ser 一个空字符串,用于存放序列
* @return 序列化后的字符串
*/
public String serialize(BinaryTreeNode treeNode,String ser){
if (treeNode == null)
return ser;
ser += treeNode.nValue;
ser += ",";
if (treeNode.left!=null)
ser=serialize(treeNode.left,ser);
else
ser+="$,";
if (treeNode.right!=null)
ser=serialize(treeNode.right,ser);
else
ser+="$,";
return ser;
}
/**
* 反序列化方法
* @param ser 序列通过“,”分割后形成的字符串数组
* @return 发序列化后的根节点
*/
public BinaryTreeNode deSerialize(String[] ser){
if (ser == null || index >= ser.length)
return null;
String sNum = ser[index];
BinaryTreeNode treeNode =null;
if (!sNum.equals("$")){
treeNode = new BinaryTreeNode(Integer.parseInt(sNum));
index++;
treeNode.left = deSerialize(ser);
index++;
treeNode.right =deSerialize(ser);
}
return treeNode;
}
算法思路
序列化思路:对二叉树进行前序遍历,每个接待你用逗号隔开,如果节点为空,则用” ”代替。反序列化思路:将序列通过逗号分割成字符串数组,对字符串数组进行遍历,如果节点不等于” ” 代 替 。 反 序 列 化 思 路 : 将 序 列 通 过 逗 号 分 割 成 字 符 串 数 组 , 对 字 符 串 数 组 进 行 遍 历 , 如 果 节 点 不 等 于 ” ”,则new一个二叉树节点存放该值,递归地对下一个字符进行判定,并将其赋给该节点的左孩子、右孩子。
面试题38 字符串的排列
输入一个字符串,打印出该字符串中字符的所有排列。例如,输入字符串abc,则打印出由a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。
测试样例
- 功能测试(字符串有多个字符;只有一个字符)
- 特殊值测试(字符串为null;字符串为空串“”)
实现代码
/**
* 求一个字符串的全排列
* @param str 字符串
* @param i 当前起始位置
*/
public static void permutation(char[] str, int i) {
if (i >= str.length)
return;
if (i == str.length - 1) {
System.out.println(String.valueOf(str));
} else {
for (int j = i; j < str.length; j++) {
char temp = str[j];
str[j] = str[i];
str[i] = temp;
permutation(str, i + 1);
temp = str[j];
str[j] = str[i];
str[i] = temp;
}
}
}
public static void main(String[] args) {
String s = "abc";
permutation(s.toCharArray(),0);
}
算法思路
把一个字符串分成两部分:第一部分是它的第一个字符;第二部分是后面的字符。我们求整个字符串的排列,可以看成两步。第一步求所有可能出现在第一个位置的字符,即把第一个字符和后面所有的字符交换。第二步固定第一个字符,求后面所有字符的排列。这是,后面的步骤同之前一样,可以使用递归实现。
面试题39 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如,输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组出现超过了5次,超过数组长度的一半,因此输出2.
测试用例
- 功能测试(数组只有一个元素,数组有多个元素)
- 特殊值测试(数组为空)
实现代码
/**
* 找出数组中出现次数超过一半的数字
* @param array 要查找的数组
* @return 超过一半的数字
* @throws Exception 当数组为空是抛异常
*/
public int moreThanHalfNum(int[] array) throws Exception {
if (array == null || array.length == 0)
throw new Exception();
int num;
int times=1;
num = array[0];
for (int i=1;i<array.length;i++){
if (num == array[i]){
times++;
}else{
if (times == 0) {
num = array[i];
times =1;
}
else
times--;
}
}
return num;
}
public static void main(String[] args){
InterQuestions39 test = new InterQuestions39();
int[] array = {1,2,3,2,2};
try {
System.out.println(test.moreThanHalfNum(array));
} catch (Exception e) {
System.out.println("the array is null!");
}
算法思路
在遍历数组的时候保存两个值:一个是数组中的数字;另一个是该数字出现的次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1;如果下一个数字和我们之前保存的数字不同,则次数减1。如果次数是0,那么我们保存另一个数字。
面试题40 最小的k个数
输入n个整数,找出其中最小的k个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
测试用例
- 边界值测试(k等于数组的大小,k等于1)
- 功能测试(数组有或者没有重复数字)
- 特殊值测试(k=0;数组为空)
实现代码
/**
* 以数组的left位置的数作为枢纽值,对数组进行一趟快速排序,找到枢纽值的恰当位置,并返回该位置
* @param arr 数组
* @param left 数组的左边界
* @param right 数组的右边界
* @return 左边界数值的最终位置
*/
public int partition(int[] arr, int left, int right) {
int result = arr[left];
if (left > right)
return -1;
while (left < right) {
while (left < right && arr[right] >= result) {
right--;
}
arr[left] = arr[right];
while (left < right && arr[left] < result) {
left++;
}
arr[right] = arr[left];
}
arr[left] = result;
return left;
}
/**
* 求最小k个数的核心方法。
* @param input 输入的数组
* @param k 前k个最小数
* @return 包含k个最小数的数组
*/
public int[] getLeastNumbers(int[] input,int k){
if(k==0 || input == null || input.length == 0)
return null;
int start = 0,end = input.length-1;
int index = partition(input,start,end);
int[] output = new int[k];
while (index!=k-1) {
if (index > k - 1) {
end = index-1;
index = partition(input, start, end);
} else {
start = index+1;
index = partition(input, start, end);
}
}
for(int i = 0;i<k;i++){
output[i] = input[i];
}
return output;
}
public static void main(String[] args){
int[] arr= {4,5,1,6,2,7,3,8};
InterQuestions40 test = new InterQuestions40();
int[] output=test.getLeastNumbers(arr, 4);
for(int i = 0;i<output.length ;i++){
System.out.print(output[i]+",");
}
}
面试题41 数据流中的中位数
如何得到一个数据流汇总的中位数?如果从数据流中读入奇数个数字,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
测试样例
- 功能测试(流中有偶数/奇数个数字;流中有/没有重复数字;流中只有一个数字)
- 特殊值测试(流为null;流中没有数字)
实现代码
private int count = 0;
private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
//读入字符,放到合适位置
public void Insert(Integer num) {
if (num == null)
return;
if (count %2 == 0) {
maxHeap.offer(num);
int filteredMaxNum = maxHeap.poll();//平衡两个堆,将右边小顶堆的最小元素交给左边的大顶堆
minHeap.offer(filteredMaxNum);
} else {
minHeap.offer(num);
int filteredMinNum = minHeap.poll();
maxHeap.offer(filteredMinNum);
}
count++;
}
//求中位数
public Double GetMedian() {
if (count %2 == 0) {
return (double) (minHeap.peek() + maxHeap.peek()) / 2;
} else {
return new Double(minHeap.peek());
}
}
public static void main(String[] args) {
InterQuestions41 test = new InterQuestions41();
int[] a={5,3,4,8,9,2,3,1};
//int[] b={4};
for (int i=0;i<a.length;i++)
test.Insert(a[i]);
System.out.println(test.GetMedian());
/* test.Insert(b[0]);
System.out.println(test.GetMedian());*/
}
算法思路
使用两个堆(一个大顶堆,一个小顶堆),大顶堆在左,小顶堆在右,使得大顶堆的所有元素都小于小顶堆的元素,同时,使两个堆中元素个数相差不超过1.这样中位数就始终在两个堆的堆顶。Java中的priorityQueue本身就是一个小顶堆,可以通过修改构造参数建立大顶堆。
面试题42 连续子数组的最大和
输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。
测试用例
- 功能测试(数组全为负数;有正有负;全是正数)
- 特殊输入测试(数组为null;数组中没有元素)
实现代码
/**
* 连续子数组的最大和
* @param array 数组
* @return 子数组的最大和
* @throws Exception 非法输入异常
*/
public int greatestSumOfSubArray(int[] array) throws Exception {
if (array == null|| array.length<1)
throw new Exception("invalid input!");
int greatestSum=0;
int curSum =0;
for (int i=0;i<array.length;i++){
if (curSum <= 0)
curSum = array[i];
else
curSum += array[i];
if (curSum>greatestSum)
greatestSum = curSum;
}
return greatestSum;
}
public static void main(String[] args) throws Exception {
InterQuestions42 test = new InterQuestions42();
int[] array = {1,-2,3,10,-4,7,2,-5};
int[] array2 = {-1,-5,-2,-6,-8};
System.out.println(test.greatestSumOfSubArray(array));
System.out.println(test.greatestSumOfSubArray(array2));
}
算法思路
分析数组规律。定义两个变量,一个表示最大和,一个表示当前和,即最大和加下一个数的和,如果当前和大于最大和,那么更新最大和。如果当前和小于最大和,那么继续遍历下一个。如果当前和变为负数,那么清空当前和。
面试题43 1~n整数中1出现的次数
输入一个整数n,求1~n这n个整数的十进制表示中1出现的次数。例如,输入12,1~12这些整数中包含1的有1、10、11和12,1总共出现了5次。
测试样例
- 功能测试(n等于1;n非常大)
- 特殊值测试(n小于1)
实现代码
/**
* 求1的出现次数
* @param n 1~n
* @return 出现的次数
*/
public int count(int n){
if (n<1)
return 0;
int count = 0;
int base = 1;
int round = n;
while (round>0){
int weight = round%10;
round/=10;
count += round*base;
if (weight == 1)
count+=(n%base)+1;
else if (weight>1)
count+=base;
base*=10;
}
return count;
}
算法思路
思路较为复杂。
参加博客:http://blog.csdn.net/yi_afly/article/details/52012593
面试题44 数字序列中某一位的数字
数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从0开始计数)是5,第13位是1,第19位是4,等等。请写一个函数,求任意第n为对应的数字。
测试样例
- 功能测试(n很大;n小于9;n大于9)
- 特殊值测试(n小于0)
实现代码
/**
* 求digits位数字总共有多少个
* @param digits 数字的位数
* @return 总共有多少个
*/
public int countOfIntegers(int digits){
if (digits == 1)
return 10;
return (int)(9*Math.pow(10,digits-1));
}
public int digitAtIndex(int index){
if (index<0)
return -1;
int digits = 1;
while (true){
int numbers = countOfIntegers(digits);
if (index<numbers*digits)
return digitAtIndex(index,digits);
index -= numbers*digits;
digits++;
}
}
/**
* 当已经知道了要找的那一位数字是几位数,可以用如下函数求得那一位数字
* @param index 数字
* @param digits 位数
* @return 那一位数字
*/
private int digitAtIndex(int index, int digits) {
int number = beginNumber(digits)+index/digits;
int indexFromRight = digits -index%digits;
for (int i=1;i<indexFromRight;i++)
number/=10;
return number%10;
}
/**
* 求第一个m位数。如第一个1位数是0,第一个2位数是10.
* @param digits 位数
* @return 起始数字
*/
private int beginNumber(int digits) {
if (digits ==1)
return 0;
return (int)Math.pow(10,digits-1);
}
算法思路
通过分解寻找数字规律。1位数有0~9,共9个;两位数有10~99,共90个;三位数100~999有900个….n位数(n>1)有9*pow(10,n-1)。当n>1是每个n位数都是以10、100、1000这样的数字开头的。
面试题45 把数组排成最小的数
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼出的所有数字中最小的一个。例如,输入数组{3,32,321},则打印出这3个数能拼成的最小数字321323。
测试样例
- 功能测试(数组只有一个数字;数组有多个数字;数组有/无重复数字)
- 特殊值测试(数组为null;数组为空数组)
实现代码
/**
* 使用了ArrayList的排序功能、sort的comparable构造参数以及lambda表达式
* @param arr 数组
*/
public void printMin(int[] arr){
ArrayList<Integer> list = new ArrayList<>();
for (int anArr : arr)
list.add(anArr);
list.sort((o1, o2) -> { //lambda表达式
String s1 = o1+""+o2;
String s2 = o2+""+o1;
return s1.compareTo(s2);
});
for (Integer aList : list) {
System.out.print(String.valueOf(aList));
}
}
算法思路
将数组装载到java的ArrayList中去,然后使用list自带的sort函数(定义新的排序规则)进行排序。