A-数组中出现次数超过一半的数字&最小的k个数

题目29:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。分析:  数组中有一个数字出现次数超过数组长度的一半,也就是说它出现的次数比其它数字出现的次数的和还要多。因此可以在遍历数组的时候保留两个值:一个是数组中的一个数字,另一个是次数。当遍历到下一个数...
摘要由CSDN通过智能技术生成

题目29:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。
分析:
  数组中有一个数字出现次数超过数组长度的一半,也就是说它出现的次数比其它数字出现的次数的和还要多。因此可以在遍历数组的时候保留两个值:一个是数组中的一个数字,另一个是次数。当遍历到下一个数字的时候,如果下一个数字和之前保存的数字相同,则次数加1,不同则次数减1。如果次数为0,需要保存下一个数字,并把次数设置为1。由于要找出的数字出现比其它所有的数字出现次数之和还要多,那么要找的数字肯定是最后一次把数字设为1时对应的数字。
  代码如下所示:

    public static int moreThanHalfNum(int[] array) {
        if (checkInvalidArray(array)) return 0;
        int result = array[0];
        int times = 1;
        for (int i = 1; i < array.length; i++) {
            if (times == 0) {
                result = array[i];
                times = 1;
            } else if (result == array[i])
                times++;
            else
                times--;
        }
        if (!checkMoreThanHalf(array, result)) return 0;
        return result;
    }

    // 全局变量判断数组是否无效
    public static boolean inputInvalid = false;

    public static boolean checkInvalidArray(int[] array) {
        inputInvalid = false;
        if (array == null || array.length <= 0) inputInvalid = true;
        return inputInvalid;
    }

    public static boolean checkMoreThanHalf(int[] array, int result) {
        int times = 0;
        for (int i = 0; i < array.length; i++) {
            if (array[i] == result) times++;
        }
        boolean isMoreThanHalf = true;
        if (times * 2 <= array.length) {
            inputInvalid = true;
            isMoreThanHalf = false;
        }
        return isMoreThanHalf;
    }

    public static void main(String[] args) {
        System.out.println(moreThanHalfNum(new int[]{1, 2, 3, 2, 2, 2, 5, 4, 2}));
        System.out.println(moreThanHalfNum(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}));
        System.out.println(moreThanHalfNum(new int[]{1}));
        System.out.println(moreThanHalfNum(null));
    }

题目30:输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数是1、2、3、4。
分析:
  最直接的思路是把输入的n个数排序,位于最前的k个数就是最小的k个数,这种思路的时间复杂度是O(nlogn)。
  可以先出创建一个大小为k的容器来存储最小的k个数字,如果容器已有的数字少于k个,则直接把读入的数字放进容器中;如果容器已有k个数字,此时不再是插入数字,而是替换容器中的数字。找出已有的k个数的最大值,然后拿待插入的数字和最大值比较,如果待插入的数字比最大值小,则替换最大值;如果待插入的数字比最大值大,那么不可能是最小的k个数,丢弃掉。
  当容器满了之后需要做三件事:1.在k个整数中找出最大整数;2.可能删除这个最大整数;3.可能插入一个新的整数。如果用二叉树实现这个容器,能在O(logk)时间内实现这三步,对于n个数字而言,总的时间复杂度是O(nlogk)。
  由于每次都需要找到k个数字的最大数字,很容易就会想到最大堆。在最大堆中,根结点的数字总是大于它的子树中的任意值。于是可以在O(1)时间内找到已有的k个数字的最大值,但需要O(logk)时间完成删除及插入操作。
  红黑树通过把结点分为红、黑两种颜色并根据一些规则确保树在一定程度上是平衡的,从而保证在红黑树中查找、删除、和插入操作都只需要O(logk)时间。使用TreeSet来实现。
  代码如下所示:

    // array中有相同数字的待解决
    public static void getLeastNumbers(int[] array, int k) {
        if (array == null || array.length == 0 || k <= 0 || k > array.length) return;
        TreeSet<Integer> set = new TreeSet<>();
        for (int i = 0; i < array.length; i++) {
            if (set.size() < k) set.add(array[i]);
            else if (array[i] < set.last()) {
                set.remove(set.last());
                set.add(array[i]);
            }
        }
        Iterator<Integer> iterator = set.iterator();
        while (iterator.hasNext()) {
            System.out.print(iterator.next() + " ");
        }
    }
    
	public static void main(String[] args) {
        getLeastNumbers(new int[]{4, 5, 1, 6, 2, 7, 3, 8}, 4);
        System.out.println();
        getLeastNumbers(new int[]{4, 5, 1, 6, 2, 7, 3, 8}, 1);
        System.out.println();
        getLeastNumbers(new int[]{4, 5, 1, 6, 2, 7, 3, 8}, 8);
        System.out.println();
        getLeastNumbers(new int[]{4, 5, 1, 6, 2, 7, 3, 8}, 0);
        System.out.println();
        getLeastNumbers(new int[]{4, 5, 1, 6, 2, 7, 3, 8}, 9);
        System.out.println();
        getLeastNumbers(null, 9);
        System.out.println();
        // 待解决
        getLeastNumbers(new int[]{1, 5, 1, 6, 2, 7, 1, 8}, 4);
    }

  这种解法适合海量数据处理,尤其是n较大且k较小的情况。
  当需要在某数据容器内频繁查找及替换最大值时,二叉树是一个合适的选择,并能想到用堆或者红黑树等特殊情况的二叉树来实现。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值