Leetcode_剑指Offer
1、 数组中重复的数字(03、Easy)
1)题目要求
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
2)我的解法
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set=new HashSet<Integer>();
for(int i=0;i<nums.length;i++){
if(set.contains(nums[i]))return nums[i];
set.add(nums[i]);
}
return 0;
}
}
3)其他解法
如果没有重复数字,那么正常排序后,数字i应该在下标为i的位置,所以思路是重头扫描数组,遇到下标为i的数字如果不是i的话,(假设为m),那么我们就拿与下标m的数字交换。在交换过程中,如果有重复的数字发生,那么终止返回ture
class Solution {
public int findRepeatNumber(int[] nums) {
int temp;
for(int i=0;i<nums.length;i++){
while (nums[i]!=i){
if(nums[i]==nums[nums[i]]){
return nums[i];
}
temp=nums[i];
nums[i]=nums[temp];
nums[temp]=temp;
}
}
return -1;
}
}
作者:derrick_sun
链接: link
来源:力扣(LeetCode)
4)自己的优化代码
class Solution {
public int findRepeatNumber(int[] nums) {
int temp=0;
for(int i=0;i<nums.length;i++){
if(nums[i]==i)continue;
if(nums[nums[i]]==nums[i])return nums[i];
temp=nums[i];
nums[i]=nums[nums[i]];
nums[temp]=temp;
}
return 0;
}
}
5)学到的东西
题目中 “在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内” 这句话很重要,
这意味着一个萝卜一个坑
2、二维数组中的查找(04、Easy)
1)题目要求
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
限制:
0 <= n <= 1000
0 <= m <= 1000
2)我的解法
1、暴力
2、超时(递归本身比较慢(需要增加栈空间))
class Solution {
public boolean dfs(int i,int j,int[][] matrix, int target){
if(i>=matrix.length||i<0||j<0||j>=matrix[0].length)return false;
if(matrix[i][j]==target)return true;
if(matrix[i][j]>target)return false;
if(i==j){
if(i<matrix.length-1&&j<matrix[0].length-1&&matrix[i+1][j+1]<=target)return dfs(i+1,j+1,matrix,target);
else return dfs(0,j+1,matrix,target)||dfs(i+1,0,matrix,target);
}
else return dfs(i,j+1,matrix,target)||dfs(i+1,j,matrix,target);
}
public boolean findNumberIn2DArray(int[][] matrix, int target) {
int i=0,j=0;
return dfs(0,0,matrix,target);
}
}
3)其他解法
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
int i = matrix.length - 1, j = 0;
while(i >= 0 && j < matrix[0].length)
{
if(matrix[i][j] > target) i--;
else if(matrix[i][j] < target) j++;
else return true;
}
return false;
}
}
作者:jyd
链接: link
来源:力扣(LeetCode)
2、
class Solution {
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;
int row = 0, column = columns - 1;
while (row < rows && column >= 0) {
int num = matrix[row][column];
if (num == target) {
return true;
} else if (num > target) {
column--;
} else {
row++;
}
}
return false;
}
}
作者:LeetCode-Solution
链接:link
来源:力扣(LeetCode)
3、
4)自己的优化代码
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if(matrix.length==0)return false;
int i=0,j=matrix[0].length-1;
while(i<matrix.length&&j>=0){
if(matrix[i][j]==target)return true;
if(matrix[i][j]<target)i++;
else j--;
}
return false;
}
}
5)学到的东西
找规律、把二维数组斜过来看
3、替换空格(05、Easy)
1)题目要求
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1:
输入:s = “We are happy.”
输出:“We%20are%20happy.”
限制:
0 <= s 的长度 <= 10000
2)我的解法
class Solution {
public String replaceSpace(String s) {
StringBuilder sb=new StringBuilder();
for(char c:s.toCharArray()){
if(c!=' ')sb.append(c);
else sb.append("%20");
}
return sb.toString();
}
}
3)其他解法
class Solution {
public String replaceSpace(String s) {
int length = s.length();
char[] array = new char[length * 3];
int size = 0;
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (c == ' ') {
array[size++] = '%';
array[size++] = '2';
array[size++] = '0';
} else {
array[size++] = c;
}
}
String newStr = new String(array, 0, size);
return newStr;
}
}
作者:LeetCode-Solution
链接: link
来源:力扣(LeetCode)
4)自己的优化代码
class Solution {
public String replaceSpace(String s) {
char[] res=new char[s.length()*3];
int index=0;
for(char c:s.toCharArray()){
if(c!=' ')res[index++]=c;
else {
res[index++]='%';
res[index++]='2';
res[index++]='0';
}
}
return new String(res,0,index);//最后一个参数为长度
}
}
5)学到的东西
String :toCharArray()
new String(char[] arr,int start,int length)
4、从尾到头打印链表(06、Easy)
1)题目要求
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2]
输出:[2,3,1]
限制:
0 <= 链表长度 <= 10000
2)我的解法
class Solution {
public int[] reversePrint(ListNode head) {
int len=0;
List<Integer> temp=new ArrayList<>();
while(head!=null){
len++;
temp.add(head.val);
head=head.next;
}
int[] res=new int[temp.size()];
int index=0;
for(int i=temp.size()-1;i>=0;i--)res[index++]=temp.get(i);
return res;
}
}
3)其他解法
1、栈
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
Stack<ListNode> stack = new Stack<ListNode>();
ListNode temp = head;
while (temp != null) {
stack.push(temp);
temp = temp.next;
}
int size = stack.size();
int[] print = new int[size];
for (int i = 0; i < size; i++) {
print[i] = stack.pop().val;
}
return print;
}
}
作者:LeetCode-Solution
链接:link
来源:力扣(LeetCode)
2、空间复杂度:O(1)
public int[] reversePrint(ListNode head) {
//先获取链表长度,创建对应长度数组
ListNode currNode = head;
int len = 0;
while(currNode != null){
len ++;
currNode = currNode.next;
}
int[] result = new int[len];
//再次遍历链表,将值倒序填充至结果数组
currNode = head;
while(currNode != null){
result[len - 1] = currNode.val;
len --;
currNode = currNode.next;
}
return result;
}
作者:编程小学生
链接:官方题解评论区
来源:力扣(LeetCode)
3、递归
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
if(!head)
return {};
vector<int> a=reversePrint(head->next);
a.push_back(head->val);
return a;
}
};
作者:格戮
链接:官方题解评论区
来源:力扣(LeetCode)
4)自己的优化代码
class Solution {
public int[] reversePrint(ListNode head) {
int len=0;
ListNode temp=head;
while(temp!=null){
len++;
temp=temp.next;
}
int[] res=new int[len];
while(head!=null){
res[--len]=head.val;
head=head.next;
}
return res;
}
}
5)学到的东西
尽量降低时间复杂度和空间复杂度
5、重建二叉树(07、Medium)
1)题目要求
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/
9 20
/
15 7
限制:
0 <= 节点个数 <= 5000
2)我的解法
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length==0||inorder.length==0)return null;
TreeNode t=new TreeNode(preorder[0]);
int i=0;
for(i=0;i<inorder.length;i++){
if(inorder[i]==preorder[0])break;//在中序数组中找到当前结点
}
if(i==0||preorder.length<=1)t.left=null;
else t.left=buildTree(Arrays.copyOfRange(preorder,1,i+1),Arrays.copyOfRange(inorder,0,i));
if(inorder.length<=i+1||preorder.length<=i+1)t.right=null;
else t.right=buildTree(Arrays.copyOfRange(preorder,i+1,preorder.length),Arrays.copyOfRange(inorder,i+1,inorder.length));
return t;
}
}
3)其他解法
1、递归
class Solution {
HashMap<Integer, Integer> dic = new HashMap<>();
int[] po;
public TreeNode buildTree(int[] preorder, int[] inorder) {
po = preorder;
for(int i = 0; i < inorder.length; i++)
dic.put(inorder[i], i);
return recur(0, 0, inorder.length - 1);
}
TreeNode recur(int pre_root, int in_left, int in_right) {
if(in_left > in_right) return null;
TreeNode root = new TreeNode(po[pre_root]);
int i = dic.get(po[pre_root]);
root.left = recur(pre_root + 1, in_left, i - 1);
root.right = recur(pre_root + i - in_left + 1, i + 1, in_right);
return root;
}
}
作者:jyd
链接:link
来源:力扣(LeetCode)
2、迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder == null || preorder.length == 0) {
return null;
}
TreeNode root = new TreeNode(preorder[0]);
int length = preorder.length;
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
int inorderIndex = 0;
for (int i = 1; i < length; i++) {
int preorderVal = preorder[i];
TreeNode node = stack.peek();
if (node.val != inorder[inorderIndex]) {
node.left = new TreeNode(preorderVal);
stack.push(node.left);
} else {
while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
node = stack.pop();
inorderIndex++;
}
node.right = new TreeNode(preorderVal);
stack.push(node.right);
}
}
return root;
}
}
作者:LeetCode-Solution
链接:link
来源:力扣(LeetCode)
4)自己的优化代码
class Solution {
Map<Integer,Integer> map=new HashMap<>();
public TreeNode build(int[] preorder, int[] inorder,int preStart,int preEnd, int inoStart,int inoEnd){
if(preEnd<preStart||inoEnd<inoStart)return null;
TreeNode t=new TreeNode(preorder[preStart]);
int i=0;
i=map.get(preorder[preStart]);
t.left=build(preorder,inorder,preStart+1,preStart+(i-inoStart),inoStart,i-1);
t.right=build(preorder,inorder,preStart+(i-inoStart)+1,preEnd,i+1,inoEnd);
return t;
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
for(int i=0;i<inorder.length;i++)map.put(inorder[i],i);
return build(preorder,inorder,0,preorder.length-1,0,inorder.length-1);
}
}
5)学到的东西
利用开始位置和结束位置移动窗口
Arrays.copyOfRange(int[] original,int from,int to) [from,to)
to==from时返回的是空数组,小于则会报错
List 中的 list.subList(int from,int to) 会改变list
优化算法,不一定要优化思想本身。比如本题用map可以节省在中序数组中找节点的时间
6、用两个栈实现队列(09、Easy)
1)题目要求
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:
输入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
提示:
1 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用
2)我的解法
class CQueue {
Stack<Integer> s1,s2;
public CQueue() {
s1=new Stack<Integer>();
s2=new Stack<Integer>();
}
public void appendTail(int value) {
s1.push(value);
}
public int deleteHead() {
while(!s1.empty()){
s2.push(s1.pop());
}
int returnValue=-1;
if(!s2.empty())returnValue=s2.pop();
while(!s2.empty()){
s1.push(s2.pop());
}
return returnValue;
}
}
3)其他解法
class CQueue {
LinkedList<Integer> A, B;
public CQueue() {
A = new LinkedList<Integer>();
B = new LinkedList<Integer>();
}
public void appendTail(int value) {
A.addLast(value);
}
public int deleteHead() {
if(!B.isEmpty()) return B.removeLast();
if(A.isEmpty()) return -1;
while(!A.isEmpty())
B.addLast(A.removeLast());
return B.removeLast();
}
}
作者:jyd
链接:link
来源:力扣(LeetCode)
4)自己的优化代码
class CQueue {
Stack<Integer> s1,s2;
public CQueue() {
s1=new Stack<Integer>();//负责入
s2=new Stack<Integer>();//负责出
}
public void appendTail(int value) {
s1.push(value);
}
public int deleteHead() {
if(!s2.empty())return s2.pop();
//当s2里删完了,再把s1里的搬过来
if(s1.empty())return -1;
while(!s1.empty())s2.push(s1.pop());
return s2.pop();
}
}
5)学到的东西
一个栈负责入,一个负责出
s2里的删完了,再把s1里的搬过来
7、斐波那契数列(10-1,Easy)
1)题目要求
2)我的解法
1、超时
class Solution {
public int fib(int n) {
if(n==0||n==1)return n;
return (fib(n-1)+fib(n-2))%1000000007;
}
}
2、动态规划
class Solution {
int[] dp=new int[101];
public Solution(){
for(int i=0;i<=100;i++){
if(i==0||i==1)dp[i]=i;
else dp[i]=(dp[i-1]+dp[i-2])%1000000007;
}
}
public int fib(int n) {
return dp[n];
}
}
3)其他解法
class Solution {
public int fib(int n) {
int a = 0, b = 1, sum;
for(int i = 0; i < n; i++){
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
}
}
作者:jyd
链接: link
来源:力扣(LeetCode)
4)自己的优化代码
class Solution {
public int fib(int n) {
if(n==0||n==1)return n;
int a=0,b=1,sum=0;//a即dp[n-2],b即dp[n-1]
for(int i=2;i<=n;i++){
sum=(a+b)%1000000007;//sum即dp[i]
a=b;
b=sum;
}
return sum;
}
}
5)学到的东西
递归一般都可以用动态规划
8、青蛙跳台阶问题(10-2、Easy)
1)题目要求
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:2
示例 2:
输入:n = 7
输出:21
示例 3:
输入:n = 0
输出:1
提示:
0 <= n <= 100
2)我的解法
class Solution {
public int numWays(int n) {
int a=1,b=1,sum=1;
for(int i=2;i<=n;i++){
sum=(a+b)%1000000007;
a=b;
b=sum;
}
return sum;
}
}
3)其他解法
class Solution {
public int numWays(int n) {
int a = 1, b = 1, sum;
for(int i = 0; i < n; i++){
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
}
}
作者:jyd
链接: link
来源:力扣(LeetCode)
4)自己的优化代码
class Solution {
public int numWays(int n) {
int a=1,b=1,sum=1;
for(int i=2;i<=n;i++){
sum=(a+b)%1000000007;
a=b;
b=sum;
}
return sum;
}
}
5)学到的东西
动态规划
9、 旋转数组的最小数字(11、Easy)
1)题目要求
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
示例 2:
输入:[2,2,2,0,1]
输出:0
2)我的解法
1、
class Solution {
public int minArray(int[] numbers) {
for(int i=0;i<numbers.length;i++){
if(i>0&&numbers[i]<numbers[i-1])return numbers[i];
}
return numbers[0];
}
}
2、O(n//2) (不是二分)
class Solution {
public int minArray(int[] numbers) {
int i=0,j=numbers.length-1;
while(i<j){
if(numbers[i+1]<numbers[i])return numbers[i+1];
if(numbers[j]<numbers[j-1])return numbers[j];
i++;j--;
}
return numbers[0];
}
}
3)其他解法
1、
class Solution {
public int minArray(int[] numbers) {
int i = 0, j = numbers.length - 1;
while (i < j) {
int m = (i + j) / 2;
if (numbers[m] > numbers[j]) i = m + 1;
else if (numbers[m] < numbers[j]) j = m;
else j--;
}
return numbers[i];
}
}
2、
class Solution {
public int minArray(int[] numbers) {
int i = 0, j = numbers.length - 1;
while (i < j) {
int m = (i + j) / 2;
if (numbers[m] > numbers[j]) i = m + 1;
else if (numbers[m] < numbers[j]) j = m;
else {
int x = i;
for(int k = i + 1; k < j; k++) {
if(numbers[k] < numbers[x]) x = k;
}
return numbers[x];
}
}
return numbers[i];
}
}
作者:jyd
链接:link
来源:力扣(LeetCode)
4)自己的优化代码
class Solution {
public int minArray(int[] numbers) {
int i=0,j=numbers.length-1,m=0;
while(i<j){
m=i+(j-i)/2;//可避免在i和j较大时,int溢出
if(numbers[m]>numbers[j])i=m+1;//和number[j]比较是因为旋转点要么没有,要么在右边
else if(numbers[m]<numbers[j])j=m;
else{//如[2,2,2,1,2,2],只能一点点缩小范围
j--;
}
}
return numbers[i];
}
}
5)学到的东西
二分法的细节
(复习leetcode_入门_二分法)
10、矩阵中的路径(12、Medium)
1)题目要求
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
示例 1:
输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
示例 2:
输入:board = [[“a”,“b”],[“c”,“d”]], word = “abcd”
输出:false
提示:
1 <= board.length <= 200
1 <= board[i].length <= 200
注意:本题与主站 79 题相同:https://leetcode-cn.com/problems/word-search/
2)我的解法
class Solution {
public boolean find(int i,int j,char[][] board,String word,int index){
if(i<0||i>=board.length||j<0||j>=board[0].length)return false;
if(board[i][j]==word.charAt(index)){
board[i][j]='*';
if(index==word.length()-1)return true;
boolean tag=find(i-1,j,board,word,index+1)||find(i+1,j,board,word,index+1)||
find(i,j-1,board,word,index+1)||find(i,j+1,board,word,index+1);
board[i][j]=word.charAt(index);//需复原
return tag;
}
else return false;
}
public boolean exist(char[][] board, String word) {
for(int i=0;i<board.length;i++){
for(int j=0;j<board[0].length;j++){
if(board[i][j]==word.charAt(0)){
if(find(i,j,board,word,0))return true;
}
}
}
return false;
}
}
3)其他解法
class Solution {
public boolean exist(char[][] board, String word) {
char[] words = word.toCharArray();
for(int i = 0; i < board.length; i++) {
for(int j = 0; j < board[0].length; j++) {
if(dfs(board, words, i, j, 0)) return true;
}
}
return false;
}
boolean dfs(char[][] board, char[] word, int i, int j, int k) {
if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;
if(k == word.length - 1) return true;
char tmp = board[i][j];
board[i][j] = '/';
boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) ||
dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
board[i][j] = tmp;
return res;
}
}
作者:jyd
链接:link
来源:力扣(LeetCode)
4)自己的优化代码
class Solution {
public boolean find(int i,int j,char[][] board,String word,int index){
if(i<0||i>=board.length||j<0||j>=board[0].length)return false;
if(board[i][j]==word.charAt(index)){
board[i][j]='*';
if(index==word.length()-1)return true;
boolean tag=find(i-1,j,board,word,index+1)||find(i+1,j,board,word,index+1)||
find(i,j-1,board,word,index+1)||find(i,j+1,board,word,index+1);
board[i][j]=word.charAt(index);
return tag;
}
else return false;
}
public boolean exist(char[][] board, String word) {
for(int i=0;i<board.length;i++){
for(int j=0;j<board[0].length;j++){
if(board[i][j]==word.charAt(0)){
if(find(i,j,board,word,0))return true;
}
}
}
return false;
}
}
5)学到的东西
深度优先