面试题53:在排序数组中查找数字

题目:

数字在排序数组中出现的次数。

统计一个数字在排序数组中出现的次数。例如,输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在这个数组中出现了4次,因此输出4。

分析:

数组是有序的,自然想到用二分查找,二分查找到一个数据后,它的左右可能还有相同的数字,这时候,要向左右遍历,统计数量,考虑一个极端情况,假设数组中的数字全都相同,通过二分查找到中间数字后,继续向两侧判断和统计,此时时间复杂度是O(n),不是最佳的方案,还有更好的方法。

假设我们知道了相同值的第一个和最后一个,last-start+1就是出现的次数了,那么就需要找last和first,不同于前面的遍历查找,试想,在一些连续的,值相同的数组中,first和last和mid有什么区别呢?first的前一个要么是空,要么小于first的值,last的后一个要么是空,要么大于last的值,根据这个条件,可以区分开first,mid,last。找到mid后,继续使用二分的方法查找first和last,判别条件就是前面提到的判断相邻的元素,此时时间复杂度是O(logn)。

解法:

package com.wsy;

public class Main {
    public static int[] array;
    public static int length;
    public static int k;

    public static void main(String[] args) {
        array = new int[]{1, 2, 3, 3, 3, 3, 4, 5};
        k = 3;
        length = array.length;
        findKTimes();
    }

    public static int findFirstK(int start, int end) {
        if (start > end) {
            return -1;
        }
        int mid = (start + end) >> 1;
        if (array[mid] == k) {
            if (mid > 0 && array[mid - 1] != k || mid == 0) {
                return mid;
            } else {
                end = mid - 1;
            }
        } else if (array[mid] > k) {
            end = mid - 1;
        } else {
            start = mid + 1;
        }
        return findFirstK(start, end);
    }

    public static int findLastK(int start, int end) {
        if (start > end) {
            return -1;
        }
        int mid = (start + end) >> 1;
        if (array[mid] == k) {
            if (mid < length - 1 && array[mid + 1] != k || mid == length - 1) {
                return mid;
            } else {
                start = mid + 1;
            }
        } else if (array[mid] > k) {
            end = mid - 1;
        } else {
            start = mid + 1;
        }
        return findLastK(start, end);
    }

    public static void findKTimes() {
        int start = findFirstK(0, length - 1);
        int end = findLastK(0, length - 1);
        if (start != -1 && end != -1) {
            System.out.println(k + "的出现次数是:" + (end - start + 1));
        }
    }
}

题目:

0~n-1中缺失的数字。

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

分析:

有一个直观的解决方案,利用公式求前n项和,再减去实际数组中的和,这个差值,就是缺少的数字,此时时间复杂度是O(n)。数组中的数字是0~n-1,对应下标也是0~n-1,那么有一些数据和下标值相等,有一些数据和下标值不相等,我们找到第一个不相等的值,就能知道缺失的数字是多少了。

我们可以基于二分查找用如下过程查找:如果中间元素的值和下标相等,那么下一轮查找只需要查找右半边;如果中间元素的值和下标不相等,并且它前面的一个元素和下标值相等,那么中间的数字正好是缺失的数字;如果中间元素的值和下标不相等,并且它前面的一个元素和下标也不相等,那么下一轮查找我们只需要在左半边查找即可。

解法:

package com.wsy;

public class Main {
    public static void main(String[] args) {
        int[] array = new int[]{0, 1, 2, 3, 4, 5, 7};
        findMissNumber(array);
    }

    public static void findMissNumber(int[] array) {
        int length = array.length;
        int start = 0, end = length - 1;
        while (start <= end) {
            int mid = (start + end) >> 1;
            if (array[mid] != mid) {
                if (mid == 0 || array[mid - 1] == mid - 1) {
                    System.out.println("缺失的数字是:" + mid);
                    return;
                } else {
                    end = mid - 1;
                }
            } else {
                start = mid + 1;
            }
        }
    }
}

题目:

数组中数值和下标相等的元素。

假设一个单调递增的数组里的每个元素都是整数并且是唯一的。请编程实现一个函数,找出数组中任意一个数值等于其下标的元素。假如,在数组{-3,-1,1,3,5}中,数字3和它的下标相等。

分析:

还是使用二分法,查找的依据是下标和数值相同的点。

解法:

package com.wsy;

public class Main {
    public static void main(String[] args) {
        int[] array = new int[]{-3, -1, 1, 3, 5};
        findMissNumber(array);
    }

    public static void findMissNumber(int[] array) {
        int length = array.length;
        int start = 0, end = length - 1;
        while (start <= end) {
            int mid = (start + end) >> 1;
            if (array[mid] == mid) {
                System.out.println("数值和下标相等的是:" + mid);
                return;
            } else if (array[mid] > mid) {
                end = mid - 1;
            } else {
                start = mid + 1;
            }
        }
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值