Java~算法训练之数组篇

目录

二维数组中的查找

解题思路

具体代码

旋转数组的最小数字

解题思路 

具体代码

调整数组顺序使奇数位于偶数前面 

解题思路 

具体代码

 数组中出现次数超过一半的数字

 解题思路1

解题思路2 

解题思路3 


二维数组中的查找

题目链接二维数组中的查找

题目描述

在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

[

[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]

]

给定 target = 7,返回 true。

给定 target = 3,返回 false。

数据范围:矩阵的长宽满足 0 <= n, m <= 500 , 矩阵中的值满足 0 <= val <= 10^9
进阶:空间复杂度 O(1),时间复杂度 O(n+m)

解题思路

一种简单的做法即利用两个for循环遍历一遍二维数组,找出是否存在待查找数字即,但其时间复杂度太高了,O(n^2)。

根据题目描述可知,二维数组的每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序,因此可以根据这个二维数组的规律来写出一种时间复杂度为O(m+n)的解法.

例如上述二维数组,要查找的数字为10,根据其规律,从右上角的9开始比较,具体比较步骤为:①首先与9比较,大于9,则继续与12进行比较;

②12大于10,因此根据规律肯定在12左边,则与9进行比较;

③9小于10,因此肯定在9的下方,再与10进行比较;

④10等于10,成功查找到,返回true

具体代码

public class Solution {
    public boolean Find(int target, int [][] array) {
        int rows = array.length;
        if(rows == 0) {
            return false;
        }
        int cols = array[0].length;
        if(cols == 0) {
            return false;
        }
        int row = rows - 1;
        int col = 0;
        while(col < cols && row >= 0) {
            if(array[row][col] > target) {
                row--;
            }else if(array[row][col] < target) {
                col++;
            }else {
                return true;
            }
        }
        return false;
    }
}

旋转数组的最小数字

题目链接旋转数组的最小数字

题目描述

有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

数据范围:1≤n≤10000,数组中任意元素的值: 0≤val≤10000

要求:空间复杂度:O(1),时间复杂度:O(logn)

解题思路 

思路①:当然是遍历了,遍历整个数组求出最小值即可,但是时间复杂度是O(n),显然不满足题目要求.

思路②:根据题意,原始数组是非降序的,其旋转后就会出现递减的,而引起递减的数字,就是这个数组中的最小值.

可以利用二分思想来解决该问题,定义出首尾下标,left=0,right=arr.length-1,mid为(left+right)/2.因为是非递减数组旋转,因此旋转后数组分为两个部分,左部分是递增,右部分也是递增,只有两个部分相交的地方是递减,因此右边那个数据就是这个旋转数组的最小值

具体步骤

①如果mid下标的值大于left下标的值,说明这个最小值在mid的右半部分,因此,令left = mid,直接舍去左半部分;

②如果mid下标的值小于left下标的值,说明这个最小值在mid的右半部分,因此,令left = mid,直接舍去左半部分;

 

这样不断判断,不断缩小范围,left永远在前半部分,而right永远在后半部分,当right和left相邻时,right下标的值即为整个数组的最小值.

③但是也可能存在一种比较特殊的情况,题目中要求的是非降序数组,因此也可能出现有相等的值,此时就需要以线性探测的方式来寻找最小值了

就比如上面这种情况,此时就无法通过上面的方法来寻找最小值,就需要以线性探测的方式来寻找.

具体代码

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array == null || array.length == 0) {
            return 0;
        }
        int left = 0;
        int right = array.length - 1;
        int mid = 0;
        while(array[left] >= array[right]) {
            if(right - left == 1) {
                mid = right;
                break;
            }
            if(array[mid] == array[left] && array[mid] == array[right]) {
                int ret = array[left];
                for(int i = left + 1; i < right; i++) {
                    if(ret > array[i]) {
                        ret = array[i];
                    }
                }
                return ret;
            }
            mid = left + ((right - left) >> 1);
            if(array[mid] >= array[left]) {
                left = mid;
            }else {
                right = mid;
            }
        }
        return array[mid];
    }
}

调整数组顺序使奇数位于偶数前面 

题目链接:调整数组顺序使奇数位于偶数前面

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

解题思路 

根据题目要求,要保证相对位置不变,因此不能利用双指针分别从左和右依次寻找奇数和偶数,并对其进行交换.对于该问题的解决可以借鉴插入排序思想.

例如下图数据

定义一个k来表示目前已经有几个奇数放在了应该放的位置,在之后的移动数据中,就不需要移动这几个数据,以上述数据为例,第一次交换后,k = 1,未发生位置交换.

 在第二次寻找到奇数,此时j = 2, k = 1,根据代码思想,其将3之前的偶数全部后移一位,将空闲的位置再赋值为3即可.

一直根据这种思想来进行交换,最终即可得到所求的结果

具体代码

public class Solution {
    public void reOrderArray(int [] array) {
        int k = 0;
        for(int i = 0; i < array.length; i++) {
            if((array[i] & 1) == 1) {
                int tmp = array[i];
                int j = i;
                while(j > k) {
                    array[j] = array[j-1];
                    j--;
                }
                array[k++] = tmp;
            }
        }
    }
}

 数组中出现次数超过一半的数字

题目链接数组中出现次数超过一半的数字

题目描述

给一个长度为 n 的数组,数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

例如输入一个长度为9的数组[1,2,3,2,2,2,5,4,2]。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。

数据范围:0<=n<=50000,数组中元素的值 0≤val≤10000

要求:空间复杂度:O(1),时间复杂度:O(n)

 

 解题思路1

可以直接利用map来统计,建立<数字,次数>的映射关系,统计每个字符出现的次数,找到出现次数超过一半的那个,输出数字即可

import java.util.*;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array == null || array.length == 0) {
            return 0;
        }
        Map<Integer, Integer> map = new HashMap<>();
        for(int i = 0; i < array.length; i++) {
            if(map.containsKey(array[i])) {
                int count = map.get(array[i]);
                count++;
                map.put(array[i], count);
            }else {
                map.put(array[i], 1);
            }
            if(map.get(array[i]) > array.length/2) {
                return array[i];
            }
        }
        return 0;
    }
}

解题思路2 

先对数组进行排序,其出现次数超过一半的数字一定是在中间位置,因为题目保证一定会有结果,因此直接返回数组中间位置的数字即可.

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array == null || array.length == 0) {
            return 0;
        }
        Arrays.sort(array);
        return array[array.length/2];
    }
}

解题思路3 

要求的是出现次数超过一半的数字,这种解题思路的大致思想是每次删除掉相同的两个数字,到最后剩下的那个数字就是要求的数字,最终再将这个数字代入到原数组遍历一遍统计一下出现次数,保证结果的正确性.以下列数组为例:

 

例如当i处于下图位置时,此时target和times分别为

 

当for循环遍历结束后,此时此时target和times分别为

 此时可以确定,出现次数超过一半的数字就是2,再将其代入原数组中遍历一遍确定一下结果即可

import java.util.*;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array == null || array.length == 0) {
            return 0;
        }
        int target = array[0];
        int times = 1;
        for(int i = 1; i < array.length; i++) {
            if(times == 0) {
                target = array[i];
                times = 1;
            }else if(array[i] == target) {
                times++;
            }else {
                times--;
            }
        }
        times = 0;
        for(int i = 0; i < array.length; i++) {
            if(target == array[i]) {
                times++;
            }
        }
        return times > array.length/2 ? target : 0;
    }
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li_yizYa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值