算法题型:数组

目录

一、剑指offer(一)二维数组中的查找

二、剑指offer(三十)连续子数组的最大和

三、剑指offer(三十四)第一次只出现一次的字符

四、剑指offer(三十五):数组中的逆序对

五、剑指offer(三十七):数字在排序数组中出现的次数

六、剑指offer(四十)数组中只出现一次的数字:

七、剑指offer(四十二)和为S的两个数

八、剑指offer(四十五)扑克牌顺子

九、剑指offer(五十)第一个重复的数字


一、剑指offer(一)二维数组中的查找

一、题目描述

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

二、解题思路

/* 思路
* 矩阵是有序的,从左下角来看,向上数字递减,向右数字递增,
* 因此从左下角开始查找,当要查找数字比左下角数字大时。右移
* 要查找数字比左下角数字小时,上移
*/

三、代码

public class Solution {
    public boolean Find(int target, int [][] array) {
        //定义二维数组的长度和深度
        int deep=array.length;
        int length=array[0].length;
 
        //定义长度和深度的下标
        int i=deep-1;
        int j=0;
 
        //方法,选择最左下角的元素arr[i][j],若targrt>该元素,右移;若target<该元素则上移
        while(i>=0&&j<=length-1){
            int element=array[i][j];
            if(target==element){return true;}
            else if(target>element){
                j++;
            }
            else if(target<element){
                i--;
            }
        }
        return false;
 
 
    }
}

二、剑指offer(三十)连续子数组的最大和

连续子数组的最大和
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。
今天测试组开完会后,他又发话了:在古老的一维模式识别中,
常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。
但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?
例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。
给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
 

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int len=array.length;
        int res=Integer.MIN_VALUE;
        for(int i=0;i<len-1;i++){
            int max=array[i];
            for(int j=i+1;j<len;j++){
                res=Math.max(max,res);
                max+=array[j];
            }
            res=Math.max(max,res);
        }
        return res;
    }
}

三、剑指offer(三十四)第一次只出现一次的字符

1.题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).

2.分析:

1.遍历str,把字符存进一个数组中,数组下标和字符ASCII码相关 ,值代表频次。

2.再遍历一遍str,去数组中查找相对应的频次如何,如果=1,则返回,此时就是第一个只出现一次的字符。

public class Solution {
    public int FirstNotRepeatingChar(String str) {
        //a-z:65~90  A-Z:97~122  所以a~Z:共65位
        int [] arr = new int[65];
        for (int i=0;i<str.length();i++){
            int index=(int)str.charAt(i)-65;
            if (arr[index]==0)arr[index]=1;
            else arr[index]++;
        }
       for (int j=0;j<str.length();j++){
           int index=(int)str.charAt(j)-65;
           if (arr[index]==1)return j;
       }
       return -1;
    }
}

四、剑指offer(三十五):数组中的逆序对

1.题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
    对于%50的数据,size<=10^4
    对于%75的数据,size<=10^5
    对于%100的数据,size<=2*10^5
示例1 
输入
复制
1,2,3,4,5,6,7,0
输出
复制
7


/**
 * 2.分析:使用归并排序:https://blog.csdn.net/qq_37768971/article/details/88777334
 * 1.arr[i]<arr[j],不存在逆序对
 * 2.arr[i]>arr[j],逆序对数量增加:mid+1-i
 */
/**
     *   [2,6,4,1]
     *  [2,6][4,1]
     * [2][6][4][1]
     * ————————————————————————
     *  [2,6][1,4]      ->[2,6][*,4]
     *  [1,*,*,*]
     * ————————————————————————
     *  [2,6][*,4]      ->[*,6][*,4]
     *  [1,2,*,*]
     * ————————————————————————
     *  [*,6][*,4]      ->[*,6][*,*]
     *  [1,2,4,*]
     * ————————————————————————
     *  [*,6][*,*]      ->[*,*][*,*]
     *  [1,2,4,6]
     */

public class Sword35_InversePairs {
    
    int count;
    public int InversePairs(int [] array) {
        count=0;
        int len = array.length;
        if (len==0)return 0;
         mergeSort(array,0,len-1);
         return count;
    }
    public void mergeSort(int [] arr , int start , int end ){
        if (start>=end)return;
        int div=start+(end-start)/2;
        mergeSort(arr,start,div);
        mergeSort(arr,div+1,end);
        merge(arr,start,end,div);
    }
    public void merge(int [] arr , int start , int end , int div){
        int len=end-start+1;
        int [] copy = new int[len];
        for(int i=0;i<len;i++){
            copy[i]=arr[i+start];
        }
        int lt = 0;
        int rt =div-start+1;
        int et = lt+end-start;
        //一定要注意lt和rt是动态的,如果需要判断一些固定的东西,不能用!!!
        for (int i=lt;i<=et;i++){
            //此处是lt>div-start,不是lt>rt
            if (lt>div-start){
                arr[start+i]=copy[rt];
                rt++;
            }
            //et是固定的可以用
            else if (rt>et){
                arr[start+i]=copy[lt];
                lt++;
            }
            //注意此处是 div-start+1 而不是rt,因为rt是在变化的
            else if (copy[div-start+1]>=copy[div-start])break;//此处为优化
            else if (copy[lt]<=copy[rt]){
                arr[start+i]=copy[lt];
                lt++;
            }
            else {
                //结果也是用的固定值,而不是rt,所以是(div-start+1)-lt,即右边第一个位置-lt;
                count=(count+div-start+1-lt)%1000000007;//注意此处要%,否则会出错
                arr[start+i]=copy[rt];
                rt++;
            }
        }
    }

