剑指offer——二维数组中的查找,替换空格,从尾到头打印链表,重建二叉树,用两个栈来实现一个队列,旋转数组的最小数字,斐波拉契数列,跳台阶,变态跳台阶,矩阵覆盖

二维数组中的查找

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

暴力法

	public boolean Find(int target, int [][] array) {
        boolean result=false;
        for(int i=0;i<array.length;i++)
            for(int j=0;j<array[i].length;j++)
                if(array[i][j]==target)
                    return true;
        return result;
    }

    时间复杂度O(MN)(M为行,N为列)

二分查找法

	public boolean Find(int target, int [][] array) {
        boolean result=false;
        for(int i=0;i<array.length;i++){
            int mid,left=0,right=array[i].length-1;
            while(left<=right){
                mid=(left+right)/2;
                if(array[i][mid]==target)
                    return true;
                if(array[i][mid]>target)
                    right=mid-1;
                else
                    left=mid+1;
            }
        }
        return result;
    }

    时间复杂度O(M*logN)

左下/右上法

    分析:
    利用该二维数组的性质:
    每一行都按照从左到右递增的顺序排序,
    每一列都按照从上到下递增的顺序排序

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

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

    时间复杂度O(M+N)

替换空格

    请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

调用自带函数

	public String replaceSpace(StringBuffer str) {
        return str.toString().replace(" ","%20");
    }

暴力法

public class Solution {
    public String replaceSpace(StringBuffer str) {
        StringBuffer result = new StringBuffer();
        for(int i = 0; i < str.length(); i++){
            if(str.charAt(i) != ' ')
                result.append(str.charAt(i));
            else
                result.append("%20");
        }
        return result.toString();
    }
}

从尾到头打印链表

    输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

public class ListNode {
    int val;
    ListNode next = null;
    ListNode(int val) {
        this.val = val;
    }
}

递归法

import java.util.ArrayList;
public class Solution {
    ArrayList<Integer> list = new ArrayList<Integer>();
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        if(listNode == null)
            return list;
        printListFromTailToHead(listNode.next);
        list.add(listNode.val);
        return list;
    }
}

重建二叉树

    输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

public class TreeNode {
	int val;
	TreeNode left;
	TreeNode right;
	TreeNode(int x) { val = x; }
}

递归法

  1. 前序的第一个元素为根节点,在中序序列中找到根节点的位置
  2. 中序序列中,根节点左边的是左子树,右边的是右子树
  3. 然后找到左子树的根节点,递归上面的步骤
public class Solution {
    
    public int preStart = 0;  //记录前序序列中遍历到了哪个位置
    
    public TreeNode reConstructBinaryTree(int [] pre,int [] in, int preEnd, int startIndex, int endIndex) {
        if(startIndex > endIndex || preStart > preEnd)
            return null;
        int index;
        for(index = startIndex; index <= endIndex && pre[preStart] != in[index]; index++);  //找到前序元素在中序的位置
        TreeNode node = new TreeNode(pre[preStart++]);
        node.left = reConstructBinaryTree(pre, in, preEnd, startIndex, index - 1);  //左子树
        node.right = reConstructBinaryTree(pre, in, preEnd, index + 1, endIndex);  //右子树
        return node;
    }
    
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        return reConstructBinaryTree(pre, in, pre.length - 1, 0, in.length - 1);
    }
}

用两个栈来实现一个队列

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

解法一

    入队列时,将stack2的值全部弹出到stack1,然后入栈stack1
    出队列时,将stack1的值全部弹出到stack2,然后将stack2栈顶元素弹出

import java.util.Stack;
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        while(!stack2.isEmpty()){
            stack1.push(stack2.pop());
        }
        stack1.push(node);
    }
    
    public int pop() {
        while(!stack1.isEmpty()){
            stack2.push(stack1.pop());
        }
        return stack2.pop();
    }
}

解法二

    入队列时,直接入栈stack1
    出队列时,如果stack2为空,则将stack1的值全部弹出到stack2,然后将stack2栈顶元素弹出

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() {
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}

