剑指Offer刷题笔记·

题目描述

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

思路:
我们可以根据二维数组的特性,在查找数组里是否有这个整数时,我们可以按行和列去查找。

在这里插入图片描述

我们可以看到以上二维数组的存储则:

行数:int row = (int)array.length;

列数:int col = (int)array[0].length;

1.有了行和列,我们就得在数组中选取一个数去array[ i][ j]和target比较大小,在这里我选取的是右上角的数去和target比较

在这里插入图片描述

a)如果array[ i][ j] > target 则向左走 即 j–

b) 如果array[ i][ j] < target 则向下走 即 i++

注意:

1.我们在选取第一个数来与target比较大小时,一定要选取数组中最靠边的数也就是每行每列最后一个数。因为这样在比较大小时,在选取第二个数比较时不会出现冲突。

2.在比较前可以判断数组是否为空,查找的这个数是否在数组中。

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

题目描述

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

*在剑指offer的的第二题就是关于Stringbuffer的字符串替换问题
此题并不是考察简单的String的库函数的运用,
*对于此题我们首先想到的是将Stringbuffer用tostring的方法变成String,然后使用String的库函数,当然这是有背出题本意的,也是不可取的。

public class Solution {
    public String replaceSpace(StringBuffer str) {
    	/*String s = str.toString();
        String o = s.replace(" ","%20");
        return o;*/
        int num = 0;
        for(int i=0;i<str.length();i++){
            if(str.charAt(i)==' ')
                num++;
        }
        int newLen = str.length() + num*2;
        for(int j=0;j<newLen;j++){
            if(str.charAt(j)==' '){
            str.delete(j,j+1);
            str.insert(j,"%20");
        }
        }
        String o = str.toString();
        return o;
    }
}

题目描述

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

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.Stack;
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        /*ArrayList<Integer> list = new ArrayList<>();
        Digui(list,listNode);
        return list;
    }
    
    public ArrayList<Integer> Digui(ArrayList<Integer> list,ListNode listNode){
        if(listNode!=null){
            Digui(list,listNode.next);
            list.add(listNode.val);
        }
        return list;
    }*/
        ArrayList<Integer> list = new ArrayList<>();
        Stack<Integer> stack = new Stack<>();
        while(listNode!=null){
            stack.push(listNode.val);
            listNode=listNode.next;
        }
        while(!stack.isEmpty()){
            list.add(stack.pop());
        }
        return list;
}
}

题目描述

用两个栈来实现一个队列,完成队列的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) {
        stack1.push(node);
    }
    
    public int pop() {
        while(!stack1.isEmpty()){
            stack2.push(stack1.pop());
        }
        int frist = stack2.pop();
        while(!stack2.isEmpty()){
            stack1.push(stack2.pop());
        }
        return frist;
    }
}

题目描述

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

思路:

  1. n=1时,只有一种跳法f(1)=1;
  2. n=2时,f(2)=2;
  3. n>2时,第一次可以跳1级或者2级台阶,跳1级台阶,剩下的跳法为f(n-1),跳2级台阶,剩下的跳法为f(n-2).因此总的跳法为f(n)=f(n-1)+f(n-2)

因此此题为斐波那契数列 (考察递归问题)

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

变态跳台阶:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。(考察贪心算法)

1.n级台阶,第一步有n种跳法:跳1级、跳2级、到跳n级
2.跳1级,剩下n-1级,则剩下跳法是f(n-1)
3.跳2级,剩下n-2级,则剩下跳法是f(n-2)
4.因此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);
所以f(n)=2^(n-1)

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

题目描述

我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

在这里插入图片描述

假设覆盖2*n个大矩形的方法为f(n)种;

举例n=8,从最左边开始,第一块覆盖的时候有两种选择,横着放或者竖着放,第一次竖着放的时候,后面还剩下2*7个大矩形,剩下的方法为f(7);

第一次如果横着放在左下角,左上角也必须横着放一个,那后面就剩下2*6个大矩形,剩下的方法为f(6);因此f(8)=f(7)+f(6)。因此为斐波那契数列,这里代码没有使用递归,避免了重复计算。

