剑指offer--二维数组中的查找

1 题目描述

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

public class Solution {
    public boolean Find(int target, int [][] array) {
        
    }
}

2 思路+代码实现

2.1 暴力法

2.1.1 分析

挨个遍历数组,如果找到就返回 true

2.1.2 代码

public class Solution {
    public boolean Find(int target, int [][] array) {
        for(int i=0;i<array.length;i++){ //the line length
            for(int j=0;j<array[0].length;j++){ //the column length
                if(array[i][j]==target) return true;
            }
        }
        return false;
    }
}

2.1.3 复杂度
时间复杂度:O(n²)

2.2 从左下找

2.2.1 分析
利用该二维数组的性质:
每一行都按照从左到右递增的顺序排序,
每一列都按照从上到下递增的顺序排序
改变个说法,即对于左下角的值 m,m 是该行最小的数,是该列最大的数
每次将 m 和目标值 target 比较:
当 m < target,由于 m 已经是该列最大的元素,想要更大只有从行考虑,取值右移一位
当 m > target,由于 m 已经是该列最大的元素,想要更小只有从列考虑,取值上移一位
当 m = target,找到该值,返回 true
用某行最小或某列最大与 target 比较,每次可剔除一整行或一整列

2.2.2 代码

public class Solution {
    public boolean Find(int target, int [][] array) {
        int rows=array.length;//the lines length
        int columns=array[0].length;//the columns length
        //the start point
        int row=rows-1;
        int column=0;
        //begin to traverse
        while(row>=0&&column<columns){
            if(array[row][column]>target){
                row--;
            }else if(array[row][column]<target){
                column++;
            }else if(array[row][column]==target){
                return true;
            }
        }
        return false;
    }
}

2.2.3 复杂度
时间复杂度:O(行高+列宽)

2.3 从右上找

思路与从左上一致.右上元素表示当前行的最大值,当前列的最小值

3.扩展:校招真题

3.1 二维数组打印-美团点评2016研发工程师编程题(二)

3.1.1 题目描述
有一个二维数组(n*n),写程序实现从右上角到左下角沿主对角线方向打印。
给定一个二维数组arr及题目中的参数n,请返回结果数组。
测试样例:

[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]],4

返回:[4,3,8,2,7,12,1,6,11,16,5,10,15,9,14,13]

示例代码:

import java.util.*;
public class Printer {
    public int[] arrayPrint(int[][] arr, int n) {
        // write code here
    }
}

3.1.2 思路+代码实现

3.1.2.1 分析
此题题意不难,就是打印一个全排列。
难点在于每次数组遍历的边界确定问题:
1.沿着主对角线打印,所以每次打印之后x与y都要加1,直到x或y超出边界
仔细分析例子后,可以得出:
每次起始点是X不变,Y-1直到Y=0;然后X+1,Y不变,直到X为边界值为止.
每次遍历是X+1,Y+1直到其中到达边界值为止.

3.1.2.2 代码

import java.util.*;
public class Printer {
    public int[] arrayPrint(int[][] arr, int n) {
        //the return array
        int result[]=new int[n*n];
        //the return array pointer
        int index=0;
        //the start point
        int row=0;
        int column=n-1;
        //begin traverse
        while(row<n){
            //Each traversal of the starting point
            int startX=row;
            int startY=column;
            while(startX<n&&startY<n){
                result[index++]=arr[startX++][startY++];
            }
            if(column>0){
                column--;
            }else{
                row++;
            }
        }
        return result;
    }
}

3.2 数组操作-小米2019秋招测试开发笔试题(A)

3.2.1 题目描述
输入一个无序整数数组,调整数组中数字的顺序, 所有偶数位于数组的前半部分,使得所有奇数位于数组的后半部分。
要求时间复杂度为O(n)。
输入描述:

给定无序数组。
长度不超过1000000。

输出描述:

