安全性を証明するためにオファー01-10

01.二次元配列を探します

この質問の知識:查找 数组

タイトル説明

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
(设行数为 m, 列数为 n)
public class Solution {
    public boolean Find(int target, int [][] array) {

    }
}

コードA

/**
 * 暴力解答 T(n) = O(m*n)
 * 两重循环遍历数组
 */
public class Solution {
    public boolean Find(int target, int [][] array) {
        for(int i=0; i<array.length; i++){
            for(int j=0; j<array[i].length; j++){
                if(target == array[i][j]){
                    return true;
                }
            }
        }
        return false;
    }
}

コード2

/**
 * 二分法 T(n) = O(m*logn)
 * 由于数组每行都是有序,对每行采用二分查找
 */
public class Solution {
    public boolean Find(int target, int [][] array) {
        for(int i=0; i<array.length; i++){
            int low = 0;
            int high = array[i].length - 1;
            while(low <= high){
                int mid = (low + high)/2;
                if(target > array[i][mid]){
                    low = mid + 1;
                }else if(target < array[i][mid]){
                    high = mid -1;
                }else{ // 找到 target
                    return true;
                }
            }
        }
        return false;
    }
}

コード3

/**
 * 由于数组由上到下,由左到右递增(每个一维数组的长度相同)
 * 选取右上角元素或左下角元素为起始点
 * 以右上角开始为例:初始值 row = 0, col = array[0].length - 1;  
 * if(target < array[row][col]) col--; //(col >= 0)
 * if(target > array[row][col]) row++; //(row <= array.length - 1)
 * 注意越界的终止条件,最终有 T(n) = O(m+n)
*/
public class Solution {
    public boolean Find(int target, int [][] array) {
        int row = 0;
        int col = array[0].length - 1;
        while(row <= array.length - 1 && col >= 0){
            if(target > array[row][col]){
                row++;
            }else if(target < array[row][col]){
                col--;
            }else{
                return true;
            }
        }
        return false;
    }
}

02.スペースを置き換えます

この質問の知識:字符串

タイトル説明

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
public class Solution {
    public String replaceSpace(StringBuffer str) {
        
    }
}

コードA

/**
 * 新建 StringBuffer 用于保存和拼接替换后的字符串结果
 * 其中 StringBuffer/StringBuilder 自带有 length()/charAt() 方法,因此不必转换成字符串再操作
 * 若某位字符为空格,则拼接"%20";否则,拼接原来字符。最后返回 buffer 对象拼接出的结果
 */
public class Solution {
    public String replaceSpace(StringBuffer str) {
        StringBuffer buffer = new StringBuffer();
        for(int i=0; i<str.length(); i++){
            char c = str.charAt(i);
            if(c == ' '){
                buffer.append("%20");
            }else{
                buffer.append(c);
            }
        }
        return buffer.toString();
    }
}

コード2

/**
 * 最简单的使用 String 类的字符串替换方法 replace()
 * String.replace(String target, String replacement)
 */
public class Solution {
    public String replaceSpace(StringBuffer str) {
        return str.toString().replace(" ","%20");
    }
}

コード3

/**
 * 使用 String 类的正则匹配字符串替换方法 replaceAll()
 * String.replaceAll(String regex, String replacement)
 */
public class Solution {
    public String replaceSpace(StringBuffer str) {
        // 正则表达式 \s 匹配任何空白字符,包括空格、制表符、换页符等等。
        // 经测试文本编辑器编写的Java文件的制表符等也会被匹配而转换,因此使用 \s 不太合理,此处单用空格符
        return str.toString().replaceAll(" ","%20");
    }
}

尾のリストから03プリントヘッド

この質問の知識:链表

タイトル説明

输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        
    }
}

コードA

/**
 * 按顺序摘取链表的结点,
 * 采用头插法将结点插入到 ArrayList 中,最后得到倒序的结果
 * 每次插入结点到列表都要将列表的元素后移一位,共插入 n 次(n个元素),故 T(n) = O(n^2)
 */
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();
        while (listNode != null) {
            // 新结点以头插法插入列表中
            list.add(0, listNode.val);
            listNode = listNode.next;
        }
        return list;
    }
}

コード2

/**
 * 此解答使用到栈先进后出的特点:
 * 先按顺序摘取链表结点入栈,
 * 从栈中弹出结点,加入列表,所得结果为倒序排列
 * T(n) = O(n)
 */
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<>();
        Stack<ListNode> stack = new Stack<>();
        
        // 按顺序摘取链表结点压入栈中
        while (listNode != null) {
            stack.push(listNode);
            listNode = listNode.next;
        }
        
        // 从栈中弹出结点,加入列表
        while (!stack.empty()){
            list.add(stack.pop().val);
        }
        return list;
    }
}

コード3

/**
 * 递归调用使用到的方法栈先进后出的特点,
 * 类似二叉树后序遍历的方式进行递归,获得倒序的结果
 * T(n) = O(n)
 */
