Java入门——数组的练习题

1. 顺序查找数组中的某个元素

public class Note {
    public static int findArr(int[] arr, int n) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == n) {
                return i;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        int[] arr = new int[]{1,3,5,7,9};
        int ret = findArr(arr,3);
        if (ret >= 0) {
            System.out.println("找到了,元素下标为:" + ret);
        } else {
            System.out.println("没找到");
        }
    }
}
结果:找到了,元素下标为:1

这个程序比较简单,只要依次去比较数组元素的值和我们需要的值是否相等,就能得出答案

2. 二分查找

前情提要:数组必须是有序的

在这里插入图片描述

public static int binarySearch(int[] arr,int num) {
        int low = 0;
        int high = arr.length - 1;
        while (low <= high) {
            int mid = (low + high) / 2;
            if(arr[mid]  == num) {
                return mid;
            } else if (arr[mid] > num) {
                high = mid -1;
            } else {
                low = mid + 1;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        int[] arr = new int[]{1,2,3,4,5,6,7,8};
        int ret = binarySearch(arr,3);
        if(ret >= 0) {
            System.out.println("找到了,下标为:" + ret);
        } else {
            System.out.println("没找到");
        }
    }

二分查找还可以使用递归地方法

 public static int binarySearchRecursion(int[] arr,int num,int low,int high){
        //先找终止条件,不需要任何辅助
        if (low > high){
            //区间中没有元素,没找到
            return -1;
        }
        //区间中还有元素
        int mid = (low + high) / 2;
        if (arr[mid] == num) {
            return mid;
        } else if(arr[mid] > num) {
            //要找的元素小于中间值,说明在左区间
            return binarySearchRecursion(arr,num,low,mid - 1);
        }else{
            //要找的元素大于中间值,说明在右区间
            return binarySearchRecursion(arr,num,mid + 1,high);
        }
    }

3. 冒泡排序

升序的冒泡排序就是将”大数“不断往上冒的过程

在这里插入图片描述

public class Note {
    /**
     * 冒泡排序
     */
    public static int[] bubbleSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            boolean isOrder = false;
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if(arr[j] > arr[j+1]) {
                    isOrder = true;
                    int tmp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = tmp;
                }
            }
            if(isOrder = false) {
                break;
            }
        }
        return arr;
    }
    public static void main(String[] args) {
        int[] arr = new int[]{7,5,9,12,10,21,15,13};
        int[] ret = bubbleSort(arr);
        System.out.println(Arrays.toString(ret));
    }
}
结果:[5, 7, 9, 10, 12, 13, 15, 21]

上面图片是一次冒泡排序的过程,n个元素的冒泡排序需要n-1趟冒泡。

上述i的取值小于数组元素长度-1的原因:当数组内只剩一个未排序的元素,代表元素已经有序,不用再进行一趟冒泡。

上述j的取值小于数组元素长度-1 -i 的原因:每当完成一趟冒泡后,最大数已经放到后面有序了,不必要在进行冒泡了。

我们可以设置一个标志位来判断数组内元素是否有序,若已经有序,就可以退出循环了。

数组的toString方法是将数据元素转换成字符串输出。

优化代码是不断进步的过程。

4.给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

输入: [2,2,1]
输出: 1

public static int findOnce(int[] arr) {
        int ret = arr[0];
        for (int i = 1; i < arr.length; i++) {
            ret = ret ^ arr[i];
        }
        return ret;
    }
    public static void main(String[] args) {
        int[] arr = new int[]{2,2,1};
        int ret = findOnce(arr);
        System.out.println(ret);
    }
}

这个代码的核心思想是把数组内所有元素的值整体异或。因为异或是相同为0,不同为1。如果两个相同的数字异或,其结果必定为0。0与任何数字异或就等于数字本身。所以所有元素异或之后,得出的结果就一定是只出现了一次的元素。

这种题目还有其他解法,欢迎大家讨论。

5.多数元素

题目:给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

第一个方法:双引用+计数

