文章目录
程序员面试的经典题库了,记录一下自己的java题解以及一些心得体会,方便自己复习。
参考:
与我的另一篇文章,剑指 Offer(专项突击版)同步更新中。
相关题目
03.数组中重复的数字
class Solution {
public int findRepeatNumber(int[] nums) {
HashSet<Integer> set=new HashSet<>();
for(int i:nums){
if(set.contains(i)) return i;
else set.add(i);
}
return 0;
}
}
04.二维数组中的查找
这题关键是能够将矩阵想象成二叉搜索树的形式,通过逆时针旋转45度,在搜寻的过程中可以根据当前值和target的关系决定方向。
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
int n=matrix.length;
if(n==0) return false;
int m=matrix[0].length;
int i=m-1;
int j=0;
while(i>=0&&j<=n-1){
if(matrix[j][i]>target) i--;
else if(matrix[j][i]<target) j++;
else return true;
}
return false;
}
}
05.替换空格
class Solution {
public String replaceSpace(String s) {
StringBuilder sb=new StringBuilder();
int len=s.length();
for(int i=0;i<len;i++){
char c=s.charAt(i);
if(c==' ') sb.append("%20");
else sb.append(c);
}
return sb.toString();
}
}
06.从尾到头打印链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
int index=0;
int[] temp=new int[10000];
ListNode cur=head;
while(cur!=null){
temp[index++]=cur.val;
cur=cur.next;
}
int[]ans=new int[index];
int idx=0;
for(int j=index-1;j>=0;j--){
ans[idx++]=temp[j];
}
return ans;
}
}
07.重建二叉树
递归重建,两个数组区间的划分很重要
/**
* 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) {
int len1=preorder.length;
int len2=inorder.length;
return buildHelper(preorder,inorder,0,len1-1,0,len2-1);
}
public TreeNode buildHelper(int[]preorder,int[]inorder,int prea,int preb,int ina,int inb){
if(prea>preb) return null;
if(prea==preb) return new TreeNode(preorder[prea]);
int target=preorder[prea];
int pivotidx=0;
for(int i=ina;i<=inb;i++){
if(inorder[i]==target){
pivotidx=i;
break;
}
}
TreeNode root=new TreeNode(target);
root.left=buildHelper(preorder,inorder,prea+1,prea+1+pivotidx-1-ina,ina,pivotidx-1);
root.right=buildHelper(preorder,inorder,prea+1+pivotidx-1-ina+1,preb,pivotidx+1,inb);
return root;
}
}
09.两个栈实现队列
关键是掌握好两个栈进栈出栈的搭配时机,先往一个栈a进,代表队列尾部插入;如果要队列头部删除,如果另一个栈b为空,则将栈a的数据全部送入栈b,然后出栈,如果栈b不为空,直接出栈。
class CQueue {
Stack<Integer> sta;
Stack<Integer> stb;
public CQueue() {
sta=new Stack<>();
stb=new Stack<>();
}
public void appendTail(int value) {
sta.push(value);
}
public int deleteHead() {
if(stb.isEmpty()){
while(!sta.isEmpty()){
stb.push(sta.pop());
}
}
return stb.isEmpty()?-1:stb.pop();
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
10.斐波那契数列
class Solution {
public int fib(int n) {
int[]dp=new int[100+1];
int mod=(int)1e9+7;
dp[0]=0;
dp[1]=1;
for(int i=2;i<=n;i++){
dp[i]=(dp[i-1]+dp[i-2])%mod;
}
return dp[n];
}
}
10.青蛙跳台阶问题
class Solution {
public int numWays(int n) {
int mod=(int)1e9+7;
int[]dp=new int[101];
dp[0]=1;
dp[1]=1;
for(int i=2;i<=n;i++){
dp[i]=(dp[i-1]+dp[i-2])%mod;
}
return dp[n];
}
}
11.旋转数组最小的数字
利用二段性进行二分查找,前一半元素>=nums[0]
,后一半元素<nums[0]
,但是考虑到原数组可能在重复点旋转了,需要消除后半段和nums]0]
相同的那部分。
class Solution {
public int minArray(int[] nums) {
int len=nums.length;
int left=0;
int right=len-1;
//消除重复性
while(right>0&&nums[right]==nums[0]) right--;
while(left<right){
int mid=(left+right+1)/2;
//找到>=nums[0]的最后一个元素
if(nums[mid]>=nums[0]){
left=mid;
}else{
right=mid-1;
}
}
return left+1<=len-1?nums[left+1]:nums[0];
}
}
12.矩阵中的路径
dfs+回溯,常规做法,vis访问矩阵记录信息防止回环
class Solution {
int m,n;
public boolean exist(char[][] board, String word) {
m=board.length;
n=board[0].length;
boolean[][]vis=new boolean[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(dfs(board,vis,word,i,j,0)) return true;
}
}
return false;
}
//表示[i,j]出发后续能够构成单词word
public boolean dfs(char[][] board, boolean[][]vis,String word,int i,int j,int depth){
//需要先进行真的返回,不然可能在只有一个元素的时候,优先返回的是i,j越界,而不是检索到最后一个位置
//本来没写真的条件返回,if判断里永远都是false
if(depth>=word.length()) return true;
if(i<0||i>=m||j<0||j>=n) return false;
if(board[i][j]!=word.charAt(depth)||vis[i][j]) return false;
vis[i][j]=true;
int[]dx=new int[]{0,-1,0,1};
int[]dy=new int[]{-1,0,1,0};
for(int k=0;k<4;k++){
if(dfs(board,vis,word,i+dx[k],j+dy[k],depth+1)){
return true;
}
}
//四周检查完不满足的话需要回溯vis
vis[i][j]=false;
return false;
}
}
上面对于dfs base情况的分析不够简洁,所以true一定要在前面,下面把所有false写在一块,所以true在false的后面。
class Solution {
int m,n;
public boolean exist(char[][] board, String word) {
m=board.length;
n=board[0].length;
boolean[][]vis=new boolean[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(dfs(board,vis,word,i,j,0)) return true;
}
}
return false;
}
//表示[i,j]出发后续能够构成单词word
public boolean dfs(char[][] board, boolean[][]vis,String word,int i,int j,int depth){
if(i<0||i>=m||j<0||j>=n||board[i][j]!=word.charAt(depth)||vis[i][j]) return false;
if(depth==word.length()-1) return true;
vis[i][j]=true;
int[]dx=new int[]{0,-1,0,1};
int[]dy=new int[]{-1,0,1,0};
for(int k=0;k<4;k++){
if(dfs(board,vis,word,i+dx[k],j+dy[k],depth+1)){
return true;
}
}
//四周检查完不满足的话需要回溯vis
vis[i][j]=false;
return false;
}
}
下面这种做法就省略了vis数组,直接在board上做标记,也可以通过下标进行回溯。
class Solution {
int m,n;
public boolean exist(char[][] board, String word) {
m=board.length;
n=board[0].length;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(dfs(board,word,i,j,0)) return true;
}
}
return false;
}
//表示[i,j]出发后续能够构成单词word
public boolean dfs(char[][] board,String word,int i,int j,int depth){
if(i<0||i>=m||j<0||j>=n||board[i][j]!=word.charAt(depth)) return false;
if(depth==word.length()-1) return true;
board[i][j]=' ';
int[]dx=new int[]{0,-1,0,1};
int[]dy=new int[]{-1,0,1,0};
for(int k=0;k<4;k++){
if(dfs(board,word,i+dx[k],j+dy[k],depth+1)){
return true;
}
}
//四周检查完不满足的话需要回溯vis
board[i][j]=word.charAt(depth);
return false;
}
}
48. 最长不含重复字符的子字符串
class Solution {
public int lengthOfLongestSubstring(String s) {
HashSet<Character> win=new HashSet<>();
int left=0;
int ans=0;
for(int right=0;right<s.length();right++){
char c=s.charAt(right);
while(win.contains(c)){
win.remove(s.charAt(left++));
}
win.add(c);
ans=Math.max(ans,right-left+1);
}
return ans;
}
}
55. 二叉树深度
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
return dfs(root);
}
public int dfs(TreeNode root){
if(root==null) return 0;
return 1+Math.max(dfs(root.left),dfs(root.right));
}
}
55.2 平衡二叉树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
int ans=dfs(root);
return ans==-1?false:true;
}
// 递归函数两个功能:如果左右差>1,就返回-1,否则返回一边树的最大高度
public int dfs(TreeNode root){
if(root==null) return 0;
int left=dfs(root.left);
if(left==-1) return -1;
int right=dfs(root.right);
if(right==-1) return -1;
int cur=Math.abs(left-right);
if(cur>1) return -1;
return 1+Math.max(left,right);
}
}
62. 圆圈中最后剩下的数字
参考 题解,f(N,M)表示,N个人报数,每报到M时杀掉那个人,最终胜利者的编号。 f ( N , M ) = ( f ( N − 1 , M ) + M ) m o d N f(N,M)=(f(N-1,M)+M) \mod N f(N,M)=(f(N−1,M)+M)modN,理解这个递推式的核心在于关注胜利者的下标位置是怎么变的。每杀掉一个人,其实就是把这个数组向前移动了M位。然后逆过来,就可以得到这个递推式。我们知道f(1,M)为0;
class Solution {
public int lastRemaining(int n, int m) {
int ans=0;
for(int i=2;i<=n;i++){
ans=(ans+m)%i;
}
return ans;
}
}
67.把字符串转换为整数
纯模拟,把情况考虑全,首尾去空格,首个字符是否为正负号,绝对值计算时避免long越界提前退出
class Solution {
public int strToInt(String str) {
//判空以及判长度为0
if(str==null) return 0;
String s=str.trim();
int len=s.length();
if(len==0) return 0;
//判断首位
char firstc=s.charAt(0);
if(firstc!='-'&&firstc!='+'&&!Character.isDigit(firstc)) return 0;
int flag=1;
if(firstc=='-'){
flag=-1;
s=s.substring(1,s.length());
}
if(firstc=='+'){
s=s.substring(1,s.length());
}
//进行绝对值计算
len=s.length();
long sum=0L;
for(int i=0;i<len&&Character.isDigit(s.charAt(i));i++){
char c=s.charAt(i);
sum=sum*10+c-'0';
//及时判断很重要,会出现大于long long的结果
if(sum>Integer.MAX_VALUE) return flag==-1?Integer.MIN_VALUE:Integer.MAX_VALUE;
}
sum*=flag;
return (int)sum;
}
}