1. 二维数组中的查找
题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
// 解法一:220ms 暴力n^2
public class Solution {
public boolean Find(int target, int [][] array) {
int x = array.length, y = array[0].length;
boolean flag = false;
for(int i = x - 1;i >= 0;i--) {
for(int j = y - 1;j >= 0;j--) {
if(array[i][j] == target) {
flag = true;
break;
}
}
}
return flag;
}
}
// 解法二:182ms 把每一行看成有序递增的数组,利用二分查找,通过遍历每一行得到答案,时间复杂度是nlognpublic class Solution {
public boolean Find(int target, int [][] array) {
if(array == null || array.length == 0 || array[0].length == 0) {
return false;
}
int row = array.length, col = array[0].length;
for(int i = 0;i < row;i++) {
// 每一行进行二分 int left = 0, right = col - 1;
while(left <= right) {
int mid = (left + right) / 2;
if(array[i][mid] > target) {
right = mid - 1;
} else if(array[i][mid] < target) {
left = mid + 1;
} else {
return true;
}
}
}
return false;
}
}
/*解法三:195ms 利用二维数组由上到下,由左到右递增的规律,那么选取右上角或者左下角的元素a[row][col]与target进行比较,当target小于元素a[row][col]时,那么target必定在元素a所在行的左边,即col--;当target大于元素a[row][col]时,那么target必定在元素a所在列的下边,即row++;*/
public class Solution {
public boolean Find(int target, int [][] array) {
if(array == null || array.length == 0 || array[0].length == 0) {
return false;
}
int row = array.length, col = array[0].length;
int r = 0, c = col - 1;//从右上角开始 while(r < row && c >= 0) {
if(array[r][c] > target) {
c--;
} else if(array[r][c] < target) {
r++;
} else {
return true;
}
}
return false;
}
}
2. 替换空格
题目:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
// 解法一:20ms 常规解法public class Solution {
public String replaceSpace(StringBuffer str) {
StringBuilder tempStr = new StringBuilder();
for(int i = 0;i < str.length();i++) {
if(str.charAt(i) == ' ') {
tempStr.append("%20");
} else {
tempStr.append(str.charAt(i));
}
}
return tempStr.toString();
}
}
3. 从尾到头打印链表
题目:输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
// 解法一:运行时间:20ms 占用内存:9252k// 利用递归求解/*** public class ListNode {* int val;* ListNode next = null;** ListNode(int val) {* this.val = val;* }* }**/
import java.util.ArrayList;
public class Solution {
public ArrayList printListFromTailToHead(ListNode listNode) {
ArrayList resList = new ArrayList();
if(listNode == null) {
return resList;
} else {
return returnListFromTailToHead(listNode);
}
}
public ArrayList returnListFromTailToHead(ListNode listNode) {
ArrayList tempList;
if(listNode.next == null) {
tempList = new ArrayList();
tempList.add(listNode.val);
} else {
tempList = returnListFromTailToHead(listNode.next);
tempList.add(listNode.val);
}
return tempList;
}
}
// 解法二: 超简洁版,运行时间:30ms 占用内存:9340k// 相当于也是递归,然后输出/*** public class ListNode {* int val;* ListNode next = null;** ListNode(int val) {* this.val = val;* }* }**/
import java.util.ArrayList;
public class Solution {
ArrayList resList = new ArrayList();
public ArrayList printListFromTailToHead(ListNode listNode) {
if(listNode != null) {
printListFromTailToHead(listNode.next);
resList.add(listNode.val);
}
return resList;
}
}
//解法三:运行时间:27ms 占用内存:9248k//先获得正序的ArrayList,在借助Collections.reverse()反转链表/*** public class ListNode {* int val;* ListNode next = null;** ListNode(int val) {* this.val = val;* }* }**/
import java.util.ArrayList;
import java.util.Collections;
public class Solution {
ArrayList resList = new ArrayList();
public ArrayList printListFromTailToHead(ListNode listNode) {
while(listNode != null) {
resList.add(listNode.val);
listNode = listNode.next;
}
Collections.reverse(resList);
return resList;
}
}
4. 重建二叉树
题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
// 解法一: 运行时间:190ms 占用内存:22148k/* 代码里有注释基本思想是递归分治,利用了先序遍历和中序遍历的特性。先序遍历的特性是:第一个访问的结点一定是根结点。(即数组第一个值是根结点的值)中序遍历的特性是:数组中的一个值(现结点),其左边的全部是现结点的左子树中的值,右边的全部是现结点的右子树中的值。*/
/*** 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 buildTree(pre, 0, pre.length - 1, in, 0, in.length - 1);
}
// 根据pre[pre_l, pre_r], in[in_l, in_r] 构建二叉树 public TreeNode buildTree(int [] pre, int pre_l, int pre_r, int [] in, int in_l, int in_r) {
// 边界处理 if(pre_l > pre_r) {
return null;
}
if(pre_l == pre_r) return new TreeNode(pre[pre_l]);
//找到前序遍历的根节点,在中序遍历中的位置 int index = find(in, in_l, in_r, pre[pre_l]);
//左子树长度 int len_l = index - in_l;
// 构建根节点 TreeNode root = new TreeNode(pre[pre_l]);
// 根据pre[pre_l + 1, pre_l + len_l], in[in_l, index - 1]构建左子树 TreeNode leftNode = buildTree(pre, pre_l+1, pre_l+len_l, in, in_l, index - 1);
// 根据pre[pre_l + len_l + 1, pre_r], in[index+1, in_r]构建右子树 TreeNode rightNode = buildTree(pre, pre_l+len_l+1, pre_r, in, index+1, in_r);
root.left = leftNode;
root.right = rightNode;
return root;
}
// 找到根节点在中序遍历中的位置 public int find(int [] in, int in_l, int in_r, int target) {
int len = in.length;
for(int i = 0;i < len;i++) {
if(target == in[i]) {
return i;
}
}
return -1;
}
}
//解法二: 运行时间:179ms 占用内存:22980k 超简洁版本/*** Definition for binary tree* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val = x; }* }*/
import java.util.*;
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre.length == 0) {
return null;
}
TreeNode root = new TreeNode(pre[0]);
if(pre.length == 1) {
return root;
}
for(int i = 0;i < in.length;i++) {
if(pre[0] == in[i]) { //根节点在中序遍历中的位置 //Arrays.copyOfRange() 是左闭右开区间 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));
}
}
return root;
}
}
5. 用两个栈实现队列
题目:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
// 解法一: 运行时间:15ms 占用内存:9404k
import java.util.Stack;
public class Solution {
Stack stack1 = new Stack();
Stack stack2 = new Stack();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.empty()) {
while(!stack1.empty()) {
int x = stack1.peek();
stack1.pop();
stack2.push(x);
}
}
int node = stack2.peek();
stack2.pop();
return node;
}
}
6. 旋转数组的最小数字
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
//解法一:运行时间:383ms 占用内存:30980k//排序import java.util.ArrayList;
import java.util.Collections;
import java.util.*;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array.length == 0) {
return 0;
}
Arrays.sort(array);
return array[0];
}
}
//解法二:运行时间:324ms 占用内存:28728k//变向二分法,分析在注释里,也可以看这个https://www.nowcoder.com/questionTerminal/9f3231a991af4f55b95579b44b7a01ba//~~~这种二分法,时间小了点,内存小了点import java.util.ArrayList;
import java.util.Collections;
import java.util.*;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array.length == 0) {
return 0;
}
int left = 0, right = array.length - 1;
int mid = 0;
// 确保left在左边递增序列,right在右边递增序列 while(array[left] >= array[right]) {
//边界条件 if(right - left == 1) {
mid = right;
break;
}
mid = (left + right) / 2;
//三者相等的情况下,无法判断,只能顺序查找 if(array[mid] == array[left] && array[mid] == array[right]){
return minNumOfArray(array, left, right + 1);
}
// 中间元素位于前面的递增子数组 // 此时最小元素位于中间元素的后面 if(array[mid] >= array[left]) {
left = mid;
}
// 中间元素位于后面的递增子数组 // 此时最小元素位于中间元素的前面 else {
right = mid;
}
}
return array[mid];
}
/*** 获得一段数组中的最小值*/
public int minNumOfArray(int [] array, int left, int right) {
int minNum = array[left];
for(int i = left;i < right;i++) {
if(array[i] < minNum) {
minNum = array[i];
}
}
return minNum;
}
}
// 解法三:运行时间:354ms 占用内存:28200k// 也是二分,上面那种方法的简洁版本,不需要定义一个辅助函数import java.util.ArrayList;
import java.util.Collections;
import java.util.*;
public class Solution {
public int minNumberInRotateArray(int [] array) {
int low = 0 ; int high = array.length - 1;
while(low < high){
int mid = (low + high) / 2;
if(array[mid] > array[low]){
low = mid;
} else if(array[mid] == array[low]){
low = low + 1;
}else{
high = mid;
}
}
return array[low];
}
}
7.斐波那契数列
题目:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39
// 解法一:运行时间:1348ms 占用内存:9408k// 暴力递归,最简单的public class Solution {
public int Fibonacci(int n) {
if(n == 0 || n == 1) {
return n;
}
return Fibonacci(n-1) + Fibonacci(n-2);
}
}
// 解法二: 运行时间:1408ms 占用内存:9300k// 存下中间值(按道理应该比上面那种方法快的)import java.util.*;
public class Solution {
int [] fibo = new int[40];
public int fibona(int n) {
if(n == 0 || n == 1) {
return n;
}
if(fibo[n] != 0) {
return fibo[n];
} else {
return fibona(n - 1) + fibona(n - 2);
}
}
public int Fibonacci(int n) {
Arrays.fill(fibo, 0);
fibo[1] = 1;
return fibona(n);
}
}
// 解法三: O(n) 运行时间:16ms 占用内存:9324k// 循环求,复杂度O(n),递归的好处是简单,但是做了很多无用的操作import java.util.*;
public class Solution {
int [] fibo = new int[40];
public int Fibonacci(int n) {
fibo[1] = 1;
fibo[0] = 0;
for(int i = 2;i <= n;i++) {
fibo[i] = fibo[i - 1] + fibo[i - 2];
}
return fibo[n];
}
}
// 解法四:O(logn) 运行时间:14ms 占用内存:9412k// 矩阵快速幂:https://blog.csdn.net/aaakkk_1996/article/details/87927108/** O(logN)解法:由f(n) = f(n-1) + f(n-2),可以知道* [f(n),f(n-1)] = [f(n-1),f(n-2)] * {[1,1],[1,0]}* 所以最后化简为:[f(n),f(n-1)] = [1,1] * {[1,1],[1,0]}^(n-2)* 所以这里的核心是:* 1.矩阵的乘法* 2.矩阵快速幂(因为如果不用快速幂的算法,时间复杂度也只能达到O(N))*/
public class Solution {
public int Fibonacci(int n) {
if(n == 0) {
return 0;
} else if(n == 2 || n == 1) {
return 1;
}
int [][]base = {{1, 1}, {1, 0}};
int [][]res = matrixPower(base, n - 2);
return res[0][0] + res[0][1];
}
//矩阵乘法 public int [][] matrixMultiply(int [][] m1, int [][]m2) {
int [][] m = new int[m1.length][m2[0].length];
for(int i = 0;i < m1.length;i++) {
for(int j = 0;j < m2[0].length;j++) {
for(int k = 0;k < m2.length;k++) {
m[i][j] += m1[i][k] * m2[k][j];
}
}
}
return m;
}
/** 矩阵的快速幂:* 1.假如不是矩阵,叫你求m^n,如何做到O(logn)?答案就是整数的快速幂:* 假如不会溢出,如10^75,把75用用二进制表示:1001011,那么对应的就是:* 10^75 = 10^64*10^8*10^2*10* 2.把整数换成矩阵,是一样的*/
public int [][] matrixPower(int [][] m, int p) {
int [][]res = new int [m.length][m[0].length];
//初始化res微单位矩阵 for(int i = 0;i < res.length;i++) {
res[i][i] = 1;
}
// tmp矩阵保存m^n, n是2的指数倍,初始化为m int [][] tmp = m;
//快速幂 for(;p != 0;p >>= 1) {
if((p&1) != 0) {
res = matrixMultiply(res, tmp);
}
tmp = matrixMultiply(tmp, tmp);
}
return res;
}
}
8. 跳台阶
题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
//解法:多列几个,找找规律,依旧是斐波那契,直接按题目7来就行了
9. 变态跳台阶
题目:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
//解法一:运行时间:13ms 占用内存:9412k// record[i] = 2 * record[i - 1]public class Solution {
public static final int N = 100001;
public int JumpFloorII(int target) {
if(target == 1) {
return 1;
}
int a = 1;
for(int i = 2;i <= target;i++) {
a *= 2;
}
return a;
}
}
// 解法二: 运行时间:21ms 占用内存:9228k// 相当于求2^(n - 1)这就可以直接库函数public class Solution {
public static final int N = 100001;
public int JumpFloorII(int target) {
if(target == 1) {
return 1;
}
return (int)Math.pow(2, target - 1);
}
}
// 解法三:运行时间:22ms 占用内存:11620k// 快速幂public class Solution {
public static final int N = 100001;
public int JumpFloorII(int target) {
if(target == 1) {
return 1;
}
return ksm(2, target - 1);
}
//快速幂 public static int ksm(int a, int p) {
int res = 1;
int tmp = a;
for(; p != 0;p >>= 1) {
if((p&1) != 0) {
res *= tmp;
}
tmp *= tmp;
}
return res;
}
}
10.矩形覆盖
题目:我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
// 解法: 斐波那契数列,参考7、8题
// 1.递归做 运行时间:311ms 占用内存:9276k// 2.存中间值,防止重复求值:运行时间:50ms 占用内存:25736k// 3.矩阵快速幂 运行时间:22ms 占用内存:9364k
11.二进制中1的个数
题目:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
//解法一:运行时间:15ms 占用内存:9344k//转二进制字符串,遍历字符串public class Solution {
public int NumberOf1(int n) {
int num = 0;
String str = Integer.toBinaryString(n);
for(int i = 0;i < str.length();i++) {
if(str.charAt(i) == '1') {
num++;
}
}
return num;
}
}
// 解法二:运行时间:20ms 占用内存:9340k// 最优解法/*如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。*/
public class Solution {
public int NumberOf1(int n) {
int num = 0;
while(n != 0) {
num ++;
n = n & (n - 1);
}
return num;
}
}
其他题目,因为篇幅太长就不贴了,大家可以看我的CSDN刷题报告:剑指offer66题blog.csdn.net