面试题 4
题目描述
在一个 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
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof
思路
每次用右上角的元素于目标值比较,那么就有3种可能
- target == 右上角元素,直接返回true;
- target > 右上角元素,说明目标值只可能在当前行的下方,因此可以舍弃当前行;
- target < 右上角元素,说明目标值只可能在当前列的左边,因此可以舍弃当前列;
Hit:也可以每次和左下角的元素对比,但是不能和左上角和右下角的元素对比
Code
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
// 注意二维数组判定
if (matrix == null || matrix.length == 0 || matrix[0].length == 0)
return false;
int row = matrix.length - 1; // 二维数组行索引
int col = matrix[0].length -1 ; // 二维数组列索引最大值
int i = 0;// 从第一行开始遍历
// 注意行列边界条件
while ( i <= row && col >= 0){
// 右上角值恰好等于 目标值 直接退出
if (matrix[i][col] == target){
return true;
} else if (matrix[i][col] > target) {
// 右上角值大于 目标值,则舍弃最后一列
col = col -1;
}else {
// 右上角值 小于 目标值,则舍弃最上面一行
i++;
}
}
return false;
}
}
复杂度分析
时间复杂度:O(m+n)
空间复杂度:O(1)
面试题 5
题目描述
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例
输入:s = "We are happy."
输出:"We%20are%20happy."
限制:
0 <= s 的长度 <= 10000
链接:https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof
思路
step 1:统计原字符串中空格的个数 space
step 2:新建一个字符数组,长度是原来字符串长度 加上 space*2
step 3:用两个指针分别指向原字符串和新建字符串的末尾,如果原来字符串中元素不是空格,那么就把这个元素复制到新字符串中,两个指针先前移动一格;如果原字符串中元素是空格,则新字符串从后往前依次赋值为 ‘0’ 、‘2’ 、’%’,并将新字符串指针先前移动3格,原字符串指针移动1格。直到遍历完原字符串所有元素。
Code
public class Solution {
public String replaceSpace(String s) {
int space = 0;// 空格数
char[] array = s.toCharArray();
// 统计原字符串中空格个数
for (int i = 0 ; i < array.length;i++){
if (array[i] == ' ')
space++;
}
// 新定义一个字符创数组 长度是原来长度加上替换空格后的长度
char[] newString = new char[array.length + space * 2];
int indexOfOld = array.length - 1; // 指向原字符串的末尾
int indexOfNew = newString.length -1 ; // 指向新字符串的末尾
while (indexOfOld >= 0 && 0 <= indexOfNew) {
// 遍历原字符串 当前字符不是空格就从后往前复制到新字符创中
if (array[indexOfOld] != ' '){
newString[indexOfNew--] = array[indexOfOld];
} else {
// 否则,将原字符串的空格在新字符串中替换成 %20 新字符串的指针先前移动三个位置
newString[indexOfNew--] = '0';
newString[indexOfNew--] = '2';
newString[indexOfNew--] = '%';
}
indexOfOld--;
}
// newString.toString(); // 这个方法返回的是 字符串的地址
// return Arrays.toString(newString); // 不能用Arrays.toString()方法 该方法返回一个string 类型的 list 而题目要求字符串
return String.valueOf(newString);
}
}
复杂度分析
时间复杂度:O(n)
空间复杂度:O(n)
面试题 6
题目描述
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 :
输入:head = [1,3,2]
输出:[2,3,1]
限制
0 <= 链表长度 <= 10000
链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof
思路
可以遍历一遍链表,依次把链表元素压入栈中,再依次弹出栈中元素到一个数组中即可
Code
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x; }
}
public class Solution {
public int[] reversePrint(ListNode head) {
if (head == null)
return new int[0] ; // 链表为空,则返回空数组
Stack<Integer> stack = new Stack<>(); // 定义一个元素类型为整型的栈
while (head != null) {
// 将每个节点元素依次入栈
stack.push(head.val);
head = head.next;
}
int number = stack.size();// 获取链表节点个数
int[] result = new int[number]; // 最终打印值
for (int i = 0; i < number; i++){
// 栈中元素依次存入 result中
result[i] = stack.pop();
}
return result;
}
}
复杂度分析
时间复杂度:O(n)
空间复杂度:O(n)
面试题 7
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
限制:
0 <= 节点个数 <= 5000
链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof
思路
前序遍历的第一个节点是根节点,只要找到根节点在中序遍历中的位置,在根节点之前被访问的节点都位于左子树,在根节点之后被访问的节点都位于右子树,由此可知左子树和右子树分别有多少个节点。
由于树中的节点数量与遍历方式无关,通过中序遍历得知左子树和右子树的节点数量之后,可以根据节点数量得到前序遍历中的左子树和右子树的分界,因此可以进一步得到左子树和右子树各自的前序遍历和中序遍历,可以通过递归的方式,重建左子树和右子树,然后重建整个二叉树。
使用一个 Map 存储中序遍历的每个元素及其对应的下标,目的是为了快速获得一个元素在中序遍历中的位置。调用递归方法,对于前序遍历和中序遍历,下标范围都是从 0 到 n-1,其中 n 是二叉树节点个数。
递归方法的基准情形有两个:判断前序遍历的下标范围的开始和结束,若开始大于结束,则当前的二叉树中没有节点,返回空值 null。若开始等于结束,则当前的二叉树中恰好有一个节点,根据节点值创建该节点作为根节点并返回。
若开始小于结束,则当前的二叉树中有多个节点。在中序遍历中得到根节点的位置,从而得到左子树和右子树各自的下标范围和节点数量,知道节点数量后,在前序遍历中即可得到左子树和右子树各自的下标范围,然后递归重建左子树和右子树,并将左右子树的根节点分别作为当前根节点的左右子节点。