0
点赞
收藏
分享

微信扫一扫

【剑指Offer】查找算法


文章目录

  • ​​励志​​
  • ​​目录​​
  • ​​一、剑指 Offer 03. 数组中重复的数字​​
  • ​​题:​​
  • ​​解:​​
  • ​​二、剑指 Offer 04. 二维数组中的查找​​
  • ​​题:​​
  • ​​解:​​
  • ​​三、剑指 Offer 11. 旋转数组的最小数字​​
  • ​​题:​​
  • ​​解:​​
  • ​​四、剑指 Offer 50. 第一个只出现一次的字符​​
  • ​​题:​​
  • ​​解:​​
  • ​​五、剑指 Offer 53 - I. 在排序数组中查找数字 I​​
  • ​​题:​​
  • ​​解:​​
  • ​​六、剑指 Offer 53 - II. 0~n-1 中缺失的数字​​
  • ​​题:​​
  • ​​解:​​

励志



题目来源:
​​​https://leetcode-cn.com/leetbook/detail/illustration-of-algorithm/​

【剑指Offer】查找算法_数据结构


配合学习文档:

​https://www.kancloud.cn/alex_wsc/dataalg/1853982​

书籍推荐:

《漫画算法》

【剑指Offer】查找算法_算法_02


视频推荐:

​https://www.bilibili.com/video/BV1E4411H73v​

目录

【剑指Offer】查找算法_数据结构_03

一、剑指 Offer 03. 数组中重复的数字

题:

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3

限制:

2 <= n <= 100000

解:

解题思路:​​哈希表​​ AC代码:

class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> dic = new HashSet<>();
for(int num : nums){
if(dic.contains(num)) return num;
dic.add(num);
}
return -1;
}
}

  • 时间复杂度 O(N) : 遍历数组使用 O(N),HashSet 添加与查找元素皆为 O(1)
  • 空间复杂度 O(N) : HashSet 占用 O(N) 大小的额外空间

解题思路:​​索引映射​​(数组元素的索引和值是一对多的关系)

【剑指Offer】查找算法_算法_04


【剑指Offer】查找算法_算法_05

AC代码:

class Solution {
public int findRepeatNumber(int[] nums) {
int index = 0; // 指针
int len = nums.length;
while(index < len){
if(nums[index] == index){ // 索引一一对应
index++;
continue;
}
if(nums[nums[index]] == nums[index]) return nums[index];
// 交换
int temp = nums[index];
nums[index] = nums[temp];
nums[temp] = temp;
}
return -1;
}
}

时间复杂度 O(N) : 遍历数组使用 O(N) ,每轮遍历的判断和交换操作使用 O(1)。
空间复杂度 O(1) : 使用常数复杂度的额外空间。

二、剑指 Offer 04. 二维数组中的查找

题:

在一个 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

解:

解题思路:​​消消乐​

【剑指Offer】查找算法_leetcode_06


小了往上走,大了往右走。

AC代码:

class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
int i = matrix.length - 1, j = 0; // mark坐标
while(i >= 0 && j < matrix[0].length){
if(target < matrix[i][j]) i--; // target小,往上移
else if(target > matrix[i][j]) j++; // target大,往右移
else return true;
}
return false;
}
}

  • 时间复杂度 O(M+N):其中,N 和 M 分别为矩阵行数和列数,此算法最多循环 M+N 次。
  • 空间复杂度 O(1): i, j 指针使用常数大小额外空间。

三、剑指 Offer 11. 旋转数组的最小数字

题:

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

示例 1:

输入:[3,4,5,1,2]
输出:1

示例 2:

输入:[2,2,2,0,1]
输出:0

解:

解题思路:​​二分法​​(排序数组的查找问题首先考虑使用 二分法 解决,其可将 遍历法线性级别 时间复杂度降低至 对数级别 。)

【剑指Offer】查找算法_数组_07


【剑指Offer】查找算法_数组_08

【剑指Offer】查找算法_数组_09

AC代码:

