A 参考资料
- 书本《剑指offer(第二版)》
JZ01 二位数组中的查找
1.题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]给定 target = 7,返回 true。给定 target = 3,返回 false。
示例1
输入7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]返回值true说明存在7,返回true
2.暴力求解
当然最容易想到的是暴力求解,就是一个个查找,如果找到就返回true,没找到就返回false,代码很简单,没什么可说的。
public boolean findNumberIn2DArray(int[][] matrix, int target) {if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {return false;}int rows = matrix.length, columns = matrix[0].length;for (int i = 0; i < rows; i++) {for (int j = 0; j < columns; j++) {if (matrix[i][j] == target) {return true;}}}return false;}
3.线性查找
题中说了每行都是递增的,每列也是递增的。所以我们查找的时候可以利用这个特性,如果我们从左上角开始找,当目标值target大于当前值的时候,我们需要往更大的找,但这个时候无论往右找还是往下找都是比当前值大,所以我们无法确定该往哪个方向找。同理右下角也一样,所以我们只能从右上角或者左下角开始找。我们就用上面的数据当target等于23的时候从右上角开始找,来画个图看一下
从右上角开始找有个方便的地方就是他左边的都是比他小的,他下边的都是比他大的,如果target大于当前值我们就往下边找,如果target小于当前值我们就往左边找,来看下代码。
public boolean Find(int target, int[][] array) {if (array == null || array.length == 0 || array[0].length == 0) {return false;}int rows = array.length, col = array[0].length;//从第0行col - 1列开始查找,也就是第1行最后一列的那个数字开始int row = 0;int column = col - 1;while (row < rows && column >= 0) {//num表示当前值int num = array[row][column];if (num == target) {//如果找到直接返回return true;} else if (num > target) {//到前面查找column--;} else {//到下面查找row++;}}return false;}
当然从左下角查找也是可以的,因为左下角右边的值是比他大的,上边的值是比他小的,也能区分,代码和上面差不多,来看下
public boolean Find(int target, int[][] array) {if (array == null || array.length == 0 || array[0].length == 0) {return false;}int rows = array.length, col = array[0].length;int row = rows - 1;int column = 0;while (row >= 0 && column < col) {int num = array[row][column];if (num == target) {//如果找到直接返回return true;} else if (num > target) {//往上面找row--;} else {//往右边找column++;}}return false;}
JZ02 替换空格
1.题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
示例1
输入"We Are Happy"返回值"We%20Are%20Happy"
2.String.replaceAll()
直接调用String的封装方法
public String replaceSpace (String s) {if (s == null || "".equals(s))return s;return s.replaceAll(" ", "%20");}
3.Spring.split()
常规方法,拆分加替换
public String replaceSpace (String s) {StringBuilder sb = new StringBuilder();if (s == null || "".equals(s))return s;String[] strs = s.split("");for (String str : strs) {if (" ".equals(str))sb.append("%20");elsesb.append(str);}return sb.toString();}
4.传统解法
虽然Java版的参数传入是String类,不像C语言直接就是字符数组,也不能直接在数组后面扩容。但是其实可以利用StringBuffer类来实现这样的操作。具体的思路的话书上已经讲解了,这里主要是贴出程序来介绍Java的实现方式。
pubilc class Solution{pubilc String replaceSpace(String s){if(s == null){ return null; }StringBuffer str = new StringBuffer(s);int length = str.length();int spaceNum = 0;for(int i = 0;i < length;i++){ if(str.charAt(i) == ' '){ spaceNum++; } }int oldStr = length - 1;length += 2 * spaceNum;int newStr = length - 1;str.setLength(length);while(spaceNum > 0 && newStr >= 0){char ch = str.charAt(oldStr--);if(ch == ' '){str.setCharAt(newStr--,'0');str.setCharAt(newStr--,'2');str.setCharAt(newStr--,'%');spaceNum--;}else{ str.setCharAt(newStr--,ch); }}return str.toString();}}
JZ03 从尾到头打印链表
1.题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
示例1
输入{67,0,24,58}返回值[58,24,0,67]
2.解题
一、非递归
1. 分析
listNode 是链表,只能从头遍历到尾,但是输出却要求从尾到头,这是典型的”先进后出”,我们可以想到栈!ArrayList 中有个方法是 add(index,value),可以指定 index 位置插入 value 值所以我们在遍历 listNode 的同时将每个遇到的值插入到 list 的 0 位置,最后输出 listNode 即可得到逆序链表
2. 代码
import java.util.*;public class Solution {public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {ArrayList<Integer> list = new ArrayList<>();ListNode tmp = listNode;while(tmp!=null){list.add(0,tmp.val);tmp = tmp.next;}return list;}}
二、递归
1. 分析
既然非递归都实现了,那么我们也可以利用递归,借助系统的”栈”帮忙打印
2. 代码
import java.util.*;public class Solution {ArrayList<Integer> list = new ArrayList();public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {if(listNode!=null){printListFromTailToHead(listNode.next);list.add(listNode.val);}return list;}}
3. 复杂度
时间复杂度:
空间复杂度:
1.Java—链表ListNode
2.ArrayList详解,看这篇就够了
3.Java List.add()方法
4.牛客编程题高级数据结构格式说明(待整理)
JZ04 重建二叉树
1.题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
示例1
输入{67,0,24,58}返回值[58,24,0,67]
2.解题一
关键是:利用前序序列根节点在前找到根节点,用根节点去中序序列划分成两部分,左部分是左子树,右部分是右子树。再利用子树长度去前序序列把前序序列中的左右子树找出来,同时可以找出根节点。递归进行此步骤,如果子树长度为0,则不需要生成子问题。
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}}/*** 关键是:利用前序序列根节点在前找到根节点,用根节点去中序序列划分成两部分,左部分是左子树,右部分是右子树。再利用子树长度去前序序列把前序序列中的左右子树找出来,同时可以找出根节点。递归进行此步骤,如果子树长度为0,则不需要生成子问题。*/public class Solution {public TreeNode reConstructBinaryTree(int[] pre, int[] in) {TreeNode root = new TreeNode(pre[0]);build(root, pre, 0, pre.length, in, 0, in.length);return root;}/*** 递归和二分思想,将问题不断划分,直到问题容易解决。* 做法是:对于一个根节点,先去中序序列中找到根节点的值所在位置,利用这个位置分成2部分,左部分的中序序列长度即为前序序列中左部分的中序序列长度,右部分亦然。* 然后开始生成子问题,如果序列长度为0则不需要生成子问题。否则:利用前序序列第一个元素为根节点的性质生成根节点,然后构造子问题。* @param root 根节点* @param pre 前序序列 范围是[pleft,pright)* @param in 中序序列 范围是[ileft,iright)*/public void build(TreeNode root, int[] pre, int pleft, int pright, int[] in, int ileft, int iright) {int i;for (i = ileft; i < iright; i++) {if (in[i] == root.val) {//从中序序列寻找根节点的位置break;}}int t = i - ileft;if (t > 0) {//子树长度为0时不必生成子问题root.left = new TreeNode(pre[pleft + 1]);build(root.left, pre, pleft + 1, pleft + 1 + t, in, ileft, i);}if (pright - pleft - 1 - t > 0) {root.right = new TreeNode(pre[pleft + 1 + t]);build(root.right, pre, pleft + 1 + t, pright, in, i + 1, iright);}}}
目前报错数组越界,应该是索引没有完全找对,这个方法确实最好把图画出来
3.解题二
思路:
前序遍历:根→左→右中序遍历:左→根→右
- 由前序遍历序列pre={1,2,4,7,3,5,6,8}可知根结点是1;
- 则在中序遍历序列in={4,7,2,1,5,3,8,6}中找到1,便可知1所在位置的左侧是左子树,1所在位置的右侧是右子树;
- 递归调用:将左子树和右子树分别看成一颗树,将其前序遍历序列、中序遍历序列分别传入到该方法中,便可得到左子树的根结点、右子树的根结点。此时需要用第一步得到的根结点连接它们;
- 递归调用的终止条件:直到传入数组为空,说明已经没有节点,直接返回null。
import java.util.Arrays;public class Solution {public TreeNode reConstructBinaryTree(int [] pre,int [] in) {//递归调用的终止条件if(pre.length == 0 || in.length == 0){return null;}//由前序遍历得到该二叉树的根结点TreeNode root = new TreeNode(pre[0]);//在中序遍历中找根结点位置,进行左右子树的划分for(int i = 0; i < in.length; i++){if(root.val == in[i]) {//将左子树看成一棵二叉树调用该方法,可以得到左子树根结点,即上面根结点的左子结点root.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i));//将右子树看成一棵二叉树调用该方法,可以得到右子树根结点,即上面根结点的右子结点root.right = reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,pre.length),Arrays.copyOfRange(in,i+1,in.length));//找到根结点位置便跳出循环break;}}//返回根结点return root;}}
1.关于Java中的Arrays.copyOfRange()方法
JZ04 重建二叉树
1.题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
示例1
输入{67,0,24,58}返回值[58,24,0,67]
2.解题一
3.解题二
JZ04 重建二叉树
1.题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
示例1
输入{67,0,24,58}返回值[58,24,0,67]
2.解题一
3.解题二
JZ04 重建二叉树
1.题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
示例1
输入{67,0,24,58}返回值[58,24,0,67]
