本文为刷【剑指】备战校招的过程,持续更新
备战虽晚但卷,共勉!
目录,按刷题时间排序
-
- 斐波那契数列
- 用两个栈实现队列
- 二维数组中的查找
- 包含min函数的栈
- 从尾到头打印链表
- 反转链表
- 复杂链表的复制
- 替换空格
- 左旋转字符串
- 数组中重复的数字
- 在排序数组中查找数字 I
- 0~n-1中缺失的数字
- 旋转数组的最小数字
- 第一个只出现一次的字符
- 从上到下打印二叉树I
- 从上到下打印二叉树 II
- 从上到下打印二叉树 III
- 青蛙跳台阶
- 股票的最大利润
- 树的子结构
- 二叉树的镜像
- 对称的二叉树
- 连续子数组的最大和
- 礼物的最大价值
- 把数字翻译成字符串
- 最长不含重复字符的子字符串
- 删除链表的节点
- 链表中倒数第k个节点
- 合并两个排序的链表
- 两个链表的第一个公共节点
- 调整数组顺序使奇数位于偶数前面
- 和为s的两个数字
- 翻转单词顺序
斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
一刷 2021.7.13
-
无脑平推法(渣渣能快速想到的就是这一种)
public class Solution { public int Fibonacci(int n) { int x = 0; int y = 1; if(n<1 || n>39){ return 0; } for(int i = 1;i<n;i++){ y = x+y; x = y-x; } return y; } }
-
递归实现
这个解法是百度发到了斐波那契数列的公式得出的,下面que出斐波那契数列的公式
通过以上公式,就能得出以下的算法啦public class Solution { public int Fibonacci(int n) { if(n==0) return 0; if(n==1) return 1; return Fibonacci(n-1) +Fibonacci(n-2); } }
-
数组实现
用一个数组将每个值存起来,需要的时候直接取,不用重复计算
//第一种
public static int Fibonacci2(int n) {
ArrayList<Integer> list = new ArrayList<>();
list.add(0);
list.add(1);
if(n <= 1){
return list.get(n);
}else{
for(int i = 2; i <= n; i++){
list.add(list.get(i-1)+list.get(i-2));
}
return list.get(n);
}
}
//第二种
class Solution {
public int fib(int n) {
if (n<2) return n;
long[] result = new long[n+1];
result[0] = 0;
result[1] = 1;
for(int i = 2; i<=n;i++){
result[i] = result[i-1]+result[i-2];
result[i] %= (Math.pow(10,9) + 7);
}
return (int)result[n];
}
}
用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
一刷 2021.8.2
ps:渣渣第一眼看完题目,这是什么
过一会联想到上午看《大话数据结构》时书上说到的可以用两个栈怼一起,方便入栈和出栈,但好像跟这道题没关系,不过我联想到的,我觉得至少算学习了,普通且自信
但是还是没有任何解题思路,所以我看了题解视频,网址如下
题解链接
二刷 2021.8.10
游客前一次的经验,这次上手就熟悉了很多,比较顺利
-
思路1
要实现队头删除和队尾插入,首先我们需要知道元素入栈是放在栈底的,且在此思路中,栈1和栈2不会同时存在元素,即若队列不为空,则元素要么全在栈1中,要么全在栈2中队尾插入
若元素都在栈2,栈2的栈顶元素为队列的队头,要实现队尾插入,就要栈2的元素全部入栈1,此时栈1的栈顶元素为队尾,插入即可
若元素都在栈1,此时栈顶元素即为队尾,直接在栈顶插入即可队头删除
若元素起初都入了栈1,要删除栈1的栈底元素,就把栈1的元素全部入栈2.此时栈1的栈底元素就变为了栈2的栈顶元素,只需要将其pop出即可
若元素都在栈2,直接pop出栈2的栈顶元素即可
但要注意校验队列是否为空代码实现
class CQueue { //初始化两个栈 private Stack<Integer> stack1; private Stack<Integer> stack2; //队列 public CQueue() { this.stack1 = new Stack<>(); this.stack2 = new Stack<>(); } //队尾插入 public void appendTail(int value) { //若栈1 while(!stack2.isEmpty()){ stack1.push(stack2.pop()); } stack1.push(value); } //删除头部 public int deleteHead() { while(!stack1.isEmpty()){ stack2.push(stack1.pop()); } if(stack2.isEmpty()){ return -1; } return stack2.pop(); } }
-
思路2
要实现队头删除和队尾插入,首先我们需要知道元素入栈是放在栈底的,在此思路中,栈1和栈2可能同时存在元素队尾插入
在思路1的基础上,不论栈2是否为空,我们将元素直接插入到栈1都是正确的,所以我们在插入时,直接将元素入栈1,此时插入的时间复杂度为O(1)队头删除
由思路1,栈2的栈顶元素永远是队列的头部,所以队头删除只需要将栈2中的栈顶元素pop出来即可
但是删除元素需要对栈2做非空校验
若栈2为空,将栈1中的元素pop出来,放到栈2中,此时栈2的栈顶元素还是是队列的头部,栈2中的栈顶元素pop出来即可
若此时仍为空,即队列为空,return -1即可代码实现
class CQueue { //初始化两个栈 private Stack<Integer> stack1; private Stack<Integer> stack2; //队列 public CQueue() { this.stack1 = new Stack<>(); this.stack2 = new Stack<>(); } //队尾插入 public void appendTail(int value) { stack1.push(value); } //删除头部 public int deleteHead() { if(stack2.isEmpty()){ while(!stack1.isEmpty()){ stack2.push(stack1.pop()); } } if(stack2.isEmpty()){ return -1; }else{ return stack2.pop(); } } } ``
二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
一刷2021.8.4
- 思路一 暴力破解
无脑但好使
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;
int 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;
}
}
- 思路二 线性查找
由题可知:每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序
从右上角开始找
如果当前元素大于目标值,说明当前元素的下边的所有元素都一定大于目标值,因此往下查找不可能找到目标值,往左查找可能找到目标值。如果当前元素小于目标值,说明当前元素的左边的所有元素都一定小于目标值,因此往左查找不可能找到目标值,往下查找可能找到目标值。
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;
int columns = matrix[0].length;
int row = 0;
int 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;
}
}
包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
一刷 2021.8.10
看到的时候是比较蒙的,所以只能看题解
- new 两个栈,数据栈和辅助栈
- 数据栈用来存储入栈的元素,辅助栈用来动态存储栈中的最小元素,且栈中的最小元素恒为辅助栈的栈顶元素
- 当往栈中添加元素的时候,在入数据栈之前先与辅助栈的栈顶元素比较,
- 若要添加元素小于辅助栈的栈顶元素,即此时栈中的最小元素为添加元素,将该元素分别入辅助栈和数据栈即可
- 若要添加元素大于或等于辅助栈的栈顶元素,即此时栈中的最小元素仍为辅助栈的栈顶元素,故将辅助栈的栈顶元素再次入栈,然后将元素入数据栈
- 当删除栈顶元素的时候,由于栈中的最小元素恒为辅助栈的栈顶元素,只需将数据栈和辅助栈的栈顶元素分别出栈即可
- 返回栈顶元素即返回数据栈的栈顶元素
- 返回栈中的最小元素即返回辅助栈的栈顶元素
class MinStack {
//数据栈
private Stack<Integer> dataStack;
//辅助栈,每当有元素进出数据栈,更新辅助栈的最小值
private Stack<Integer> minStack;
public MinStack() {
//初始化数据栈和辅助栈
dataStack = new Stack<>();
minStack = new Stack<>();
}
public void push(int x) {
//如果辅助栈为空或者x小于辅助栈的栈顶元素
//即此时栈的最小元素应为x,则将x入辅助栈
if(minStack.isEmpty() || x<minStack.peek()){
minStack.push(x);
}else{
//如果x大于等于辅助栈的栈顶元素,则将栈顶元素再次入栈
minStack.push(minStack.peek());
}
//将x入数据栈
dataStack.push(x);
}
//删除元素只需将数据栈和辅助栈的栈顶元素分别出栈即可
public void pop() {
dataStack.pop();
minStack.pop();