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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
很明显数组有一定的规律,即每行从左到右递增,每列从上往下递增,可以从右上开始进行判断,若给定值相等,return true,如果小于右上,则列–,否则行++,重复进行,直至得到相等的值,如果循环结束后仍不存在相同的值,则可以判定不存在,返回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, 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;
}
}
2、给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
示例 4:
输入: s = “”
输出: 0
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
题解
对于此题,我们要寻找的是一段连续字符串的最大不重复子串,因此可以采用双指针的方式进行查找,即窗口。左指针从字符串首字符开始查找,右指针设置为-1,即认为是指针末尾。另外设置一个HashSet集合用来存储不重复的字符,首先左指针不动,然后右指针不断右移,如果当前右指针所指示的字符不存在set中,则将该字符存入set集合中,同时右指针后移,重复进行,如果当前右指针已经存在set中,则从set集合中删除此字符,同时左指针右移。此时字符串长度为res-i+1
代码如下:
public int lengthOfLongestSubstring(String s) {
int n = s.length();
int res = -1, max=0;//设置右指针为-1,max用于存储最长字符长度,即最终结果
Set<Character> set = new HashSet<Character>();//设置set集合存储字符判断字符是否出现过
for(int i=0;i<n;++i){
if(i!=0){
//左指针右移,删除一个元素
set.remove(s.charAt(i-1));
}
while(res+1<n && !set.contains(s.charAt(res+1))){
//不断移动右指针
set.add(s.charAt(res+1));
res++;
}
//第i到res个字符是一个最长的字符串
max = Math.max(max,res-i+1);
}
return max;
}
3.请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1:
输入:s = “We are happy.”
输出:“We%20are%20happy.”
限制:
0 <= s 的长度 <= 10000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
一、利用char数组存储新字符串
public String replaceSpace(String s) {
char[] chars = new char[s.length()*3];
int size = 0;
for(int i=0;i<s.length();i++){
char c = s.charAt(i);
if(c==' '){
chars[size++]='%';
chars[size++]='2';
chars[size++]='0';
}
else{
chars[size++]=c;
}
}
String newStr = new String(chars, 0, size);
return newStr;
}
二、利用StringBuilder存储新字符串,利用append添加字符
public String replaceSpace(String s) {
StringBuilder sb = new StringBuilder();
for(int i=0;i<s.length();i++){
char c = s.charAt(i);
if(c==' '){
sb.append("%20");
}else{
sb.append(c);
}
}
return sb.toString();
}
4.给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
示例 3:
输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000
示例 4:
输入:nums1 = [], nums2 = [1]
输出:1.00000
示例 5:
输入:nums1 = [2], nums2 = []
输出:2.00000
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
利用两个数组的指针进行数组的删除(理论上排除一部分不需要的值,同时需要修改k的值)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int length1 = nums1.length, length2 = nums2.length;
int totalLength = length1 + length2;
if (totalLength % 2 == 1) {
int midIndex = totalLength / 2;
double median = getKthElement(nums1, nums2, midIndex + 1);
return median;
} else {
int midIndex1 = totalLength / 2 - 1, midIndex2 = totalLength / 2;
double median = (getKthElement(nums1, nums2, midIndex1 + 1) + getKthElement(nums1, nums2, midIndex2 + 1)) / 2.0;
return median;
}
}
public int getKthElement(int[] nums1, int[] nums2, int k) {
/* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
* 这里的 "/" 表示整除
s1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
* nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
ivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
* 这样 pivot 本身最大也只能是第 k-1 小的元素
pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
* 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
*/
int length1 = nums1.length, length2 = nums2.length;
int index1 = 0, index2 = 0;
int kthElement = 0;
while (true) {
// 边界情况
if (index1 == length1) {
return nums2[index2 + k - 1];
}
if (index2 == length2) {
return nums1[index1 + k - 1];
}
if (k == 1) {
return Math.min(nums1[index1], nums2[index2]);
}
// 正常情况
int half = k / 2;
int newIndex1 = Math.min(index1 + half, length1) - 1;
int newIndex2 = Math.min(index2 + half, length2) - 1;
int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2];
if (pivot1 <= pivot2) {
k -= (newIndex1 - index1 + 1);
index1 = newIndex1 + 1;
} else {
k -= (newIndex2 - index2 + 1);
index2 = newIndex2 + 1;
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Aq1z4Rca-1619352644070)(C:\Users\Lin\AppData\Roaming\Typora\typora-user-images\image-20210425163606566.png)]
题解
一、使用递归,分别构建左子树和右子树。首先找到根节点在中序遍历中的位置,从而得到左子树和右子树的长度,进而递归构建(每次修改指针,指向下一个根节点(子树的根节点))
public class Solution1 {
private Map<Integer,Integer> indexMap;
public TreeNode myBuildTree(int[] preorder, int preorderLeft,int preorderRight, int[] inorder , int inorderLeft, int inorderRight) {
if (preorderLeft>preorderRight){
return null;
}
//查得头结点
int pre_root = preorderLeft;
int in_root = indexMap.get(preorder[pre_root]);
//构建根节点
TreeNode root = new TreeNode(preorder[pre_root]);
//构建左子树
int size_leftTree = in_root - inorderLeft;
root.left = myBuildTree(preorder,preorderLeft+1,preorderLeft+size_leftTree,inorder,inorderLeft,in_root);
//构建右子树
root.right = myBuildTree(preorder,preorderLeft+size_leftTree+1,preorderRight,inorder,in_root+1,inorderRight);
return root;
}
public TreeNode buildTree(int[] preorder,int[] inorder){
int n = preorder.length;
indexMap = new HashMap<>();
for (int i = 0; i < n; i++) {
indexMap.put(inorder[i],i);
}
return myBuildTree(preorder,0,n-1,inorder,0,n-1);
}
}
解法二、使用迭代进行构建,利用栈进行构建,思路:将前序遍历的值进行压栈,直到找到左子树的根节点(此时栈顶的值等于中序遍历序列的第一个),然后转而构建右子树,同理进行迭代,对于中序遍历,需要一个指针,每次出栈指针后移一位。
public class Solution2 {
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
if (n<=0){
return null;
}
//对中序遍历序列设置指针,从零开始指向第一个元素
int index = 0;
Deque<TreeNode> stack = new LinkedList<>();
//构建根节点,并入栈
TreeNode root = new TreeNode(preorder[0]);
stack.push(root);
//构建子树
for (int i = 1; i < n; i++) {
//取出栈顶元素
TreeNode treeNode = stack.peek();
//如果栈顶元素值不等于中序遍历指针对应的值,说明此元素为栈顶节点的左子树
if (treeNode.val!=inorder[index]){
//链接并入栈
treeNode.left = new TreeNode(preorder[i]);
stack.push(treeNode.left);
}else {//此时说明已经找到左子树的最后一个元素,即最左下,依次弹出栈顶元素并将index指针后移一位,当栈顶元素值不等于指针对应元素的值时,说明当前i指针指向的值为前节点的右子树
while (!stack.isEmpty()&&stack.peek().val==inorder[index]){//此处treeNode=刚才弹出的栈顶元素,不能使用,仍需使用peek()取出此时的栈顶元素的值
treeNode = stack.pop();
++index;
}
//连接右子树,并压栈
treeNode.right = new TreeNode(preorder[i]);//前序遍历总是根节点优先访问
stack.push(treeNode.right);
}
}
return root;
}
}