public class Solution {
    public int RectCover(int target) {
      if(target == 1||target == 2) return target;
       else{
           int jumpOne = 1;
           int jumpTwo = 2;
           int count = 0;
           for(int i = 3; i <= target; i++){
               count = jumpOne + jumpTwo ;
               jumpOne = jumpTwo ;
               jumpTwo  = count;
           }
           return count;
       }
    }
}

题目描述

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
在内存负数是用补码表示的(进制转换)

调用API函数
Integer.toBinaryString(n)这是处理问题更符合面向对象的一种方式

public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        String m = Integer.toBinaryString(n);
        for(int i=0;i<m.length();i++){
            if(m.charAt(i)=='1')
                count++;
        }
        return count;
    }
}

题目描述

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

public class Solution {
    public void reOrderArray(int [] array) {
        for(int i=0;i<array.length;i++){
            for(int j=0;j<array.length-1;j++){
                if(array[j]%2==0&&array[j+1]%2==1){
                    int t = array[j+1];
                    array[j+1]=array[j];
                    array[j]=t;
                }
            }
        }
    }
}

输入一个链表,输出该链表中倒数第k个结点。

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
       
       ListNode node = head;
        int len = 0;
        while(node != null){
            node = node.next;
            len++;
        }
        if(k>len) return null;
        node = head;
        int i = 0;
        while(i<len-k){
            node=node.next;
            i++;
        }
        return node;
    }
}

题目:

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

在这里插入图片描述

比如我们知道一二叉树的前序遍历和中序遍历,要求画出这棵树。
前序遍历为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) {
        return rebuild(pre,in,0,pre.length-1,0,in.length-1);
    }
    
    public TreeNode rebuild(int [] pre,int [] in,int pre_left,int pre_right,int in_left,int in_right){
        if(pre_left>pre.length||in_left>in.length||pre_left>pre_right||in_left>in_right) return null;
        
        int value = pre[pre_left];
        TreeNode node = new TreeNode(value);
        
        int count = in_left;
        while(in[count]!=value)
            count++;
        count = count - in_left;
        
        node.left = rebuild(pre,in,pre_left+1,pre_left+count,in_left,in_left+count+1);
        node.right = rebuild(pre,in,pre_left+count+1,pre_right,in_left+count+1,in_right);
        return node;
    }
}

题目描述

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if(root1 == null || root2 == null) return false;
        
        boolean res = check(root1,root2);
        if(res) return true;
        
        if(root1.left!=null){
            res = HasSubtree(root1.left,root2);
            if(res) return true;
        }
        
        if(root1.right!=null){
            res = HasSubtree(root1.right,root2);
            if(res) return true;
        }
        
        return false;
    }
    
    public boolean check(TreeNode root1,TreeNode root2){
        if(root2 == null) return true;
        if(root1 == null) return false;
        if(root1.val!=root2.val) return false;
        return check(root1.left,root2.left)&&check(root1.right,root2.right);
    }
}

题目描述

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。

import java.util.Stack;

public class Solution {
    Stack<Integer> stack = new Stack<>();
    int m = Integer.MAX_VALUE;;
 
    
    public void push(int node) {
        if(node<=m){
            stack.push(m);//大的先入
            m = node;//同时记录当前最小值
        }
        stack.push(node);//后入栈,后者比m大的入栈
    }
    
    public void pop() {
        if(stack.pop()==m){
            m = stack.pop();
        }
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int min() {
        return m;
    }
}

题目描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

从上面的两个例子可以找到判断一个序列是不是栈的弹出序列的规律:如果下一个弹出的数字刚好是栈顶数字,那么直接弹出。如果下一个弹出的数字不在栈顶,我们把压栈序列中还没有入栈的数字压入辅助栈,直到把下一个需要弹出的数字压入栈顶为止。如果所有的数字都压入栈了仍然没有找到下一个弹出的数字,那么该序列就不可能是一个弹出序列。

import java.util.ArrayList;
import java.util.Stack;
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
        boolean flag = false;
        int popAIndex = 0;
        Stack <Integer> stack = new Stack<>();
        for(int i=0;i<pushA.length;i++){
            stack.push(pushA[i]);
            while(!stack.isEmpty()&&popA[popAIndex]==stack.peek()){
                stack.pop();
                popAIndex++;
            }
        }
        
       if(stack.isEmpty()) flag = true;
        return flag;
    }
}

