2月底前基本熟悉算法,把剑指offer刷完!
T1. 二维数组中的查找
T2. 替换空格
T3. 从尾到头打印链表
T4. 重建二叉树
T5. 用两个栈实现队列
T6. 旋转数组的最小数字
T7. 斐波那契数列
T8. 跳台阶
T9. 变态跳台阶
T10. 矩阵覆盖
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
1.暴力法:
挨个遍历数组,找出该数。
public class Solution {
public boolean Find(int target, int [][] array) {
for(int i=0;i
2.从右上找:
对于右上角的数m,是此行最大,此列最小,
用m和target 判断,每次删除一行或一列。
public class Solution {
public boolean Find(int target, int [][] array) {
int rows = array.length;
if(rows == 0){
return false;
}
int cols = array[0].length;
if(cols == 0){
return false;
}
// 右上
int row = 0; //注意
int col = cols-1; //注意
while(row=0){ //注意
if(array[row][col] < target){
row++; //注意
}else if(array[row][col] > target){
col--; //注意
}else{
return true;
}
}
return false;
}
}
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
1.调用自带函数
public class Solution {
public String replaceSpace(StringBuffer str) {
return str.toString().replace(" ","%20");
}
}
2.挨个比对,建新字符串
public class Solution {
public String replaceSpace(StringBuffer str) {
StringBuilder cm=new StringBuilder();
for(int i=0;i<str.length();i++){
if(str.charAt(i)==' '){
cm.append("%20");
}else{cm.append(str.charAt(i));}
}
return cm.toString();
}
}
String、StringBuffer和StringBuilder的区别
一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。
charAt() 方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1。 语法 public char charAt(int index)
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
1.listNode 是链表,只能从头遍历到尾,但是输出却要求从尾到头,这是典型的"先进后出",我们可以想到栈!
ArrayList 中有个方法是 add(index,value),可以指定 index 位置插入 value 值
所以我们在遍历 listNode 的同时将每个遇到的值插入到 list 的 0 位置,最后输出 listNode 即可得到逆序链表
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;
}
}
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;
}
}
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回
递归思想,每次将左右两颗子树当成新的子树进行处理,中序的左右子树索引很好找,前序的开始结束索引通过计算中序中左右子树的大小来计算,然后递归求解,直到startPre>endPre||startIn>endIn说明子树整理完到。方法每次返回左子树活右子树的根节点
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
return root;
}
//前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) {
if(startPre>endPre||startIn>endIn)
return null;
TreeNode root=new TreeNode(pre[startPre]);
for(int i=startIn;i<=endIn;i++)
if(in[i]==pre[startPre]){
root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1);
root.right=reConstructBinaryTree(pre,i-startIn+startPre+1,endPre,in,i+1,endIn);
break;
}
return root;
}
}
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
栈A用来作入队列,栈B用来出队列,当栈B为空时,栈A全部出栈到栈B,栈B再出栈(即出队列)
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.size()<=0){
> while(stack1.size()!=0)
> {stack2.push(stack1.pop());} }
> return stack2.pop();
也可以是/等于:
> if (stack2.empty()) {
> while (!stack1.empty()) {
> stack2.push(stack1.pop());
> }
> }
> return stack2.pop();
}
}
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
二分法 o(lgn)
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if (array.length == 0)
return 0;
int left = 0;
int right = array.length - 1;
int middle = -1;
while (array[left]>=array[right]) {
if(right-left==1){
middle = right;
break;
}
middle = left + (right - left) / 2;
//判断middle是否在左边递增序列
if (array[middle] >= array[left]) {
left = middle;
}
//判断middle是否在右边递增序列
if (array[middle] <= array[right]) {
right = middle;
}
}
return array[middle];
}
}
二分法查找:是一种在有序数组中查找某一特定元素的搜索算法。
练习实现!!
暴力对比一般情况也可以,但是遇到数据量很大时,时间复杂度很大,o(n)。
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。 n<=39
public class Solution {
public int Fibonacci(int n) {
int a = 0;
int b = 1;
//n减去1是否大于0
while(n-->0){
b = a + b;
a = b - a;
}
return a;
}
}
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
public class Solution {
public int JumpFloor(int target) {
//分类讨论
if(target <= 2){
return target;
}
//斐波那契数列,只考虑n-1和n-2的跳法
int pre1 = 2,pre2 = 1;
for (int i = 3; i <= target; i++){
int cur = pre2 + pre1;
pre2 = pre1;
pre1 = cur;
}
return pre1;
}
}
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
public class Solution {
public int JumpFloorII(int target) {
int sum=1;//当n=1,跳法为1
if(target==0)
return 0;
//易知 f(n)=f(n-1)+f(n-2)+……f(1)
//f(n-1)=f(n-2)+……f(1)
//两式相减得f(n)=2f(n-1)
for(int i=1;i<target;i++)
sum=2*sum;
return sum;
}
}
我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
比如n=3时,2*3的矩形块有3种覆盖方法
public class Solution {
public int RectCover(int target) {
if (target <= 2){
return target;
}
int pre1 = 2; // n 最后使用一块,剩下 n-1 块的写法
int pre2 = 1; // n 最后使用两块,剩下 n-2 块的写法
for (int i = 3; i <= target; i++){
int cur = pre1 + pre2;
pre2 = pre1;
pre1 = cur;
}
return pre1; //相对于 n+1 块来说,第 n 种的方法
}
}