Skip to content

Commit f0450a1

Browse files
committed
[Feature] add for new
1 parent 6aa0b83 commit f0450a1

11 files changed

+3375
-0
lines changed

src/posts/leetcode/sorts/2016-07-14-sort-00-overview-sort.md

Lines changed: 405 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 376 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
---
2+
3+
title: sort-01-bubble sort 冒泡排序算法详解
4+
date: 2016-07-14
5+
categories: [Algorithm]
6+
tags: [sort, exchange-sorts]
7+
published: true
8+
---
9+
10+
11+
# 排序系列
12+
13+
[sort-00-排序算法汇总](https://houbb.github.io/2016/07/14/sort-00-overview-sort)
14+
15+
[sort-01-bubble sort 冒泡排序算法详解](https://houbb.github.io/2016/07/14/sort-01-bubble-sort)
16+
17+
[sort-02-QuickSort 快速排序到底快在哪里?](https://houbb.github.io/2016/07/14/sort-02-quick-sort)
18+
19+
[sort-03-SelectSort 选择排序算法详解](https://houbb.github.io/2016/07/14/sort-03-select-sort)
20+
21+
[sort-04-heap sort 堆排序算法详解](https://houbb.github.io/2016/07/14/sort-04-heap-sort)
22+
23+
[sort-05-insert sort 插入排序算法详解](https://houbb.github.io/2016/07/14/sort-05-insert-sort)
24+
25+
[sort-06-shell sort 希尔排序算法详解](https://houbb.github.io/2016/07/14/sort-06-shell-sort)
26+
27+
[sort-07-merge sort 归并排序](https://houbb.github.io/2016/07/14/sort-07-merge-sort)
28+
29+
[sort-08-counting sort 计数排序](https://houbb.github.io/2016/07/14/sort-08-counting-sort)
30+
31+
[sort-09-bucket sort 桶排序](https://houbb.github.io/2016/07/14/sort-09-bucket-sort)
32+
33+
[sort-10-bigfile 大文件外部排序](https://houbb.github.io/2016/07/14/sort-10-bigfile-sort)
34+
35+
36+
# 创作目的
37+
38+
最近想系统整理一下数据库的索引系列,然后就牵扯到了二分查找,二分查找的前提需要排序。
39+
40+
排序工作中我们用的很多,不过很少去关心实现;面试中,排序的出场率非常高,以此来验证大家是否懂得“算法”。
41+
42+
无论如何,排序算法都值得每一位极客去学习和掌握。
43+
44+
# 冒泡排序
45+
46+
冒泡排序(英语:Bubble Sort)又称为泡式排序,是一种简单的排序算法。
47+
48+
它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
49+
50+
冒泡排序对 n 个项目需要 O(n^2) 的比较次数,且可以原地排序。
51+
52+
尽管这个算法是最简单了解和实现的排序算法之一,但它对于包含大量的元素的数列排序是很没有效率的。
53+
54+
## 流程
55+
56+
冒泡排序算法的运作如下:
57+
58+
1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
59+
60+
2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
61+
62+
3. 针对所有的元素重复以上的步骤,除了最后一个。
63+
64+
4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
65+
66+
由于它的简洁,冒泡排序通常被用来对于程序设计入门的学生介绍算法的概念。
67+
68+
![排序过程](https://en.wikipedia.org/wiki/File:Bubble-sort-example-300px.gif)
69+
70+
# 代码实现
71+
72+
## 已有实现的不足
73+
74+
冒泡排序的文章和实现在网上有很多,讲解的也很详细。
75+
76+
此处只补充一下自己觉得不足的地方:
77+
78+
(1)大部分实现都只是一个 int 比较的例子,实用性不强。
79+
80+
(2)没有统一的接口,不便于后期的统一拓展和自适应。(根据不同的数量,选择不同的算法)
81+
82+
(3)没有自适应性的日志输出,不便于学习。
83+
84+
## 接口定义
85+
86+
基于上面 3 点,我们做一点小小的改进。
87+
88+
第一步:统一定义一个排序的接口。
89+
90+
```java
91+
package com.github.houbb.sort.api;
92+
93+
import java.util.List;
94+
95+
/**
96+
* 排序接口
97+
* @author binbin.hou
98+
* @since 0.0.1
99+
*/
100+
public interface ISort {
101+
102+
/**
103+
* 排序
104+
* @param original 原始列表
105+
* @since 0.0.1
106+
*/
107+
void sort(List<?> original);
108+
109+
}
110+
```
111+
112+
## 抽象实现
113+
114+
为了便于后期拓展,统一实现一个抽象父类:
115+
116+
```java
117+
package com.github.houbb.sort.core.api;
118+
119+
import com.github.houbb.heaven.util.util.CollectionUtil;
120+
import com.github.houbb.sort.api.ISort;
121+
122+
import java.util.List;
123+
124+
/**
125+
* 抽象排序实现
126+
* @author binbin.hou
127+
* @since 0.0.1
128+
*/
129+
public abstract class AbstractSort implements ISort {
130+
131+
@Override
132+
public void sort(List<?> original) {
133+
//fail-return
134+
if(CollectionUtil.isEmpty(original) || original.size() == 1) {
135+
return;
136+
}
137+
138+
doSort(original);
139+
}
140+
141+
/**
142+
* 执行排序
143+
* @param original 原始结果
144+
* @since 0.0.1
145+
*/
146+
protected abstract void doSort(List<?> original);
147+
148+
}
149+
```
150+
151+
152+
这里很简单,针对空列表,或者大小为1的列表,无需进行排序。
153+
154+
## 冒泡排序
155+
156+
接下来我们实现以下冒泡排序即可:
157+
158+
有几点需要说明下:
159+
160+
(1)这里是对 Comparable 对象的支持,本质上和常见的 int 比较一样,这样的适用范围更加广泛一些。
161+
162+
(2)这里是基于 java 的 list 进行排序,因为个人认为 list 的出场率是高于数组的,当然大家如果想实现数组版本的,也是类似的。
163+
164+
(3)changeFlag 也就是我们常说的针对冒泡排序的优化,如果没有变更,说明排序完成,可以直接返回了。
165+
166+
```java
167+
package com.github.houbb.sort.core.api;
168+
169+
import com.github.houbb.heaven.annotation.ThreadSafe;
170+
import com.github.houbb.log.integration.core.Log;
171+
import com.github.houbb.log.integration.core.LogFactory;
172+
173+
import java.util.List;
174+
175+
/**
176+
* 冒泡排序
177+
* @author binbin.hou
178+
* @since 0.0.1
179+
*/
180+
@ThreadSafe
181+
public class BubbleSort extends AbstractSort {
182+
183+
private static final Log log = LogFactory.getLog(BubbleSort.class);
184+
185+
@Override
186+
@SuppressWarnings("unchecked")
187+
public void doSort(List<?> original) {
188+
boolean changeFlag;
189+
190+
for(int i = 0; i < original.size()-1; i++) {
191+
changeFlag = false;
192+
193+
for(int j = 0; j < original.size()-1-i; j++) {
194+
// 如果 j > j+1
195+
Comparable current = (Comparable) original.get(j);
196+
Comparable next = (Comparable) original.get(j+1);
197+
if(current.compareTo(next) > 0) {
198+
swap(original, j, j+1);
199+
changeFlag = true;
200+
}
201+
}
202+
203+
// 如果没发生置换,说明后面已经排序完成
204+
if(!changeFlag) {
205+
return;
206+
}
207+
}
208+
}
209+
210+
/**
211+
* 执行数据的交换
212+
* @param original 原始
213+
* @param i 第一个
214+
* @param j 第二个
215+
* @since 0.0.1
216+
*/
217+
@SuppressWarnings("unchecked")
218+
private void swap(List original,
219+
int i, int j) {
220+
Object temp = original.get(i);
221+
original.set(i, original.get(j));
222+
original.set(j, temp);
223+
}
224+
225+
}
226+
```
227+
228+
## 工具类
229+
230+
为了让这个实现类使用起来更加方便,我们就模仿一下 jdk 中的方法。提供一个工具类:
231+
232+
```java
233+
package com.github.houbb.sort.core.util;
234+
235+
import com.github.houbb.heaven.support.instance.impl.Instances;
236+
import com.github.houbb.sort.core.api.BubbleSort;
237+
238+
import java.util.List;
239+
240+
/**
241+
* 排序工具类
242+
* @author binbin.hou
243+
* @since 0.0.1
244+
*/
245+
public final class SortHelper {
246+
247+
private SortHelper(){}
248+
249+
/**
250+
* 冒泡排序
251+
* @param <T> 泛型
252+
* @param list 列表
253+
* @since 0.0.1
254+
*/
255+
public static <T extends Comparable<? super T>> void bubble(List<T> list) {
256+
Instances.singleton(BubbleSort.class).sort(list);
257+
}
258+
259+
}
260+
```
261+
262+
# 测试
263+
264+
我们来验证一下排序算法。
265+
266+
## 测试代码
267+
268+
```java
269+
List<Integer> list = RandomUtil.randomList(5);
270+
System.out.println("开始排序:" + list);
271+
SortHelper.bubble(list);
272+
```
273+
274+
其中 RandomUtil 是一个随机生成的工具,便于我们测试,实现如下:
275+
276+
```java
277+
package com.github.houbb.sort.core.util;
278+
279+
import java.util.ArrayList;
280+
import java.util.List;
281+
import java.util.Random;
282+
import java.util.concurrent.ThreadLocalRandom;
283+
284+
/**
285+
* @author binbin.hou
286+
* @since 0.0.1
287+
*/
288+
public final class RandomUtil {
289+
290+
private RandomUtil(){}
291+
292+
/**
293+
* 随机列表
294+
* @param size 大小
295+
* @return 结果
296+
* @since 0.0.1
297+
*/
298+
public static List<Integer> randomList(final int size) {
299+
List<Integer> list = new ArrayList<>(size);
300+
301+
Random random = ThreadLocalRandom.current();
302+
for(int i = 0; i < size; i++) {
303+
list.add(random.nextInt(100));
304+
}
305+
306+
return list;
307+
}
308+
}
309+
```
310+
311+
## 日志
312+
313+
为了便于理解,我们可以在排序实现中加一点日志:
314+
315+
```java
316+
if(current.compareTo(next) > 0) {
317+
swap(original, j, j+1);
318+
changeFlag = true;
319+
if(log.isDebugEnabled()) {
320+
String format = String.format("i=%s, j=%s, c=%s, n=%s, 排序后: %s",
321+
i, j, current, next, original);
322+
log.debug(format);
323+
}
324+
} else {
325+
if(log.isDebugEnabled()) {
326+
String format = String.format("i=%s, j=%s, c=%s, n=%s, 无变化: %s",
327+
i, j, current, next, original);
328+
log.debug(format);
329+
}
330+
}
331+
```
332+
333+
i,j 代表本次循环的 i,j; c 代表当前值,n 代表 next 下一个值。
334+
335+
测试日志如下:
336+
337+
```
338+
开始排序:[77, 48, 10, 8, 28]
339+
340+
i=0, j=0, c=77, n=48, 排序后: [48, 77, 10, 8, 28]
341+
i=0, j=1, c=77, n=10, 排序后: [48, 10, 77, 8, 28]
342+
i=0, j=2, c=77, n=8, 排序后: [48, 10, 8, 77, 28]
343+
i=0, j=3, c=77, n=28, 排序后: [48, 10, 8, 28, 77] -- 第一次冒泡,把最大的 77 排序到最右侧
344+
345+
i=1, j=0, c=48, n=10, 排序后: [10, 48, 8, 28, 77]
346+
i=1, j=1, c=48, n=8, 排序后: [10, 8, 48, 28, 77]
347+
i=1, j=2, c=48, n=28, 排序后: [10, 8, 28, 48, 77] -- 第二次冒泡,把第二大的 48 排序到最右侧
348+
349+
i=2, j=0, c=10, n=8, 排序后: [8, 10, 28, 48, 77] -- 这次排序结束后, 28 和 10 已经放在了对应的位置
350+
i=2, j=1, c=10, n=28, 无变化: [8, 10, 28, 48, 77]
351+
352+
i=3, j=0, c=8, n=10, 无变化: [8, 10, 28, 48, 77]
353+
```
354+
355+
这个算法,是把小的值放在前面,所以只有当 c > n 的时候,才会发生排序。
356+
357+
最后 i = 6, j = 2 的时候,已经排序完成。
358+
359+
# 开源地址
360+
361+
为了便于大家学习,上面的排序已经开源,开源地址:
362+
363+
> [https://github.com/houbb/sort](https://github.com/houbb/sort)
364+
365+
欢迎大家 fork/star,鼓励一下作者~~
366+
367+
# 小结
368+
369+
希望本文对你有帮助,如果有其他想法的话,也可以评论区和大家分享哦。
370+
371+
各位**极客**的点赞收藏转发,是老马持续写作的最大动力!
372+
373+
# 参考资料
374+
375+
[Bubble_sort](https://en.wikipedia.org/wiki/Bubble_sort)
376+

0 commit comments

Comments
 (0)