《剑指offer》Java实现——每天9题——第5天

面试题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函数(定义新的排序规则)进行排序。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值