Skip to content

Commit 10c3213

Browse files
committed
030
1 parent fa1760a commit 10c3213

File tree

7 files changed

+198
-13
lines changed

7 files changed

+198
-13
lines changed

016-反转链表/problem016.go

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,14 @@ type NodeList struct {
1212
func reverse(head *NodeList) *NodeList {
1313
if head == nil || head.Next == nil { return head }
1414
vide := &NodeList{-1, nil}
15-
// first
16-
vide.Next = head
17-
next := head.Next
18-
head.Next = nil
19-
head = next
20-
next = next.Next
21-
// rest
22-
for next != nil {
15+
16+
for head != nil {
17+
next := head.Next
2318
head.Next = vide.Next
2419
vide.Next = head
2520
head = next
26-
next = next.Next
2721
}
2822

29-
// last one
30-
head.Next = vide.Next
31-
vide.Next = head
32-
3323
return vide.Next
3424

3525
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# 题意
2+
3+
题目描述
4+
5+
输入一个字符串,按字典序打印出该字符串中字符的所有排列
6+
7+
例如输入字符串abc,
8+
9+
则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
10+
11+
结果请按字母顺序输出。
12+
13+
注意 输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母
14+
15+
样例输入
16+
17+
1 2 3 2 2 2 5 4 2
18+
19+
样例输出
20+
21+
2
22+
23+
# 分析
24+
25+
- 排序以后统计个数
26+
- 基于Partition函数的$O(n)$的查找第K大的数
27+
- 阵地攻守(特点-它出现的次数比其他所有的数组出现的次数之和还要多)pick
28+
- 使用map来建立数字到出现次数的映射
29+
30+
31+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package problem010
2+
3+
import "errors"
4+
5+
func getMostFreq(nums []int) (int, error){
6+
if len(nums) == 0 {
7+
return -1, errors.New("Array is empty")
8+
}
9+
count := 1
10+
value := nums[0]
11+
for i := 1; i < len(nums); i++ {
12+
if nums[i] == value {
13+
count++
14+
} else {
15+
count--
16+
if count == 0 {
17+
value = nums[i]
18+
count = 1
19+
}
20+
}
21+
}
22+
return value, nil
23+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package problem010
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func Test_case(t *testing.T) {
8+
nums := []int{2,1,2,1,2}
9+
res, err := getMostFreq(nums)
10+
if res == 2 {
11+
t.Log("Pass")
12+
} else {
13+
t.Error("Failed")
14+
}
15+
16+
nums = []int{1,2,2,2,2,6,7}
17+
res, err = getMostFreq(nums)
18+
if res == 2 {
19+
t.Log("Pass")
20+
} else {
21+
t.Error("Failed")
22+
}
23+
24+
nums = []int{1}
25+
res, err = getMostFreq(nums)
26+
if res == 1 {
27+
t.Log("Pass")
28+
} else {
29+
t.Error("Failed")
30+
}
31+
32+
nums = []int{}
33+
res, err = getMostFreq(nums)
34+
if err != nil {
35+
t.Log("Pass")
36+
} else {
37+
t.Error("Failed")
38+
}
39+
40+
}
41+

030-最小的K个数/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#题意
2+
题目描述
3+
4+
输入n个整数,找出其中最小的K个数。
5+
6+
例如输入4,5,1,6,2,7,3,8这8个数字, 则最小的4个数字是1,2,3,4,。
7+
8+
#分析
9+
10+
##方法一--排序
11+
要求一个序列中最小的K个数,按照惯有的思维方式,很简单,先对这个序列从小到大排序,然后输出前面的最小的K个数即可;
12+
13+
至于选取什么样的排序方法,第一时间应该想到的是快速排序,我们知道,快速排序平均时间复杂度为O(nlogn),然后再遍历序列中前K个元素输出,即可,总的时间复杂度为O(nlogn + k) = O(nlogn);——方法一
14+
15+
##方法二--选择或者交换排序
16+
再进一步想想,题目并没有要求要查找的k个数,甚至是后面的n-k个数是有序的,既然这样,咱们又何必对所有的n个数都进行排序呢? 这个时候,想到了选择或交换排序,即遍历n个数,先把最先遍历到的K个数存入大小为k的数组之中,对这k个数,利用选择或交换排序,找到k个数中的最大数Kmax(Kmax为这K个元素的数组中最大的元素),用时间为O(k)(你应该知道,插入或选择排序查找操作需要O(k)的时间),后再继续遍历后n-k个数,x与Kmax比较:如果x< Kmax,则x代替Kmax,并再次重新找出K个元素的数组中的最大元素Kmax';如果x>Kmax,则不更新数组。这样每次更新和不更新数组所用的时间为O(k)或O(0),整趟下来,总的时间复杂度平均下来为:nO(k) = O(nk);——方法二
17+
18+
##方法三--最小堆
19+
当然,更好的办法是维护k个元素的最大堆,原理与上述第2个方案一致,即用容量为K的最大堆存储最先遍历的K个数,并假设它们即是最小的K个数,建堆需要O(k)后,有k1)。继续遍历数列,每次遍历一个元素x,与堆顶元素比较,x),否则不更新堆。这样下来,总费时O(k+(n-k)logk) = O(nlogk)。此方法得益于在堆中,查找等各项操作时间复杂度均为logk(不然,就如上述思路2所述:直接用数组也可以找出前k个小的元素,用时O(nk));
20+
21+
##方法四--快速排序的分治划分(中位数作为枢轴)
22+
按编程之美第141页上解法二的所述,类似快速排序的划分方法,N个数存储在数组S中,再从数组中随机选取一个数X(随机选取枢纽元,可做到线性期望时间O(N)的复杂度),把数组划分为Sa和Sb两部分,Sa<= X <=Sb,如果要查找的K个小的元素小于Sa中的元素个数,则返回Sa中较小的K个元素,否则返回Sa中K个小的元素 + Sb中小的K-|Sa|个元素。像上述过程一样,这个运用类似快速排序的partition的快速选择Select算法寻找最小的K个元素,在最坏的情况下亦能做到O(N)的复杂度。
23+
24+
不过值得一提的是,这个快速选择Select算法是选择数组中“中位数的中位数”作为枢纽元,而非随机选择枢纽元;
25+
26+
##方法五--快速排序的分治划分(随机枢轴)
27+
Randomized-Select,每次都是随机选择数列中的一个元素作为主元,在O(n)的时间内找到第K小的元素,然后遍历输出前面的K个小的元素。如果能的话,那么总的时间复杂度为线性期望时间:O(n+k) = O(n)(当n比较小时);
28+
29+
##方法六--线性排序
30+
线性时间的排序,即计数排序,时间复杂度虽能达到O(n),但是,限制条件太多了,不常用;
31+
32+
##方法七--最小堆与优先队列
33+
”可以用最小堆初始化数组,然后取这个优先队列前k个值。复杂度为O(n)+kO(logn)“。意思是针对整个数组序列建立最小堆,建堆所用时间为O(n),然后取堆中的前k个数,即总的时间复杂度为:O(n+klogn)。
34+
35+
##方法八--提取最小堆的元素
36+
与上述思路7类似,不同的是在对元素数组原地建立最小堆O(n)后,然后提取K次,但是每次提取时,换到顶部的元素只需要下移顶多K次就足够了,下移次数逐次减少(而上述思路7每次提取都需要logn,所有提取K次,思路7需要K*logn,而本思路8只需要K^2);

030-最小的K个数/problem030.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package problem010
2+
3+
import "errors"
4+
5+
func getMostFreq(nums []int) (int, error){
6+
if len(nums) == 0 {
7+
return -1, errors.New("Array is empty")
8+
}
9+
count := 1
10+
value := nums[0]
11+
for i := 1; i < len(nums); i++ {
12+
if nums[i] == value {
13+
count++
14+
} else {
15+
count--
16+
if count == 0 {
17+
value = nums[i]
18+
count = 1
19+
}
20+
}
21+
}
22+
return value, nil
23+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package problem010
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func Test_case(t *testing.T) {
8+
nums := []int{2,1,2,1,2}
9+
res, err := getMostFreq(nums)
10+
if res == 2 {
11+
t.Log("Pass")
12+
} else {
13+
t.Error("Failed")
14+
}
15+
16+
nums = []int{1,2,2,2,2,6,7}
17+
res, err = getMostFreq(nums)
18+
if res == 2 {
19+
t.Log("Pass")
20+
} else {
21+
t.Error("Failed")
22+
}
23+
24+
nums = []int{1}
25+
res, err = getMostFreq(nums)
26+
if res == 1 {
27+
t.Log("Pass")
28+
} else {
29+
t.Error("Failed")
30+
}
31+
32+
nums = []int{}
33+
res, err = getMostFreq(nums)
34+
if err != nil {
35+
t.Log("Pass")
36+
} else {
37+
t.Error("Failed")
38+
}
39+
40+
}
41+

0 commit comments

Comments
 (0)