Skip to content

Commit f256717

Browse files
Obsession-kaiminibear2333
Obsession-kai
authored andcommitted
update day4/31
1 parent 6e2d6a1 commit f256717

File tree

3 files changed

+226
-0
lines changed

3 files changed

+226
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// 题目链接:https://leetcode.cn/problems/que-shi-de-shu-zi-lcof/
2+
// day4/31
3+
// 第 4 天主题为:查找算法(简单)
4+
// 包含三道题目:
5+
// 剑指offer03.数组中重复的数字
6+
// 剑指offer53-I.旋转数组的最小数字
7+
// 剑指offer50.第一个只出现一次的字符
8+
9+
// 解题思路:题目出现有序,优先考虑二分
10+
// 此题的通俗解法为,遍历数组
11+
// 当nums[i]不等于下标i时,说明从下标i开始,之后的元素值均为下标值+1,返回下标i即可。
12+
package main
13+
14+
func missingNumber(nums []int) int {
15+
for i:=0;i<len(nums);i++{
16+
if i != nums[i]{
17+
return i
18+
}
19+
}
20+
return -1
21+
}
22+
23+
// 解法2:二分查找
24+
// 根据题意,可将有序数组 nums 分为两部分,第一部分其值 nums[i] = i,第二部分 nums[i]=i+1
25+
// 并且第二部分元素数量限制均为 大于等于0 且 小于等于 n-1(此n为函数形参n,非数组长度)。
26+
// 先考虑两种特殊情况:
27+
//- 当第一部分长度为0,返回0
28+
//- 第二部分长度为0,返回数组长度即可
29+
// 在常规情况下,我们要找的是第二部分第一个元素的下标
30+
func missingNumber_2(nums []int) int {
31+
n := len(nums)
32+
left,right := 0,n-1
33+
for left <= right{
34+
mid := left+(right-left)>>1
35+
//优先考虑两种极端情况
36+
if mid==0 && nums[mid]!=mid{
37+
return mid
38+
} else if mid==n-1 && nums[mid]==mid{
39+
return mid+1
40+
} else if nums[mid]!=mid && nums[mid-1]==mid-1{
41+
return mid
42+
} else if nums[mid] != mid{
43+
right = mid - 1
44+
} else {
45+
left = mid + 1
46+
}
47+
}
48+
return -1
49+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//题目链接:https://leetcode.cn/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/
2+
3+
package main
4+
5+
// 最通俗的解法,遍历数组,统计target出现的次数
6+
// 但这样的话完全没用到题目中数组是排序的 这个条件
7+
// 显然,本题想要考察二分查找算法!
8+
// 看到有序数组就应该第一时间想到二分查找
9+
func search(nums []int, target int) int {
10+
n := len(nums)
11+
ans := 0
12+
for i := 0;i < n;i++{
13+
if nums[i] == target{
14+
ans += 1
15+
}
16+
}
17+
return ans
18+
}
19+
20+
// 方法2:两次二分查找
21+
// 统计一个数字在排序数组中出现的次数,那我们只需要知道其在数组中第一次和最后一次出现的下标
22+
// 设为 left 和 right,出现次数即为:right-left+1
23+
// 函数 first_equal_search 和 last_equal_search 分别用于查找数字第一次和最后一次出现的位置下标。
24+
// 实现上述两个函数时,网上有很多花里花哨的写法,如果去死记硬背,没过几天就会全部忘光
25+
// 下面我的写法非常好理解
26+
// 为避免命名冲突,次函数名添加后缀 “_2”
27+
func search_2(nums []int, target int) int {
28+
left := first_equal_search(nums,target)
29+
right := last_equal_search(nums,target)
30+
// 若出现次数为 0,需额外处理
31+
if left == -1{
32+
return 0
33+
}
34+
return right-left+1
35+
}
36+
37+
func first_equal_search(nums []int,target int) int{
38+
n := len(nums)
39+
left,right := 0,n-1
40+
for left <= right{
41+
mid := left + (right-left)>>1
42+
if mid==0 && nums[mid]==target{
43+
return mid
44+
} else if mid>0 && nums[mid]==target && nums[mid-1]!=target{
45+
return mid
46+
} else if nums[mid] < target{
47+
left = mid + 1
48+
} else if nums[mid] > target{
49+
right = mid - 1
50+
} else {
51+
// 此种情况说明 mid 指向元素为众多与target相等元素的其中一个
52+
// 而且不在起始点,我们要找第一个,所以移动右指针
53+
right = mid -1
54+
}
55+
}
56+
return -1
57+
}
58+
59+
func last_equal_search(nums []int,target int) int{
60+
n := len(nums)
61+
left,right := 0,n-1
62+
for left <= right{
63+
mid := left + (right-left)>>1
64+
if mid==n-1 && nums[mid]==target{
65+
return mid
66+
} else if mid<n-1 && nums[mid]==target && nums[mid+1]!=target{
67+
return mid
68+
} else if nums[mid] < target{
69+
left = mid + 1
70+
} else if nums[mid] > target{
71+
right = mid - 1
72+
} else {
73+
// 与找第一个相等的元素同理,不在最后一个,所以移动左指针
74+
left = mid + 1
75+
}
76+
}
77+
return -1
78+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// 题目链接:https://leetcode.cn/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/
2+
3+
package main
4+
5+
// 解题思路:本题最通俗的解法为哈希表,用哈希表记录nums数组中元素是否出现过,
6+
// 若出现过,返回该元素即可。
7+
func findRepeatNumber(nums []int) int {
8+
record := map[int]int{}
9+
for _,num := range nums{
10+
if record[num] > 0{
11+
return num
12+
}
13+
record[num] ++
14+
}
15+
return -1
16+
}
17+
18+
// 方法2:设计哈希表
19+
// 哈希表解法的时间与空间复杂度均为O(n),该方法没有用到题目给的数字范围条件,所以应该思考下如何做改进
20+
// 试想下如何降低复杂度,对长度为n的数组,其中所有数字都在0~n-1范围内,而0~n-1又是长度为n的数组的所有下标索引。我们刚才方法是将其值放入哈希表
21+
// 而现在我们给定数组的长度为n,0~n-1刚好可以对应数组索引,让我们有一种将数组设计为哈希表的思路:
22+
// 对数组进行遍历,设当前遍历到的的数字值为 x,则将索引为 x 对应的数字打标记,若之后遍历到某元素后,打标记过程中发现该元素已打标记,说明该数字已出现过,返回即可。
23+
// 现在要做的就是设计标记,标记为取负值,流程如下:
24+
// 一次遍历数组中元素,设当前元素值为num,它可能已经被打了标记,我们取用其原数x=abs(num)
25+
// 如果 nums[x] < 0,说明 x 已经出现过,返回 x 即可,否则,打标记,nums[x] = -nums[x]
26+
// 但还存在一个问题,就是0取负还是0,我没想到太好的解决方案,就用了最朴素的方法,单独处理0。
27+
// 处理流程如下:初始化0的下标为 zero_index,遍历数组,得到0的下标zero_index,然后第二次遍历数组,若数组元素值为 zero_index的元素个数大于1,说明该元素重复,返回0的下标即可。
28+
// 这样的解题思路来自LeetCode41题:缺失的第一个正数
29+
// 算法核心在于将输入数组设计为哈希表,另外,我在此题的题解部分还没有看人有人用类似的方案解此题
30+
// 在进行此操作前,请务必与面试官进行交流,询问是否可以修改输入数组,确定可以的话,再用此方案。
31+
// 为避免命名冲突,次函数名添加后缀 “_2"
32+
func findRepeatNumber_2(nums []int) int {
33+
// 先处理0的情况
34+
// m为nums中0的下标出现的次数,zero_index为0的下标初始值
35+
m := 0
36+
zero_index := -1
37+
// 第一次遍历得到0的下标
38+
for i:=0;i<len(nums);i++{
39+
if nums[i] == 0{
40+
zero_index = i
41+
break
42+
}
43+
}
44+
// 第二次遍历判断0的下标是否出现两次及两次以上
45+
for i:=0;i<len(nums);i++{
46+
if nums[i]==zero_index{
47+
m ++
48+
}
49+
}
50+
if m > 1{
51+
return zero_index
52+
}
53+
// 将输入数组设计为哈希表
54+
for _,num := range nums{
55+
x := abs(num)
56+
// nums[x]<0,说明x元素此前出现过
57+
if nums[x] < 0{
58+
return x
59+
}
60+
// 若未出现过,打标记
61+
nums[x] = -nums[x]
62+
}
63+
return -1
64+
}
65+
66+
func abs(num int) int {
67+
if num < 0{
68+
return -num
69+
}
70+
return num
71+
}
72+
73+
74+
// 方法3:原地交换 次题解方法来自:https://leetcode.cn/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/solution/mian-shi-ti-03-shu-zu-zhong-zhong-fu-de-shu-zi-yua/
75+
// 长度为 n 的数组 nums 里的所有数字都在 0 ~ n-1 的范围内 。 此说明含义:数组元素的 索引 和 值 是 一对多 的关系。
76+
// 因此,可遍历数组并通过交换操作,使元素的 索引 与 值 一一对应(即 nums[i] = i)。因而,就能通过索引映射对应的值,起到与字典等价的作用。
77+
// 遍历中,第一次遇到数字 x 时,将其交换至索引 x 处;而当第二次遇到数字 x 时,一定有 nums[x] = x,此时即可得到一组重复数字。。
78+
// 算法流程:
79+
// 遍历数组 nums,设索引初始值为 i = 0 :
80+
// 若 nums[i] = i: 说明此数字已在对应索引位置,无需交换,因此跳过;
81+
// 若 nums[nums[i]] = nums[i]: 代表索引nums[i]处和索引i处的元素值都 nums[i],即找到一组重复值,返回此值 nums[i];
82+
// 否则,交换索引为i和nums[i]的元素值,将此数字交换至对应索引位置。
83+
// 若遍历完毕尚未返回,则返回 -1 。
84+
// 为避免命名冲突,次函数名添加后缀 “_3"
85+
func findRepeatNumber_3(nums []int) int {
86+
i := 0
87+
for i < len(nums){
88+
// 交换 nums[i]至索引i出后才进行下一索引处的交换
89+
if nums[i] == i{
90+
i ++
91+
continue
92+
}
93+
if nums[nums[i]] == nums[i]{
94+
return nums[i]
95+
}
96+
nums[nums[i]],nums[i] = nums[i],nums[nums[i]]
97+
}
98+
return -1
99+
}

0 commit comments

Comments
 (0)