面试题3:二维数组中的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
测试用例:
(1)输入的数组是空指针
(2)输入的数组中没有目标数
(3)输入的数组中有目标数
//我的代码1 时间复杂度为O(mn) 运行时间为257ms 慢了!
public class Solution {
public boolean Find(int target, int [][] array) {
if(array == null) return false; #处理空指针
for(int i = 0;i < array.length;i++){
for(int j=0;j<array[i].length;j++){
if(array[i][j] == target){
return true;
}else if(array[i][j] > target){
break;
}
}
}
return false;
}
}
注意:上面的有一块是可以优化的,就是break那里,因为还可以分为是否在第一个位置和不在第一个位置,虽然这样也可以完成!
// 我的代码2 时间复杂度为O(n) 运行时间为157ms
public class Solution {
public boolean Find(int target, int [][] array) {
if(array == null) return false;
int i = array.length-1,j = 0;
while(i>=0 && j<array[0].length){
if(array[i][j] == target){
return true;
}else if(array[i][j] > target){
i--;
}else{
j++;
}
}
return false;
}
}
这个比较快,而且充分应用了对角点的特点。
面试题4:替换空格
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
//第一种:这种方法是取巧型的,主要借助了java的底层封装。这种代码主要就是用了StringBuffer的底层可扩充的性能
public class Solution {
public String replaceSpace(StringBuffer str){
if(str == null) return null;
StringBuffer s = new StringBuffer();
for(int i = 0;i<str.toString().length();i++) {
char a = str.charAt(i);
if(String.valueOf(a).equals(" ")) {
s.append("%20");
}else {
s.append(str.charAt(i));
}
}
return s.toString();
}
}
关于字符串的javaAPI和操作知识点:
- java中String和StringBuffer是不一样的,String是不可变得,而StringBuffer则是长度可变的。并且返回值时也不一样,如果错把StringBuffer当成String返回则会报错!
2.String应该是用的最多的,其中常用的方法有:(假设str为一个字符串变量)
- 字符串的长度: str.length length后面木有括号!
- 按照索引找到字符串的字符 str.charAt(i) #i就是索引数字
- StringBuffer类型不能直接使用String的方法,那么需要转化的时候,可以调用strB.toString()就变成了字符串类型。
- StringBuffer类型可以进行长度扩充,使用append()方法即可。
面试题5:打印链表
输入一个链表,从尾到头打印链表每个节点的值(注意是从尾到头)
//方法一:借助栈
定义一下链表的结构:
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
import java.util.Stack;
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if(listNode == null) {
ArrayList<Integer> a = new ArrayList<>();
return a;
}
ArrayList<Integer> a = new ArrayList<>();
// 借助栈的先进后出,所以需要导入java.util.Stack
Stack<Integer> s =new Stack<Integer>();
while(listNode != null) {
s.push(listNode.val);
listNode = listNode.next;
}
while(!s.isEmpty()) {
a.add(s.pop());
}
return a;
}
}
这道题会有两个易出错的地方:
(1)常规处理输入为空的时候,我们返回的是null,但是这道题会有输入为{}的情况,这个时候需要该题返回的是[],所以需要特别的处理。
(2)java中的栈需要导入的包是java.util.Stack
与栈相关的方法有:s.push(element) 进栈
s.pop(element) 出栈
s.isEmpty() 判断栈是否为空(注意,不是用的遍历个数,而是直接判断栈是否为空)
这道题考察了数据结构栈来辅助进行逆序!
相关java知识点:
- List集合代表一个元素有序、可重复的集合。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List集合默认按照元素的添加顺序设置元素的索引,索引从0开始。
ArrayList是基于数组实现的List类。
List中的常用方法:
1. void add(int index,Object element)将元素添加到索引index处。
2. Object get(int index) 返回索引处的元素
面试题6:重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
//代码:
定义二叉树:
* 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==null || in == null) return null;
if(pre.length==0 || in.length == 0) return null;
TreeNode a = new TreeNode(pre[0]);
for(int i=0;i<pre.length;i++) {
if(pre[0] == in[i] ) {
a.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i+1),Arrays.copyOfRange(in, 0, i));
a.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i+1, pre.length),Arrays.copyOfRange(in, i+1, pre.length));
}
}
return a;
}
}
二叉树相关知识点:
前序遍历:遍历顺序规则为【根左右】
中序遍历:遍历顺序规则为【左根右】
后序遍历:遍历顺序规则为【左右根】
什么是【根左右】?就是先遍历根,再遍历左孩子,最后遍历右孩子;
java数组方法:
上面用到了Arrays.copyOfRange(in, 0, i))用来拷贝数组。其中第一个参数是要拷贝的数组,第二个和第三个参数是要拷贝的数组下标范围(拷贝的时候包括前标,但是不包括后标,也就是前闭后开)
面试题7:用两个栈实现队列
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
//代码:
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
while(!stack2.isEmpty()){
stack1.push(stack2.pop());
}
stack1.push(node);
}
public int pop() {
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
return stack2.pop();
}
}
知识点:
这道题主要考的就是队列的特性是先进先出,栈的特性是先进后出。所以模拟的时候需要注意这个问题!
面试题8:旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
//代码:
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
if(array==null || array.length==0) return 0;
int min = array[array.length -1];
for(int i = array.length-1;i>=0;i--) { #这个位置一定要注意-1)
if(array[i] < min) {
min = array[i];
}else if(array[i]>min) {
break;
}
}
return min;
}
}
面试题9:斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
n<=39
//代码:(使用循环实现)
public class Solution {
public int Fibonacci(int n) {
if(n==0) return 0; //这里可能会忽略处理,通过代码要求对0进行处理
if(n==1 || n==2) return 1;
int [] a ;
a = new int [39];
a[0] = 1;a[1]=1;
for(int i=2;i<n;i++) {
a[i] = a[i-1]+a[i-2];
}
return a[n-1];
}
}
注意:这个应该还有一种使用递归实现的算法
知识点:
在数学上,斐波纳契数列以如下被以递归的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)
面试题10:跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
//代码:
public class Solution {
public int JumpFloor(int target) {
if(target == 0) return 0;
if(target == 1) return 1;
if(target == 2) return 2;
int [] a;
a = new int [target];
a[0] = 1;
a[1] = 2;
for(int i=2;i<target;i++) {
a[i] = a[i-1] + a[i-2];
}
return a[target-1];
}
}
>
注意:
(1) 这道题主要考察的是递归和循环
(2)是斐波那契数列的变体,只不过是前两个初值不同而已
面试题11:变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
//代码:
public class Solution {
public int JumpFloorII(int target) {
if(target == 0) return 0;
if(target == 1) return 1;
int [] a;
a = new int [target];
a[0] = 1;
for(int i=1;i<target;i++) {
for(int j=0;j<i;j++) {
a[i] += a[j];
}
a[i] += 1;
}
return a[target-1];
}
}
注意:
(1) 这道题虽然看上去和上面那道题很像,但是实际上原理相差的就比较多了,虽然也是考的递归和循环,但是需要自己手动找规律,也不属于斐波那契数列了!
下面是大神总结的思路,我的并没有写出最后的那个公式:
面试题12: 矩形覆盖
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
//代码:
public class Solution {
public int RectCover(int target) {
if(target == 0) return 0;
if(target == 1) return 1;
if(target == 2) return 2;
int [] a ;
a = new int [target];
a[0] = 1;
a[1] = 2;
for(int i=2;i<target;i++) {
a[i] = a[i-1]+a[i-2];
}
return a[target-1];
}
}
注意:这道题的思路和青蛙那道一模一样,代码都没变!!
面试题13:位运算
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
代码:
public class Solution {
public int NumberOf1(int n) {
if(n == 0) return 0;
int count = 0;
while(n!=0) {
count += 1;
n = n & (n-1);
}
return count;
}
}
思路解析:
如果一个整数不为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,就可以进行多少次这样的操作。