import java.util.ArrayList;
public class Solution {
    ArrayList<Integer> list = new ArrayList<>();
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        if(listNode != null){
            // 递归调用自身,同时减小问题规模(调用 next)
            printListFromTailToHead(listNode.next);
            // 等到所有递归开始返回时,加入结点
            list.add(listNode.val);
        }
        return list;
    }
}

04.バイナリツリーを再構築

この質問の知識:

タイトル説明

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        
    }
}

分析的思考

バイナリツリートラバーサルは次の特徴があります。

トラバーサル 略称
予約限定! root preLeft preRight 周りのルート
予約限定! inLeft root inRight 左と右のルート
後順 左ポスト postRight root 周りのルート

コード

/**
 * 将先序序列看作 root + preLeft + preRight 三个部分
 * 将中序序列看作 inLeft + root + inRight 三个部分
 *
 * 解题思路:二叉树根节点为初始先序序列的root,使用先序序列首元素拆分中序为左右两个部分,
 * 并根据 inLeft / inRight 的分别元素长度,拆分出 preLeft + preRight 序列 => 递归求解
 *
 * 返回条件:任意子树序列的长度(preLeft || preRight || inLeft || inRight)等于 0 
 */
import java.util.Arrays;
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        // 出口条件:子树序列长度为 0
        if(pre.length == 0 || in.length == 0){
            return null;
        }
        
        // 取先序序列的首元素为二叉树(包括其子树)的根结点 root
        TreeNode node = new TreeNode(pre[0]);
        
        for(int i=0; i<in.length; i++){
            if(pre[0] == in[i]){ // 找到 root 在中序序列中的位置
                // 此处关注中序的长度反求先序的长度:
                // inLeft.length = (i)-(0) = preLeft.length = (i+1)-(1)
                node.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i+1),Arrays.copyOfRange(in, 0, i));
                // 同理,
                // inRight.length = (in.length)-(i+1) = preLeft = (pre.length)-(i+1)
                node.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i+1, pre.length),Arrays.copyOfRange(in, i+1, in.length));
            }
        }
        return node;
    }
}
/**
 * Arrays.copyOfRange()  摘自 JDK8,注意 【】 内的描述
 * @param original the array from which a range is to be copied
 * @param from the initial index of the range to be copied, inclusive
 * @param to the final index of the range to be copied, 【exclusive】.
 * The length of the returned array will be 【 to - from 】.
*/
copyOfRange(T[] original, int from, int to);

2つのスタックを持つ05.キュー

この質問の知識:队列

タイトル説明

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        
    }
    
    public int pop() {
    
    }
}

アイデア解析

順次の動作は以下の変更を受けます:

  1. 2つのスタックは空です
  2. - > stack1ない空の空stack2
  3. - > stack1空stack2空ではありません
  4. - > 2つのスタックが空ではありません
2つのスタックは空です stack1 = [] stack2 = [] ステップ
描画{1,2} stack1 = [2,1] stack2 = [] 2 * stack1.push
ポップ stack1 = [] stack2 = [] リターンはnull


空1 2空ではありません stack1 = [2,1] stack2 = [] ステップ
描画{3,4} stack1 = [4、3、2、1] stack2 = [] 2 * stack1.push
ポップ stack1 = [] stack2 = [2、3、4] 4 * stack1.pop
4 * stack2.push
リターンstack2.pop


1 2ない空の空 stack1 = [] stack2 = [2、3、4] ステップ
描画{5,6} stack1 = [6,5] stack2 = [2、3、4] 2 * stack1.push
ポップ stack1 = [] stack2 = [3、4] stack2.pop戻ります


二つのスタックが空ではありません stack1 = [6,5] stack2 = [2、3、4] ステップ
描画{7,8} stack1 = 8、7、6、5] stack2 = [2、3、4] 2 * stack1.push
ポップ stack1 = [6,5] stack2 = [3、4] stack2.pop戻ります

概要

  1. 状況をスタック:常にstack1.push
  2. スタックケース:
    • そして、stack2にstack1スタックのすべての要素stack2.popを実行します。空のstack2
    • stack2空ではありません。stack2.pop

コード

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        stack1.push(node);
    }
    
    public int pop() {
        // 若 stack2 为空:将所有 stack1 元素入栈到 stack2
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}

06.アレイの回転の最小数

この質問の知識:队列

タイトル説明

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
    
    }
}

コードA

/**
 * 原数组非减排序,最简单的想法是找到其数组的旋转中第一次出现减小的元素
 * T(n) = O(n)
 */
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        // 数组长度为 0,返回 0 
        if(array.length == 0) return 0;
        
        // 返回第一次出现减小元素的值
        for(int i=0; i<array.length-1; i++){
            if(array[i] > array[i+1]){
                return array[i+1];
            }
        }
        
        // 数组中所有元素都相等。返回以第一个元素的值
        return array[0];
    }
}

コード2

/**
 * 出现查找的问题,怎么能少了二分查找呢!
 * T(n) = O(logn)
 * 每次循环排除有序部分,缩小查找区间(剩余一个/两个元素),最终找到分界元素
 *   1. 只有一个元素时,会退出循环,返回 array[low]
 *   2. 只有两个元素时,返回 array[low]
 */