    public static void main(String[] args) {
        Sword35_InversePairs s35 = new Sword35_InversePairs();
        int arr[]={364,637,341,406,747,995,234,971,571,219,993,407,416,366,315,301,601,650,418,355,460,505,360,965,516,648,727,667,465,849,455,181,486,149,588,233,144,174,557,67,746,550,474,162,268,142,463,221,882,576,604,739,288,569,256,936,275,401,497,82,935,983,583,523,697,478,147,795,380,973,958,115,773,870,259,655,446,863,735,784,3,671,433,630,425,930,64,266,235,187,284,665,874,80,45,848,38,811,267,575};
//        s35.mergeSort(arr,0,arr.length-1);
//        System.out.print(arr);
//        int arr[] = {64,637,341,406,747,995,234,971,571,219,993,407,416,366,315,301,601,650,418,355,460,505,360,965,516,648,727};
        int count=s35.InversePairs(arr);
        System.out.print(count);
        System.out.println(arr);
    }

}

//自己的解法:O(N^2):会超时
//public class Sword35_InversePairs {
//    public int InversePairs(int [] array) {
//        int len = array.length;
//        int count = 0;
//        for(int i=len-1;i>0;i--){
//            for(int j=i-1;j>=0;j--){
//                if(array[i]<array[j])
//                    count++;
//            }
//        }
//        return count%1000000007;
//    }
//
//    public static void main(String[] args) {
//        Sword35_InversePairs s35 = new Sword35_InversePairs();
//        int arr[] = {1,2,3,4,5,6,7,0};
//        int res=s35.InversePairs(arr);
//        System.out.print(res);
//    }
//}

五、剑指offer(三十七):数字在排序数组中出现的次数

1、题目描述
统计一个数字在排序数组中出现的次数。

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
       int len = array.length;
       int count = 0;
       for(int i=0;i<len;i++){
           if(array[i]>k)break;
           if(array[i]==k)count++;
       }
        return count;
    }
}
public class Sword35_InversePairs {
    
    int count;
    public int InversePairs(int [] array) {
        count=0;
        int len = array.length;
        if (len==0)return 0;
         mergeSort(array,0,len-1);
         return count;
    }
    public void mergeSort(int [] arr , int start , int end ){
        if (start>=end)return;
        int div=start+(end-start)/2;
        mergeSort(arr,start,div);
        mergeSort(arr,div+1,end);
        merge(arr,start,end,div);
    }
    public void merge(int [] arr , int start , int end , int div){
        int len=end-start+1;
        int [] copy = new int[len];
        for(int i=0;i<len;i++){
            copy[i]=arr[i+start];
        }
        int lt = 0;
        int rt =div-start+1;
        int et = lt+end-start;
        //一定要注意lt和rt是动态的,如果需要判断一些固定的东西,不能用!!!
        for (int i=lt;i<=et;i++){
            //此处是lt>div-start,不是lt>rt
            if (lt>div-start){
                arr[start+i]=copy[rt];
                rt++;
            }
            //et是固定的可以用
            else if (rt>et){
                arr[start+i]=copy[lt];
                lt++;
            }
            //注意此处是 div-start+1 而不是rt,因为rt是在变化的
            else if (copy[div-start+1]>=copy[div-start])break;//此处为优化
            else if (copy[lt]<=copy[rt]){
                arr[start+i]=copy[lt];
                lt++;
            }
            else {
                //结果也是用的固定值,而不是rt,所以是(div-start+1)-lt,即右边第一个位置-lt;
                count=(count+div-start+1-lt)%1000000007;//注意此处要%,否则会出错
                arr[start+i]=copy[rt];
                rt++;
            }
        }
    }

