• [剑指Offer] 3 数组中重复的数字


    题目1:数组中重复的数字

    描述

    在一个长度为n的数组里的所有数字都在0~(n-1)的范围内。
    数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。
    请找出数组中任意一个重复的数字。

    • 示例

    输入:{2,3,1,0,2,5,3} 长度为7的数组
    输出:2或者3 重复的数字

    实现代码

    class DuplicateNumber {
    
        /**
         * 暴力枚举 
         * 将数组中每个数与其他数对比 有重复就返回结果
         * 最差结果 n^2 最后两个重复
         */
        public static int bruteDuplicate(int[] arr, int length) {
            if (arr.length <= 0 || length <= 0) {
                return -1;
            }
            for (int i : arr) {
                if (i < 0 || i > length - 1) {
                    return -1;
                }
            }
    
            for (int i = 0; i < length; i++) {
                for (int j = i+1; j < length; j++) {
                    if (arr[i] == arr[j]) {
                        return arr[i];
                    }
                }
            }
    
            return -1;
        }
    
        /**
         * 修改数组方法
         * 逐个排序,在排序过程中寻找重复数字
         * 从数组第一个数开始,将数字交换放入相应下标的位置,重复的数字必然会在交换过程中被发现
         * 最差结果为 n-1 数组内数字全部交换完
         * 
         * 有些类似桶方法,每个下标为一个桶,将元素放置到与下标相同的位置
         * 限制:连续的元素
         */
        public static int modifyArrayDuplicate(int[] arr, int length) {
            if (arr.length <= 0 || length <= 0) {
                return -1;
            }
            for (int i : arr) {
                if (i < 0 || i > length - 1) {
                    return -1;
                }
            }
    
            for (int i = 0; i < length; i++) {
                // 将数组内数字和下标不相等的交换 直至发现数字重复
                while (arr[i] != i) {
                    if (arr[i] == arr[arr[i]]) {
                        return arr[i];
                    }
    
                    // 交换
                    int temp = arr[i];
                    arr[i] = arr[temp];
                    arr[temp] = temp;
                }
            }
    
            return -1;
        }
    }
    
    • 测试代码
    public class FindDuplicateNumber {
    
        public static void main(String[] args) {
            // 有多个重复数字
            unitTest(new int[]{2,3,1,0,2,5,3});
            // 有多个重复数字 偏后
            unitTest(new int[]{1,2,3,4,5,5,1});
            // 有一个重复数字 前后
            unitTest(new int[]{0,2,3,4,5,6,0});
            // 都是重复数字
            unitTest(new int[]{2,2,3,3});
            // 数组数字超出边界
            unitTest(new int[]{2,3,7,0,2,5,3});
            // 数组为空
            unitTest(new int[]{});
        }
    
        private static void unitTest(int[] array) {
            int length = array.length;
            printArray(array);
            System.out.println("暴力枚举" + DuplicateNumber.bruteDuplicate(array, length));
            System.out.println("桶排序" + DuplicateNumber.modifyArrayDuplicate(array, length));
        }
    
        private static void printArray(int[] arr) {
            System.out.print("array:	" + arr.length + "
    ");
            for (int i = 0; i < arr.length; i++) {
                System.out.print(arr[i] + "	");
            }
            System.out.println();
        }
    }
    

    题目2:不修改数组找出重复的数字

    描述

    在一个长度为n+1的数组里的所有数字都在1~n的范围内。
    数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。
    请找出数组中任意一个重复的数字,但不能修改输入的数组。

    • 示例

    输入:{2,3,5,4,3,2,6,7} 长度为8的数组
    输出:2或者3 重复的数字

    • 注意:

    请勿将此题与上题联系
    题目条件只是形似 但本质是不同
    上一题是:有n个元素,有n种元素 个数和种类相同 可能出现不重复的
    本题是:有n+1个元素,有n种元素 个数和种类不同 一定会出现重复的 鸽舍原理

    思路

    按区域元素出现频次,来辨别重复数字是否出现在该区域

    鸽舍原理 n只鸽子,(n-1)个鸽舍,必有1个鸽舍有2只鸽子及以上

    区域划分可以使用二分法
    3_2_二分查找必有重复数字的数组

    实现代码

    class DuplicateNumber {
    
       /**
         * 不修改数组方法 1~n
         * 鸽舍原理 + 二分查找 -- [1,3] 这4个数字在数组中出现5次,则必有一个数字出现两次及以上
         */
        public static int binarySearchDuplicate(int[] arr, int length) {
            if (arr.length <= 0 || length <= 0) {
                return -1;
            }
            for (int i : arr) {
                if (i < 0 || i > length - 1) {
                    return -1;
                }
            }
    
            int start = 0;
            int end = length - 1;
            // 通过二分遍历元素种类 得到它在数组中出现的次数
            while (end >= start) {
                int middle = ((end - start) >> 1) + start;
                int count = countRange(arr, length, start, middle);
                
                if (end == start) {
                    if (count > 1) {
                        return start;
                    } else {
                        break;
                    }
                }
    
                // 出现频次大于元素个数 则重复元素必然在此区间
                if (count > (middle - start + 1)) {
                    end = middle;
                } else {
                    start = middle + 1;
                }
            }
    
            return -1;
        }
    
        /**
         * 找出[start,end]在数组中出现的次数
         */
        private static int countRange(int[] arr, int length, int start, int end) {
            int count = 0;
            for (int i : arr) {
                if (i >= start && i <= end) {
                    count++;
                }
            }
            return count;
        }
    }
    
    • 测试
    public class FindDuplicateNumber {
    
        public static void main(String[] args) {
            // 有多个重复数字
            unitTest(new int[]{2,3,1,4,2,5,3});
            // 有多个重复数字 偏后
            unitTest(new int[]{1,2,3,4,5,5,1});
            // 有一个重复数字 前后
            unitTest(new int[]{1,2,3,4,5,6,1});
            // 都是重复数字
            unitTest(new int[]{2,2,3,3});
            // 数组数字超出边界
            unitTest(new int[]{2,3,7,0,2,5,3});
            // 数组为空
            unitTest(new int[]{});
        }
    
        private static void unitTest(int[] array) {
            int length = array.length;
            printArray(array);
            System.out.println("二分查找" + DuplicateNumber.binarySearchDuplicate(array, length));
        }
    
        private static void printArray(int[] arr) {
            System.out.print("array:	" + arr.length + "
    ");
            for (int i = 0; i < arr.length; i++) {
                System.out.print(arr[i] + "	");
            }
            System.out.println();
        }
    }
    
  • 相关阅读:
    [转]SIFT特征提取分析
    OSGEARTH三维地形开源项目
    使用C#改变鼠标的指针形状
    检测到 LoaderLock:DLL"XXXX"正试图在OS加载程序锁内执行
    未能加载文件或程序集“XXXXX”或它的某一个依赖项。试图加载格式不正确的程序。
    未能进入中断模式,原因如下:源文件“XXXXXX”不属于正在调试的项目。
    C# 版本的 计时器类:精确到微秒 秒后保留一位小数 支持年月日时分秒带单位的输出
    OpenGL2.0及以上版本中glm,glut,glew,glfw,mesa等部件的关系
    OpenGL 4.3配置教程
    ubuntu maven环境安装配置
  • 原文地址:https://www.cnblogs.com/slowbirdoflsh/p/11288475.html
Copyright © 2020-2023  润新知