import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length == 0) return 0;
        int low = 0;
        int high = array.length - 1;
        int mid;
        
        // 每次循环排除有序部分,缩小查找区间(剩余一个/两个元素),最终找到分界元素
        while(low < high){ // 只有一个元素时,会退出循环,返回 array[low]
            // 只有两个元素时,返回 array[low]
            if (array[low] < array[high]) return array[low];
            
            mid = (low + high)/2;
            
            // 如果左半数组为有序数组
            if(array[low] < array[mid]){
                low = mid + 1;
            }
            // 如果右半数组为有序数组
            else if(array[mid] < array[high]){
                high = mid;
            }
            // 出现有相等情况
            else{
                low++;
            }   
        }
        
        return array[low];
    }
}

07.フィボナッチ数

この質問の知識:递归

タイトル説明

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39
public class Solution {
    public int Fibonacci(int n) {

    }
}

コードA

/**
 * 暴力递归法(性能超级差,谁试谁知道)
 */
public class Solution {
    
    public int Fibonacci(int n) {
        
        if(n == 0){
            return 0;
        }
        if(n == 1){
            return 1;
        }
        
        return Fibonacci(n-1)+Fibonacci(n-2);
    }
}

コード2

/**
 * 备忘录法 自顶而下,在树的多路归并时有较好的效果
 */
public class Solution {
    
    int[] array = new int[40];
    
    public int Fibonacci(int n) {
        
        array[0] = 0;
        array[1] = 1;
        array[2] = 1;
        
        if(n == 0){
            return 0;
        }
        if(n == 1 || n == 2){
            return 1;
        }

        int result = memo(n);
        return result;
    }
    
    public int memo(int n){
        if(array[n-2] == 0){
            array[n-2] = memo(n-2);
        }
        
        if(array[n-1] == 0){
            array[n-1] = memo(n-1);
        }
        
        return array[n-2] + array[n-1];
    }
}

コード3

/**
 * n 的值较小,因此可以直接将数组求出后,查找对应的 n 值
 */
public class Solution {
    
    public int Fibonacci(int n) {
        int[] array = new int[40];
        array[0] = 0;
        array[1] = 1;
        for(int i=2; i<40; i++){
            array[i] = array[i-1] + array[i-2];
        }
        return array[n];
    }
}

コード4

/**
 * 动态维护 i 和 j 两个值,根据 n 的值返回靠前的 i
 */
public class Solution {
    
    public int Fibonacci(int n) {
        int i = 0;
        int j = 1;
        int tmp;
        while((n--)>0){
            tmp = j;
            j += i;
            i = tmp;
        }
        return i;
    }
}

08.ジャンプ階段

この質問の知識:递归

タイトル説明

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
public class Solution {
    public int JumpFloor(int target) {

    }
}

分析

当 n = 1 时,只有一种跳法 f(1)=1
当 n = 2 时,有两种跳法 f(2)=2
当 n >= 3 时,由于第一次跳的情况只有两种 1步/2步,后序跳的对应的是 f(n-1)/f(n-2)
    此时对于 f(n),有 f(n) = f(n-1)+f(n-2)
由上述分析可知该题所求是斐波那契数列。

コードA

/**
 * 简单递归求解
 */
public class Solution {
    public int JumpFloor(int target) {
        if(target <= 0) return 0;
        if(target == 1) return 1;
        if(target == 2) return 2;
        
        // 对于 target >= 3 的情况
        return JumpFloor(target-1) + JumpFloor(target-2);
    }
}

コード2

/**
 * 分析知道了所求为斐波那契数列 f(n) = f(n-1) + f(n-2)
 * 对递归求解的思路进行优化:
 *   动态维护 i=f(n-2)+f(n-1) 和 j=f(n-1)+f(n) 两个值,最后返回 i
 */
public class Solution {
    public int JumpFloor(int target) {
        if(target <= 0) return 0;
        
        int i = 1;
        int j = 2;
        int tmp;
        /*
         * 当 target = 1,不执行 while, 返回 i=1 {i=f(1)=1,j=f(2)=2}
         * 当 target = 2,执行1遍 while,返回 i=2 {i=f(2)=2,j=f(1)+f(2)=3}
         * 当 target = 3,执行2遍 while,返回 i=3 (i=f(1)+f(2)=3,j=f(2)+f(3)=5)
         * ......
         * 当 target = n,执行(n-1)遍 while,返回 i {i=f(n-2)+f(n-1),j=f(n-1)+f(n)}
         */
        while((--target)>0){
            tmp = j;
            j += i;
            i = tmp;
        }
        
        return i;
    }
}

09.ジャンプステップ2

この質問の知識:贪心

タイトル説明

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
public class Solution {
    public int JumpFloorII(int target) {
        
    }
}

コード

/**
 * 对于最后一个台阶,必定是要被跳上的,
 * 对与前 n 个台阶,每个都有跳上与不跳的跳法,因此跳法共有 2^(n-1) 种
 */
public class Solution {
    public int JumpFloorII(int target) {
        if(target <= 0) return 0;
        return (int)Math.pow(2,target-1);
    }
}

おすすめ

転載: www.cnblogs.com/jianminglin/p/11432697.html