class Solution {
public int minArray(int[] numbers) {
int len = numbers.length;
int i = 0, j = len - 1; // 左右指针
while(i < j){
int mid = (i + j) / 2;
if(numbers[mid] < numbers[j]) j = mid;
else if(numbers[mid] > numbers[j]) i = mid + 1; // mid一定不是最小值,越过
else j--; // 缩小二分范围
}
return numbers[i];
}
}

补充:

【剑指Offer】查找算法_数据结构_10


【剑指Offer】查找算法_leetcode_11

四、剑指 Offer 50. 第一个只出现一次的字符

题:

在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

示例:

s = "abaccdeff"
返回 "b"

s = ""
返回 " "

限制:

0 <= s 的长度 <= 50000

解:

解题思路:​​哈希表​​ AC代码:

class Solution {
public char firstUniqChar(String s) {
Map<Character, Boolean> dic = new HashMap<>();
char[] sc = s.toCharArray();
for(char c : sc){
dic.put(c,!dic.containsKey(c));
}
for(char c : sc){
if(dic.get(c)) return c;
}
return ' ';
}
}

  • 时间复杂度 O(N): N 为字符串 s 的长度;需遍历 s 两轮,使用 O(N) ;HashMap 查找操作的复杂度为 O(1) ;
  • 空间复杂度 O(1) : 由于题目指出 s 只包含小写字母,因此最多有 26 个不同字符,HashMap 存储需占用 O(26) = O(1)的额外空间。

解题思路:​​有序哈希表​​​(缩小了第二次遍历的范围)
AC代码:

class Solution {
public char firstUniqChar(String s) {
Map<Character, Boolean> dic = new LinkedHashMap<>();
char[] sc = s.toCharArray();
for(char c : sc)
dic.put(c, !dic.containsKey(c));
// 大同小异
for(Map.Entry<Character, Boolean> d : dic.entrySet()){
if(d.getValue()) return d.getKey();
}
return ' ';
}
}

五、剑指 Offer 53 - I. 在排序数组中查找数字 I

题:

统计一个数字在排序数组中出现的次数。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

提示:

0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109

解:

解题思路:​​二分法​

【剑指Offer】查找算法_数组_12

AC代码:

class Solution {
public int search(int[] nums, int target) {
// 搜索右边界 right
int i = 0, j = nums.length - 1;
while(i <= j) {
int m = (i + j) / 2;
// 卡左(等号)指针位置
if(nums[m] <= target) i = m + 1;
else j = m - 1;
}
int right = i;
// 若数组中无 target ,则提前返回
if(j >= 0 && nums[j] != target) return 0;
// 搜索左边界 left
i = 0; j = nums.length - 1;
while(i <= j) {
int m = (i + j) / 2;
// 卡右(等号)指针位置
if(nums[m] >= target) j = m - 1;
else i = m + 1;
}
int left = j;
return right - left - 1;
}
}

代码有点冗余,优化优化(数组 nums 中元素都为整数):

【剑指Offer】查找算法_leetcode_13


通俗的讲就是 (tar - 1卡右) 等于 (tar 卡左 + 1),把卡右提取一个方法,减少冗余

AC代码:

class Solution {
public int search(int[] nums, int target) {
return findRight(nums, target) - findRight(nums, target - 1);
}
int findRight(int[] nums, int tar){
int i = 0, j = nums.length - 1;
while(i <= j){
int m = (i + j) / 2; // 下取整
// 卡左(限制i),取右(tar在右),舍边(i=m无效)
if(nums[m] <= tar) i = m + 1;
else j = m - 1;
}
return i;
}
}

六、剑指 Offer 53 - II. 0~n-1 中缺失的数字

题:

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例 1:

输入: [0,1,3]
输出: 2

示例 2:

输入: [0,1,2,3,4,5,6,7,9]
输出: 8

限制:

1 <= 数组长度 <= 10000

解:

解题思路:​​二分法​​ AC代码:

class Solution {
public int missingNumber(int[] nums) {
int i = 0, j = nums.length - 1;
while(i <= j){
int m = (i + j) / 2;
// 卡i,去边,取右
if(nums[m] == m) i = m + 1;
else j = m - 1;
}
return i;
}
}


举报

相关推荐

0 条评论