题目描述

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        Queue<TreeNode> quene = new LinkedList<>();
        ArrayList<Integer> list = new ArrayList<>();
        if(root == null) return list;
            quene.offer(root);
        while(!quene.isEmpty()){
            TreeNode node = quene.poll();
            list.add(node.val);
            if(node.left!=null) quene.offer(node.left);
            if(node.right!=null) quene.offer(node.right);
        }
        return list;
    }
}

题目描述

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
思路:
二叉搜索树的特点是,若子树非空,则左子树的节点均小于根节点,右子树的节点均大于根节点。输入是一个整数数组。

在这里插入图片描述

在后序遍历中,最后一个被读的元素是根节点,比如图上的二叉搜索树,后续遍历的结果是[1,6,10,12,9,17,25,18,16],root是16,左边部分是[1,6,10,12,9],右边部分是[17,25,18]

所以先假设这是一个后序遍历序列,则得到root节点。以root为flag从后往前移动(一定要这样),直到前一个元素小于root停下。将原数组分割成两个数组A和B,再递归。每次分割后,如果前面数组A中所有元素都比root小,就是正确的。

一开始没有AC:
错误例:[5,4,3,2,1](只有右子树没有左子树的情况)

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence==null) return false;
        int n = sequence.length;
        if(n==0) return false;
        return Judge(sequence,0,n-1);
    }
    
    public boolean Judge(int [] sequence,int start,int end){
        if(start == end||start > end) return true;
        int root = sequence[end];
        int index = 0;
        boolean Allright = true;// 少加了这个条件,即根节点没有左子树
        for(int i=end;i>=0;i--){
            if(sequence[i]<root){
                index = i;
                Allright = false;
                break;
            }
        }
        for(int i = index;i>=0;i--){// 这里进行左子树的判定,当左子树中有节点大于根节点,就return false
            if(sequence[i]>root&&Allright==false)// 但是若该节点只有右子树,则index没有被赋值,仍是0,这个判定条件就会出问题,所以要加一个条件
                return false;
        }
        return Judge(sequence,start,index)&&Judge(sequence,index+1,end-1);
    }
}

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。
思路分析:首先思考节点值的和为输入的整数,每条路径都一定是从根节点到叶子节点,在数据结构中从根节点到叶子节点的遍历称之为深度优先遍历DFS。因此整个过程可以采用先序遍历方式的DFS,即根节点》左子树》右子树。随后考虑一次遍历完成后的处理,当一次遍历完成后,如果输入整数值恰好等于节点值之和,则输出这条路径并且回退一个节点;如果不等于则直接回退一个节点,即回退到当前节点的父节点,如果该父节点有右孩子,则继续遍历,否则继续回退。考虑回退到根节点,此时如果它有右孩子,则继续遍历,否则整个DFS结束。

数据结构分析:使用java完成整个题目,采用ArrayList<ArrayList>,举个如下的例子,输入整数为22。

在这里插入图片描述

第一次遍历 10,5,4 19<22,因此ArrayList要使用remove函数回退到父节点5上,开始寻找右孩子。

第二次遍历 10,5,7 22=22,此时正好输出这条路径(将路径添加到ArrayList中),10,5,7,回退到父节点5上,此时5的左右孩子都已遍历完成,因此回退到父节点10

第三次遍历 10,12 22=22,此时正好输出这条路径(将路径添加到ArrayList中),10,12,由于12没有左右孩子,因此直接回退到父节点10

由于根节点10左右孩子都已遍历完成,因此整个DFS结束。

需要注意的是不论路径的值是否等于输入整数值,都要回退,即使用remove函数移除路径上的最后一个节点。

import java.util.ArrayList;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    private ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        private ArrayList<Integer> path = new ArrayList<Integer>();
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        if(root == null) return res;
        
        path.add(root.val);
        target = target - root.val;
        
        if(root.left == null && root.right == null && target == 0) 
            res.add(new ArrayList<Integer>(path));
        if(root.left != null && target > 0)
            FindPath(root.left,target);
        if(root.right != null && target > 0)
            FindPath(root.right,target);
        path.remove(path.size()-1);//回退到父节点
        return res;
    }
}

题目描述

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
在这里插入图片描述

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root==null) return 0;
        int left = TreeDepth(root.left);
        int right = TreeDepth(root.right);
        return Math.max(left,right)+1;
        
    }
}

接下一篇。。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值