所有偶数位于数组的前半部分,所有奇数位于数组的后半部分。
如果有多个答案可以输出任意一个正确答案。

输入

2 4 5 7 8 1

输出

2 4 8 7 5 1

3.2.2 思路+代码实现

3.2.2.1 分析
双指针,从头和从尾开始比较,偶数放前面,奇数放后面,结束条件

3.2.2.2 代码

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner scan=new Scanner(System.in);
        String str=scan.nextLine();
        String arrSplit[]=str.split(" ");
        int[] arr=new int[arrSplit.length];
        for(int i=0;i<arrSplit.length;i++){
            arr[i]=Integer.valueOf(arrSplit[i]);
        }
        int[]result=test(arr);
        for(int i=0;i<result.length;i++){
            System.out.println(result[i]+" ");
        }
    }
    public static int[] test(int array[]){
        int[] result=new int[array.length];
        int high=array.length-1;
        int low=0;
        int i=0;
        while(high>low){
        if(array[i]%2==0){
            result[low++]=array[i++];
        }else{
            result[high--]=array[i++];
        }
      }
        return result;
    }
}

3.3 回文数组-搜狐2018秋招第二批-技术类试卷

3.3.1 题目描述
对于一个给定的正整数组成的数组 a[] ,如果将 a 倒序后数字的排列与 a 完全相同,我们称这个数组为“回文”的。
例如, [1, 2, 3, 2, 1] 的倒序是他自己,所以是一个回文的数组;而 [1, 2, 3, 1, 2] 的倒序是 [2, 1, 3, 2, 1] ,所以不是一个回文的数组。
对于任意一个正整数数组,如果我们向其中某些特定的位置插入一些正整数,那么我们总是能构造出一个回文的数组。

输入一个正整数组成的数组,要求你插入一些数字,使其变为回文的数组,且数组中所有数字的和尽可能小。输出这个插入后数组中元素的和。

例如,对于数组 [1, 2, 3, 1, 2] 我们可以插入两个 1 将其变为回文的数组 [1, 2, 1, 3, 1, 2, 1] ,这种变换方式数组的总和最小,为 11 ,所以输出为 11

输入描述

输入数据由两行组成: 第一行包含一个正整数 L ,表示数组 a 的长度。 第二行包含 L 个正整数,表示数组 a 。 对于 40% 的数据: 1 < L <= 100 达成条件时需要插入的数字数量不多于 2 个。 对于 100% 的数据: 1 < L <= 1,000 0 < a[i] <= 1,000,000 达成条件时需要插入的数字数量没有限制。

输出描述

输出一个整数,表示通过插入若干个正整数使数组 a 回文后,数组 a 的数字和的最小值。

3.3.2 思路+代码

3.3.2.1 分析
问题可以转换为求回文子序列的最大和,则最终最优解为2 * sum - dp[0][a.length - 1],sum为数组a所有元素的和。

3.3.2.2 代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
/**
 * Dynamic Programming
 *
 * State:
 *   dp[i][j]: 表示a[i],...,a[j]中的回文子序列的最大和
 *
 * Initial State:
 *   dp[i][i] = a[i]
 *
 * State Transition:
 *   if (a[i] == a[j]) dp[i][j] = dp[i + 1][j - 1] + 2 * a[i];
 *   else dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
 *
 * @author wylu
 */
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        String[] strs = br.readLine().split(" ");
        int[] a = new int[n];
        long sum = 0;
        for (int i = 0; i < n; i++) {
            a[i] = Integer.parseInt(strs[i]);
            sum += a[i];
        }
 
        long[][] dp = new long[n][n];
        for (int i = a.length - 1; i >= 0; i--) {
            dp[i][i] = a[i];
            for (int j = i + 1; j < a.length; j++) {
                if (a[i] == a[j]) dp[i][j] = dp[i + 1][j - 1] + 2 * a[i];
                else dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
            }
        }
        System.out.println(2 * sum - dp[0][n - 1]);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值