旋转数组的最小数字

    把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
    输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
    例如数组{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) {
        if(array.length == 0)
            return 0;
        for(int i = array.length - 1; i >= 0; i--){
            if(array[i -1] > array[i])
                return array[i];
        }
        return array[0];
    }
}

二分查找

答案解析

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array.length == 0)
            return 0;
        int left = 0, mid = 0, right = array.length - 1;
        while(left < right){
        // 子数组是非递减的数组,10111
            if(array[left] < array[right])
                break;
            mid = left + (right - left) /2;
            if(array[mid] > array[right])
                left = mid + 1;
            else if (array[left] > array[mid])
                right = mid;
            else
                left++;
        }
        return array[left];
    }
}

斐波拉契数列

    大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。n<=39
    斐波拉契数列:0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269。

迭代法

public class Solution {
    public int Fibonacci(int n) {
        if(n == 0)
            return 0;
        if(n == 1)
            return 1;
        int a = 0, b = 1, result = 0;
        for(int i = 2; i <= n; i++){
            result = a + b;
            a = b;
            b = result;
        }
        return b;
    }
}

递归法

public class Solution {
    public int Fibonacci(int n) {
        if(n == 0 || n == 1)
            return n;
        return Fibonacci(n - 1) + Fibonacci(n - 2);
    }
}

    尾递归

public class Solution {
    public int Fibonacci(int n) {
        return Fibonacci(n, 0, 1);
    }
    
    public int Fibonacci(int n, int a, int b) {
        if(n == 0)
            return 0;
        if(n == 1)
            return b;
        return Fibonacci(n - 1, b, a + b);
    }
}

动态规划

public class Solution {
    public int Fibonacci(int n) {
        if(n == 0 || n ==1)
            return n;
        int f = 0, g = 1;
        while(n-- > 0){
            g += f;
            f = g - f;
        }
        return f;
    }
}

跳台阶

    一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
    就是一个斐波拉契数列

public class Solution {
    public int JumpFloor(int target) {
        if(target == 1 || target == 2)
            return target;
        return JumpFloor(target - 1) + JumpFloor(target - 2);
    }
}

public class Solution {
    public int JumpFloor(int target) {
        if(target == 1 || target == 2)
            return target;
        int a = 1, b = 2, result = 0;
        for(int i = 3; i <= target; i++){
            result = a + b;
            a = b;
            b = result;
        }
        return b;
    }
}

变态跳台阶

    一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

找规律

    跳1级:1(1种)
    跳2级:1 1,2(2种)
    跳3级:1 1 1,1 2,2 1,3(4种)
    跳4级:1 1 1 1,1 1 2,1 2 1,2 1 1,2 2,1 3,3 1,4(8种)
    跳5级:1 1 1 1 1,1 1 1 2,1 1 2 1,1 2 1 1,2 1 1 1,1 2 2,2 1 2,2 2 1,1 1 3,1 3 1,3 1 1,2 3,3 2,1 4,4 1,5(16种)
    显然,跳第n级有 2(n-1)种跳法

public class Solution {
    public int JumpFloorII(int target) {
        //return (int)Math.pow(2, target - 1);
        return 1 << (target - 1);
    }
}

递归法

    因为n级台阶,第一步有n种跳法:跳1级、跳2级、到跳n级
    跳1级,剩下n-1级,则剩下跳法是f(n-1)
    跳2级,剩下n-2级,则剩下跳法是f(n-2)
    所以f(n)=f(n-1)+f(n-2)+…+f(1)
    因为f(n-1)=f(n-2)+f(n-3)+…+f(1)
    所以f(n)=2*f(n-1)

public class Solution {
    public int JumpFloorII(int target) {
        if(target == 1 || target == 2)
            return target;
        return 2 * JumpFloorII(target - 1);
    }
}

矩阵覆盖

    我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2n的大矩形,总共有多少种方法?比如n=3时,23的矩形块有3种覆盖方法:
在这里插入图片描述
    又是一个斐波拉契数列

public class Solution {
    public int RectCover(int target) {
        if(target <= 2)
            return target;
        return RectCover(target - 1) + RectCover(target - 2);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值