Skip to content

Commit 8ba8388

Browse files
committed
update some pro and add tree traversal
1 parent 1b1f80c commit 8ba8388

File tree

13 files changed

+558
-158
lines changed

13 files changed

+558
-158
lines changed

Array/164_MaximumGap.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,26 @@ class Solution(object):
99
And here is a java solution in leetcode's discuss:
1010
https://leetcode.com/discuss/53636/radix-sort-solution-in-java-with-explanation
1111
"""
12+
1213
def maximumGap(self, nums):
1314
if not nums:
1415
return 0
1516

16-
max_gap = 0
17-
max_num = nums[0]
18-
for num in nums:
19-
if num > max_num:
20-
max_num = num
21-
17+
max_num = max(nums)
18+
bucket = [[] for i in range(10)]
2219
exp = 1
23-
sorted_digit = [0 for num in nums]
2420
while max_num / exp > 0:
25-
digits = [0 for i in range(10)]
2621
for num in nums:
27-
digits[(num/exp) % 10] += 1
28-
for i in range(1, 10):
29-
digits[i] += digits[i-1]
30-
for num in nums[::-1]:
31-
digit_num = (num/exp) % 10
32-
sorted_digit[digits[digit_num]-1] = num
33-
digits[digit_num] -= 1
34-
nums = sorted_digit
22+
bucket[(num / exp) % 10].append(num)
23+
nums = []
24+
for each in bucket:
25+
nums.extend(each)
26+
bucket = [[] for i in range(10)]
3527
exp *= 10
3628

3729
max_gap = 0
3830
for i in range(1, len(nums)):
39-
max_gap = max(max_gap, nums[i]-nums[i-1])
31+
max_gap = max(max_gap, nums[i] - nums[i - 1])
4032
return max_gap
4133

4234
"""
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#! /usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
# @Last Modified time: 2016-08-29 10:15:24
5+
6+
7+
class Solution(object):
8+
""" Heap merge is helpfull.
9+
"""
10+
def kthSmallest(self, matrix, k):
11+
import heapq
12+
return list(heapq.merge(*matrix))[k - 1]
13+
14+
15+
class Solution(object):
16+
""" Binary Search can solve this too.
17+
"""
18+
def kthSmallest(self, matrix, k):
19+
20+
21+
"""
22+
[[1]]
23+
1
24+
[[1,2,3], [4,5,6], [7,8,9]]
25+
3
26+
[[ 1, 5, 9], [10, 11, 13], [12, 13, 15]]
27+
8
28+
"""

BinarySearch/README.md

Lines changed: 126 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -8,50 +8,54 @@
88
* 递归实现时,检查参数的有效性(low/high参数);
99
* 计算二分查找中的中值时防止溢出;
1010
* 如何查找第一个/最后一个等值?
11-
 
11+
1212
# 简单实现
1313

1414
下面来看下简单二分查找(数组中不包含重复数字)的两种实现方案。
1515

1616
递归实现如下:
1717