    public static void main(String[] args) {
        Sword35_InversePairs s35 = new Sword35_InversePairs();
        int arr[]={364,637,341,406,747,995,234,971,571,219,993,407,416,366,315,301,601,650,418,355,460,505,360,965,516,648,727,667,465,849,455,181,486,149,588,233,144,174,557,67,746,550,474,162,268,142,463,221,882,576,604,739,288,569,256,936,275,401,497,82,935,983,583,523,697,478,147,795,380,973,958,115,773,870,259,655,446,863,735,784,3,671,433,630,425,930,64,266,235,187,284,665,874,80,45,848,38,811,267,575};
//        s35.mergeSort(arr,0,arr.length-1);
//        System.out.print(arr);
//        int arr[] = {64,637,341,406,747,995,234,971,571,219,993,407,416,366,315,301,601,650,418,355,460,505,360,965,516,648,727};
        int count=s35.InversePairs(arr);
        System.out.print(count);
        System.out.println(arr);
    }

}

//自己的解法:O(N^2):会超时
//public class Sword35_InversePairs {
//    public int InversePairs(int [] array) {
//        int len = array.length;
//        int count = 0;
//        for(int i=len-1;i>0;i--){
//            for(int j=i-1;j>=0;j--){
//                if(array[i]<array[j])
//                    count++;
//            }
//        }
//        return count%1000000007;
//    }
//
//    public static void main(String[] args) {
//        Sword35_InversePairs s35 = new Sword35_InversePairs();
//        int arr[] = {1,2,3,4,5,6,7,0};
//        int res=s35.InversePairs(arr);
//        System.out.print(res);
//    }
//}

六、剑指offer(四十)数组中只出现一次的数字:

题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

package IMUHERO;

import java.util.HashMap;

public class Solution40_FindNumsAppearOnce {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int len = array.length;
        HashMap<Integer,Integer> map=new HashMap<>();
        for(int i=0;i<len;i++){
            if(!map.containsKey(array[i])){
                map.put(array[i],1);
            }
            else{
                map.put(array[i],map.get(array[i])+1);
            }
        }
        int [] num = new int[2];
        int i=0;
        for(int key:map.keySet()){
            if (map.get(key)==1){
                num[i]=key;
                i++;
            }
        }
        num1[0]=num[0];
        num2[0]=num[1];
    }
}

其他遍历方法:

for (Map.Entry<Integer,Integer>entry:map.entrySet()){
    if (entry.getValue()==1){
        num[i]=entry.getKey();
        i++;
    }
}

七、剑指offer(四十二)和为S的两个数

1.题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述:
对应每个测试案例,输出两个数,小的先输出。

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer>list=new ArrayList<>();
        if(array.length==0)return list;
        //双指针法:
        int lt=0;//左指针
        int rt=array.length-1;//右指针
        while(lt<rt){
            if(array[lt]+array[rt]<sum){
                lt++;
            }
            else if(array[lt]+array[rt]>sum){
                rt--;
            }
            else{
                   if (list.size()==0){
                       list.add(array[lt]);
                       list.add(array[rt]);
                   }
               else if (array[lt]*array[rt]<list.get(0)*list.get(1)){
                   list.set(0,array[lt]);
                   list.set(1,array[rt]);
               }
                rt--;
            }
        }
        return list;
    }
}

八、剑指offer(四十五)扑克牌顺子

 1.题目描述

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。

2.分析:

1.先把数组排序,方便查看是否顺序

2.统计大小王数量count0 和 需要补的空缺数量count
(1)number[i] == 0 ,count0++;

(2)其他情况(不能越界):如果出现重复元素,直接无法组成,其他情况 count++

3.如果 count0>=count,表示可以组成顺序。

import java.util.Arrays;
public class Solution {
    public boolean isContinuous(int [] numbers) {
        Arrays.sort(numbers);
        int len = numbers.length;
        int count0 = 0;    //大小王的数量
        int count = 0;     //需要用大小王来补的空缺数量
        if(len==0)return false;
        for(int i=0;i<len;i++){
            if(numbers[i]==0){
                count0++;
            }
            //不能出现重复元素,不能越界
            else if(i<len-1){
                //此条不包含等于0的情况,数字相等即表示不能构成顺子
                if(numbers[i]==numbers[i+1]){
                    return false;
                }
                if(numbers[i+1]-numbers[i]>1){
                    count+=numbers[i+1]-numbers[i]-1;//需要插入的个数
                }
            }
        }
        if(count<=count0)return true;//如果大小王的数量比需要补的空缺还要多,表示可以组成顺子
        else return false;
    }
}

九、剑指offer(五十)第一个重复的数字

1.题目:在number[ ]数组中找出第一个重复的数字,如果存在,返回true,如果不存在,返回false

将重复的数字存入deplication[0]

package IMUHERO;

import java.util.HashSet;

public class Sword50_duplicated_number_in_array {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        HashSet<Integer>set = new HashSet<>();
        for (int i=0;i<length;i++){
            if (set.contains(numbers[i])){
                duplication[0]=numbers[i];
                return false;
            }
            else set.add(numbers[i]);
        }
        return true;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

IMUHERO

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

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

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

打赏作者

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

抵扣说明:

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

余额充值