public static int findSev(int[] arr) {
        int times = arr.length / 2;
        for (int i = 0; i < arr.length; i++) {
            int count = 0;
            for (int j = 0; j < arr.length; j++) {
                if(arr[i] == arr[j]) {
                    count++;
                }
            }
            if(count > times) {
                return arr[i];
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        int[] arr = new int[]{2,2,1,1,1,2,2};
        int ret = findSev(arr);
        System.out.println(ret);
    }
}
结果:2

我们只需要假设一个元素出现次数大于一半,然后从前往后遍历数组,计数和这个元素相等的元素,判断次数是否大于一半,即可返回值。

第二种方法:排序加输出
因为一个元素的出现次数大于一半,可推出这个元素一定在排序后的中间位置。

public class Note {
    public static int findSev(int[] arr) {
        Arrays.sort(arr);
        return arr[arr.length >> 1];
    }
    public static void main(String[] args) {
        int[] arr = new int[]{2, 2, 1, 1, 1, 2, 2};
        int ret = findSev(arr);
        System.out.println(ret);
    }
}

第三种方法(重点)–摩尔投票法
核心思想:

  1. 我们可以把这想象成投票,我先设置第一个元素为候选者,后面的元素对他投票,自己给自己投一票,所以起始票数为1.

  2. 设计一个计数器去接收票数,如果后面元素和他相等,就是给他投了一票,计数器+1;反之,后面元素和他不相等,就是没给他投票,计数器减一。

  3. 当计数器为0的时候,当前给他投票的元素称为新的候选者。因为当所有人投完票,即正票数和负票数的抵消问题,出现次数大于一半的元素的得票数一定是>=1的,所以计数器等于0的时候,就可以换人了。

  4. 但也有可能这组元素没有多数,也会造成计数器=1的情况。例如arr = [1,2,3]。

  5. 所以我们要对这个元素进行验证,只要遍历一遍数组,和他这个元素相等的元素大于一半,即这个元素就是多数。

public class Note {
    public static int findSev1(int[] arr) {
        int count = 1;
        int candidate = arr[0]; //设置第一个元素为候选者
        for (int i = 1; i < arr.length; i++) {
            if(arr[i] == candidate) {
                count++;
            } else if (count == 0){
                candidate = arr[i];
                count = 1;
            } else{
                count--;
            }
        }
        //验证
        count = 0;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == candidate) {
                count++;
            }
        }
        if (count > (arr.length / 2)) {
            return candidate;
        }
        return -1;
    }
    public static void main(String[] args) {
        int[] arr = new int[]{2, 2, 1, 1, 1, 2, 2};
        int ret = findSev1(arr);
        System.out.println(ret);
    }
}

这种类型的题目可以推广成:在一堆元素中,如果至多选择m个最多的元素,则他的出现次数 > n / (m + 1)

也可以写成:寻找一个数组中出现次数超过1/k的元素,满足要求的元素最多有k-1个

下面写一个m = 2的代码

public static int[] findSev2(int[] arr) {
        int count1 = 1;
        int count2 = 0;
        int candidate1 = arr[0];
        int candidate2 = arr[1];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] == candidate1){
                count1++;
            } else if (arr[i] == candidate2){
                count2++;
            } else if(count1 == 0){
                candidate1 = arr[i];
                count1 = 1;
            } else if(count2 == 0){
                candidate2 = arr[i];
                count2 = 1;
            } else {
                count1--;
                count2--;
            }
        }
        count1 = 0;
        count2 = 0;
        int[] ret = new int[2];
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] == candidate1) {
                count1++;
            } else if (arr[i] == candidate2) {
                count2++;
            }
        }
        if (count1 > arr.length / 3) {
            ret[0] = candidate1;
        }
        if (count2 > arr.length / 3) {
            ret[1] = candidate2;
        }
        return ret;
    }
    public static void main(String[] args) {
        int[] arr = new int[]{1,1,1,2,2,3,3,3};
        int[] ret = findSev2(arr);
        System.out.println(Arrays.toString(ret));
    }
}

6.移除元素(leetcode27)

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]

public int removeElement(int[] nums, int val) {
    int fir = 0;
    int sec = 0;
    while(sec < nums.length){
        if(nums[sec] != val){
            nums[fir] = nums[sec];
            fir ++;
        }
        sec ++;
    }
    return fir;
}

这道题我们可以采用双引用的解法(快慢指针),fir(慢指针),sec(快指针)。我们用快指针找到与删除元素不相等的元素,进行元素覆盖,慢指针用来接收不相等的元素,相当于相等的元素被覆盖了,就实现了元素的删除。

7.删除有序数组中的重复项(leetcode26)

给你一个 升序排列的数组 nums ,请你原地删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持一致 。
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]

public static int removeDuplicates(int[] nums) {
    int n = nums.length;
    if (n == 0){
        return 0;
    }
    int fir = 1;
    int sec = 1;
    while (sec < nums.length){
        if (nums[sec] != nums[sec - 1]){
            nums[fir] = nums[sec];
            fir++;
        }
        sec++;
    }
    return fir;
}

这道题也可以用双引用的方法,我们可以从第二元素开始,因为第一个元素一定是需要留下来的元素,所以我们用fir和sec指向第二个元素。用快慢指针,nums[sec]与前一个元素比较,相等的话向后移,不相等的话,将他覆盖到fir指向的元素。我们只要保证nums[0…fir-1]保留一个重复元素就可以了。

8.移动零(leetcode283)

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

public void moveZeroes(int[] nums) {
    int fir = 0;
    int sec = 0;
    while(sec < nums.length){
        if(nums[sec] != 0){
            nums[fir] = nums[sec];
            fir ++;
        }
        sec ++;
    }
    for(int i = fir;i < sec; i++){
        nums[i] = 0;
    }
}

这道题和上面的解法大致相同,也是双引用,当sec不等于零时,去覆盖掉fir指向的元素。循环结束后,nums[0…fir-1]的元素肯定不等于零。然后我们就可以把后面元素赋值为零就可以了。

9.总结

摩尔投票算法和双引用是常用的基本算法之一,需要深入理解。双引用做多了会习惯的。数组变幻莫测,需要继续努力。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值