18-
// 在 nums[begin, end) 中查找 target
19-
int binary_search(const vector<int> &nums, int begin, int end, int target){
20-
if(begin<0 || begin>=end || end>nums.size()){
21-
return -1;
22-
}
23-
int mid = begin + (end-1 - begin)/2;
18+
```c++
19+
// 在 nums[begin, end) 中查找 target
20+
int binary_search(const vector<int> &nums, int begin, int end, int target){
21+
if(begin<0 || begin>=end || end>nums.size()){
22+
return -1;
23+
}
24+
int mid = begin + (end-1 - begin)/2;
25+
if(nums[mid] > target){
26+
return binary_search(nums, begin, mid, target);
27+
}
28+
else if(nums[mid] < target){
29+
return binary_search(nums, mid+1, end, target);
30+
}
31+
else{
32+
return mid;
33+
}
34+
}
35+
```
36+
37+
循环实现如下:
38+
39+
```c++
40+
int binary_search(const vector<int> &nums, int target){
41+
int left = 0;
42+
int right = nums.size() - 1;
43+
while(left <= right){
44+
int mid = left + (right-left) / 2; // 防止溢出
45+
// int mid = (left+right)/2
2446
if(nums[mid] > target){
25-
return binary_search(nums, begin, mid, target);
47+
right = mid - 1;
2648
}
2749
else if(nums[mid] < target){
28-
return binary_search(nums, mid+1, end, target);
50+
left = mid + 1;
2951
}
3052
else{
3153
return mid;
3254
}
3355
}
34-
35-
循环实现如下:
36-
37-
int binary_search(const vector<int> &nums, int target){
38-
int left = 0;
39-
int right = nums.size() - 1;
40-
while(left <= right){
41-
int mid = left + (right-left) / 2; // 防止溢出
42-
// int mid = (left+right)/2
43-
if(nums[mid] > target){
44-
right = mid - 1;
45-
}
46-
else if(nums[mid] < target){
47-
left = mid + 1;
48-
}
49-
else{
50-
return mid;
51-
}
52-
}
53-
return -1; // 没有找到target
54-
}
56+
return -1; // 没有找到target
57+
}
58+
```
5559

5660
这里说是二分查找,其实严格来说是三分,mid 值有三种情况,分别是大于、小于、等于,对应这三种情况分别更新 left、right,或者返回最终结果。
5761

@@ -61,45 +65,49 @@
6165

6266
**对于排序的数组进行查找,二分查找总是应该首先考虑的解决办法。**
6367

64-
## 1 统计数字在排序数组中出现的次数
68+
## 统计数字在排序数组中出现的次数
6569

6670
简单思路,先用二分查找算法找到一个 target,由于 target 可能出现多次,于是在找到的位置向左右顺序扫描,分别找出第一个和最后一个 target。最差情况下,扫描的时间复杂度能达到O(n)。因此这种方法和从头到尾扫描整个数组统计出现的次数的方法是一样的,效率不够高。
6771

6872
因此可以考虑用二分查找直接找出重复出现的数字的第一个位置和最后一个位置。找出数字第一次出现位置的实现如下,保证了最坏情况下时间复杂度仍然为 O(logn):
6973

70-
// 在 nums[begin, end) 中查找 target
71-
int getFirstK(int nums[], int length, int k){
72-
int left = 0;
73-
int right = length-1;
74-
while(left<=right){
75-
int mid = left + (right-left)/2;
76-
if(nums[mid] < k){
77-
left = mid + 1;
78-
}
79-
else if(nums[mid] > k){
80-
right = mid - 1;
74+
```c++
75+
// 在 nums[begin, end) 中查找 target
76+
int getFirstK(int nums[], int length, int k){
77+
int left = 0;
78+
int right = length-1;
79+
while(left<=right){
80+
int mid = left + (right-left)/2;
81+
if(nums[mid] < k){
82+
left = mid + 1;
83+
}
84+
else if(nums[mid] > k){
85+
right = mid - 1;
86+
}
87+
else{
88+
if(mid > 0 && nums[mid-1] == k){
89+
right = mid-1; // 不断向左逼近
8190
}
8291
else{
83-
if(mid > 0 && nums[mid-1] == k){
84-
right = mid-1; // 不断向左逼近
85-
}
86-
else{
87-
return mid;
88-
}
92+
return mid;
8993
}
9094
}
91-
return -1;
9295
}
96+
return -1;
97+
}
98+
```
9399
94100
找到最后一次出现位置的代码,只需要在 mid == k 时稍作处理即可。
95101
96-
if(mid+1 < length && nums[mid+1] == k){
97-
left = mid+1;
98-
}
102+
```c++
103+
if(mid+1 < length && nums[mid+1] == k){
104+
left = mid+1;
105+
}
106+
```
99107

100108
Leetcode 上面有一个类似的题目,找出目标数字的最先和最后出现:[34 Search for a Range](https://leetcode.com/problems/search-for-a-range/)
101109

102-
## 2 旋转数组的最小数字
110+
## 旋转数组的最小数字
103111

104112
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个**非递减序列**的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
105113

@@ -110,45 +118,82 @@ Leetcode 上面有一个类似的题目,找出目标数字的最先和最后
110118

111119
判断出 mid 的位置后,需要恰当地去更新 left,right 的值,更新策略的选择将会直接决定 while 循环结束条件。这里我们让 left 位于旋转数组的左半部分,right位于右半部分,循环条件为 left < right。对于left 的更新每次多向右多移动一个位置:mid+1,right 每次更新为 mid,这样当循环终止时,left和right值相同,并且left指向了最小的数字。
112120

113-
int findMin(vector<int>& nums) {
114-
int left = 0;
115-
int right = nums.size()-1;
116-
117-
while(left<right){
118-
// When there is no rotate, just return self.nums[start]
119-
if(nums[left]<nums[right]) return nums[left];
120-
int mid = left + (right-left) / 2;
121-
if(nums[left] <= nums[mid]){
122-
left = mid+1;
123-
}
124-
else{
125-
right = mid;
126-
}
121+
```c++
122+
int findMin(vector<int>& nums) {
123+
int left = 0;
124+
int right = nums.size()-1;
125+
126+
while(left<right){
127+
// When there is no rotate, just return self.nums[start]
128+
if(nums[left]<nums[right]) return nums[left];
129+
int mid = left + (right-left) / 2;
130+
if(nums[left] <= nums[mid]){
131+
left = mid+1;
132+
}
133+
else{
134+
right = mid;
127135
}
128-
return nums[left];
129136
}
137+
return nums[left];
138+
}
139+
```
130140
131141
如果数组中有重复的数字,那么情况稍微有点复杂,当nums[mid] == nums[left] 时,我们没法判断 mid 在哪一边,因此选择对 left 进行简单的 +1 操作。
132142
133-
int findMin(vector<int>& nums) {
134-
int left=0;
135-
int right=nums.size()-1;
136-
while(left<right){
137-
if(nums[left] < nums[right]) return nums[left];
143+
```c++
144+
int findMin(vector<int>& nums) {
145+
int left=0;
146+
int right=nums.size()-1;
147+
while(left<right){
148+
if(nums[left] < nums[right]) return nums[left];
149+
int mid = left + (right-left)/2;
150+
if(nums[left] < nums[mid]){
151+
left = mid+1;
152+
}
153+
else if(nums[left] > nums[mid]){
154+
right = mid;
155+
}
156+
else{
157+
left += 1;
158+
}
159+
}
160+
return nums[left];
161+
}
162+
```
163+
164+
简单思路,先用二分查找算法找到一个 target,由于 target 可能出现多次,于是在找到的位置向左右顺序扫描,分别找出第一个和最后一个 target。最差情况下,扫描的时间复杂度能达到O(n)。因此这种方法和从头到尾扫描整个数组统计出现的次数的方法是一样的,效率不够高。
165+
166+
因此可以考虑用二分查找直接找出重复出现的数字的第一个位置和最后一个位置。找出数字第一次出现位置的实现如下,保证了最坏情况下时间复杂度仍然为 O(logn):
167+
168+
// 在 nums[begin, end) 中查找 target
169+
int getFirstK(int nums[], int length, int k){
170+
int left = 0;
171+
int right = length-1;
172+
while(left<=right){
138173
int mid = left + (right-left)/2;
139-
if(nums[left] < nums[mid]){
140-
left = mid+1;
174+
if(nums[mid] < k){
175+
left = mid + 1;
141176
}
142-
else if(nums[left] > nums[mid]){
143-
right = mid;
177+
else if(nums[mid] > k){
178+
right = mid - 1;
144179
}
145180
else{
146-
left += 1;
181+
if(mid > 0 && nums[mid-1] == k){
182+
right = mid-1; // 不断向左逼近
183+
}
184+
else{
185+
return mid;
186+
}
147187
}
148188
}
149-
return nums[left];
189+
return -1;
150190
}
151191

192+
找到最后一次出现位置的代码,只需要在 mid == k 时稍作处理即可。
193+
194+
if(mid+1 < length && nums[mid+1] == k){
195+
left = mid+1;
196+
}
152197

153198
## [74 Search a 2D Matrix](https://leetcode.com/problems/search-a-2d-matrix/)
154199

@@ -160,10 +205,10 @@ Leetcode 上面有一个类似的题目,找出目标数字的最先和最后
160205
161206
将二维数组看做是排好序的一维数组,然后按照一般的二分查找即可。注意left,right 开始值分别为 0, rows*cols-1,mid 的坐标为 (mid/cols, mid%cols)。
162207

163-
[具体实现](https://github.com/xuelangZF/LeetCode/blob/master/BinarySearch/74_Search2DMatrix.py)
208+
[具体代码](https://github.com/xuelangZF/LeetCode/blob/master/BinarySearch/74_Search2DMatrix.py)
164209

210+
# 更多阅读
165211

166-
# 参考
167212
[Binary search algorithm](https://en.wikipedia.org/wiki/Binary_search_algorithm)
168213
[数据库中二分查找相关问题](http://hedengcheng.com/?p=595)
169214

DynamicProgramming/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,5 +107,5 @@
107107
[Dynamic Programming:From Novice to Advanced](https://www.topcoder.com/community/data-science/data-science-tutorials/dynamic-programming-from-novice-to-advanced/)
108108
[什么是动态规划?动态规划的意义是什么?](https://www.zhihu.com/question/23995189/answer/35429905)
109109
[动态规划与状态机:最大子序列和问题的扩展](http://liam0205.me/2016/05/13/dynamic-programming-and-state-machine/)
110-
110+
[hiho一下第113周《Fibonacci》题目分析](http://hihocoder.com/discuss/question/3634)
111111

0 commit comments

Comments
 (0)