Fork me on GitHub

ARTS-8

ARTS是由左耳朵耗子陈皓在极客时间专栏《左耳听风》中发起的一个每周学习打卡计划。

1
2
3
4
5
6
7
Algorithm:至少做一个 LeetCode 的算法题。主要为了编程训练和学习。

Review:阅读并点评至少一篇英文技术文章。主要为了学习英文,如果你英文不行,很难成为技术高手。

Tip:学习至少一个技术技巧。主要是为了总结和归纳你日常工作中所遇到的知识点。

Share:分享一篇有观点和思考的技术文章。主要为了输出你的影响力,能够输出你的价值观。

Algorithm(算法)

leetcode 958 完全二叉树的判断

https://leetcode-cn.com/problems/check-completeness-of-a-binary-tree/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* @Description https://www.youtube.com/watch?v=xxIpSooeZfQ
* @Author Gao Hang Hang
* @Date 2019-11-06 00:20
**/
public class Solution {
/*
思路:
bfs 广度优先
left -> right 一旦 null,后面全 null
时间复杂度:O(N),因为遍历了二叉树
空间复杂度:O(N),使用Queue
*/
public boolean isCompleteTree(TreeNode root) {
// corner case
if (root == null) return true;
// 是否遇到过空节点,true:遇到过null,false:没遇到过null
boolean end = false;
Queue<TreeNode> q = new LinkedList<>();
// offer 添加一个元素并返回true 如果队列已满,则返回false
q.offer(root);
while (!q.isEmpty()) {
// left -> right
TreeNode node = q.poll(); // poll 移除并返问队列头部的元素 如果队列为空,则返回null
// null
if (node == null) {
end = true;
} else { // not null
if (end) return false;
// 添加左节点
q.offer(node.left);
// 添加右节点
q.offer(node.right);
}
}
return true;
}
}

Review(点评)

Tip(技巧)

url命名

https://blog.csdn.net/belalds/article/details/80060296

https://xovel.cn/article/naming-rule.html

https://blog.csdn.net/ideality_hunter/article/details/82109940

https://juejin.im/entry/584a1ae761ff4b0058d08bf1

脊柱命名法(spinal-case)
脊柱命名法是使用连字符 “-“ 分割单词的一种蛇形命名法的变体. 它的优点与缺点跟蛇形命名法非常相似, 只是有项例外是有的语言不允许使用连字符用来定义符号名称 (例如声明变量,class起名或函数命名时). 你可能会发现它参考了lisp命名的写法,因为lisp方言就是这么做的. 这种命名法也是Unix和Linux系统中传统的文件夹命名方式. 例如: spinal-case, current-user, 等等.

根据文档 RFC3986 规定, URL是大小写敏感的 (除了主机头等信息).
实际情况中,大小写敏感问题可能会对在Windows环境托管的API导致出现问题.

所以建议使用脊柱命名法(spinal-case) ( RFC3986 文档中突出强调了这一点), 这种命名法也被 Google, PayPal 和其他大公司使用.

Share(分享)

《阿里巴巴 Java 开发手册》第五章MySQL数据库解读

Spring Boot 高效数据聚合之道

原文地址: Spring Boot 高效数据聚合之道

项目地址和示例代码: https://github.com/lvyahui8/spring-boot-data-aggregator

1. 背景

接口开发是后端开发中最常见的场景,可能是 RESTFul 接口,也可能是 RPC 接口. 接口开发往往是从各处捞出数据,然后组装成结果,特别是那些偏业务的接口.

如何方便快速的开发高性能的接口 , 是一个必须思考的问题.

例如,我现在需要实现一个接口,拉取用户基础信息 + 用户的博客列表 + 用户的粉丝数据的整合数据,假设已经有如下三个接口可以使用,分别用来获取 用户基础信息 , 用户博客列表 , 用户的粉丝数据.

用户基础信息

1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class UserServiceImpl implements UserService {
@Override
public User get(Long id) {
try {Thread.sleep(1000L);} catch (InterruptedException e) {}
/* mock a user*/
User user = new User();
user.setId(id);
user.setEmail("lvyahui8@gmail.com");
user.setUsername("lvyahui8");
return user;
}
}

用户博客列表

1
2
3
4
5
6
7
8
9
10
11
@Service
public class PostServiceImpl implements PostService {
@Override
public List<Post> getPosts(Long userId) {
try { Thread.sleep(1000L); } catch (InterruptedException e) {}
Post post = new Post();
post.setTitle("spring data aggregate example");
post.setContent("No active profile set, falling back to default profiles");
return Collections.singletonList(post);
}
}

用户的粉丝数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
public class FollowServiceImpl implements FollowService {
@Override
public List<User> getFollowers(Long userId) {
try { Thread.sleep(1000L); } catch (InterruptedException e) {}
int size = 10;
List<User> users = new ArrayList<>(size);
for(int i = 0 ; i < size; i++) {
User user = new User();
user.setUsername("name"+i);
user.setEmail("email"+i+"@fox.com");
user.setId((long) i);
users.add(user);
};
return users;
}
}

注意,每一个方法都 sleep 了 1s 以模拟业务耗时.

我们需要再封装一个接口,来拼装以上三个接口的数据.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
public class UserQueryFacade {
@Autowired
private FollowService followService;
@Autowired
private PostService postService;
@Autowired
private UserService userService;

public User getUserData(Long userId) {
User user = userService.get(userId);
user.setPosts(postService.getPosts(userId));
user.setFollowers(followService.getFollowers(userId));
return user;
}
}

很明显,上面的代码,效率低下,起码要 3s 才能拿到结果 , 且一旦用到某个接口的数据,便需要注入相应的 service, 复用麻烦.

2. 并行实现

有追求的程序员可能立马会考虑到,这几项数据之间并无强依赖性,完全可以并行获取嘛,通过异步线程 + CountDownLatch+Future 实现,就像下面这样.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Component
public class UserQueryFacade {
@Autowired
private FollowService followService;
@Autowired
private PostService postService;
@Autowired
private UserService userService;

public User getUserDataByParallel(Long userId) throws InterruptedException, ExecutionException {
// 创建固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
// CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。
CountDownLatch countDownLatch = new CountDownLatch(3); // 参数count为计数值
Future<User> userFuture = executorService.submit(() -> {
try{
return userService.get(userId);
}finally {
countDownLatch.countDown();
}
});
Future<List<Post>> postsFuture = executorService.submit(() -> {
try{
return postService.getPosts(userId);
}finally {
countDownLatch.countDown();
}
});
Future<List<User>> followersFuture = executorService.submit(() -> {
try{
return followService.getFollowers(userId);
}finally {
countDownLatch.countDown();
}
});
// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
countDownLatch.await();
User user = userFuture.get();
user.setFollowers(followersFuture.get());
user.setPosts(postsFuture.get());
return user;
}
}

上面的代码,将串行调用改为并行调用,在有限并发级别下,能极大提高性能. 但很明显,它过于复杂,如果每个接口都为了并行执行都写这样一段代码,简直是噩梦.

3. 优雅的注解实现

熟悉 java 的都知道,java 有一种非常便利的特性~~注解。简直是黑魔法。往往只需要给类或者方法上添加一些注解,便可以实现非常复杂的功能.

有了注解,再结合 Spring 依赖自动注入的思想,那么我们可不可以通过注解的方式,自动注入依赖,自动并行调用接口呢?答案是肯定的.

阅读更多...

HTTP长连接和短连接

原文作者: WhyWin

原文地址: https://www.cnblogs.com/0201zcr/p/4694945.html

参考文章: https://blog.csdn.net/fenglibing/article/details/7100222

思维导图

HTTP长连接和短连接的区别

1. HTTP 协议与 TCP/IP 协议的关系

HTTP 的长连接和短连接本质上是 TCP 长连接和短连接。HTTP 属于应用层协议,在传输层使用 TCP 协议,在网络层使用 IP 协议。

IP 协议主要解决网络路由和寻址问题,TCP 协议主要解决如何在 IP 层之上可靠的传递数据包,使在网络上的另一端收到发端发出的所有包,并且顺序与发出顺序一致。TCP 有可靠,面向连接的特点。

2. 如何理解 HTTP 协议是无状态的

HTTP 协议是无状态的,指的是协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。也就是说,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系。HTTP 是一个无状态的面向连接的协议,无状态不代表 HTTP 不能保持 TCP 连接,更不能代表 HTTP 使用的是 UDP 协议(无连接)。

3. 什么是长连接、短连接?

在 HTTP/1.0 中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次 HTTP 操作,就建立一次连接,但任务结束就中断连接。如果客户端浏览器访问的某个 HTML 或其他类型的 Web 页中包含有其他的 Web 资源,如 JavaScript 文件、图像文件、CSS 文件等;当浏览器每遇到这样一个 Web 资源,就会建立一个 HTTP 会话。

但从 HTTP/1.1 起,默认使用长连接,用以保持连接特性。使用长连接的 HTTP 协议,会在响应头有加入这行代码:

1
Connection:keep-alive

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive 不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如 Apache)中设定这个时间。实现长连接要客户端和服务端都支持长连接。

HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。

3.1 TCP 连接

当网络通信时采用 TCP 协议时,在真正的读写操作之前,server 与 client 之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接 时它们可以释放这个连接,连接的建立是需要三次握手的,而释放则需要 4 次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的

经典的三次握手示意图:

经典的四次握手关闭图:

3.2 TCP 短连接

我们模拟一下 TCP 短连接的情况,client 向 server 发起连接请求,server 接到请求,然后双方建立连接。client 向 server 发送消息,server 回应 client,然后一次读写就完成了,这时候双方任何一个都可以发起 close 操作,不过一般都是 client 先发起 close 操作。为什么呢,一般的 server 不会回复完 client 后立即关闭连接的,当然不排除有特殊的情况。从上面的描述看,短连接一般只会在 client/server 间传递一次读写操作

3.3 TCP 长连接

接下来我们再模拟一下长连接的情况,client 向 server 发起连接,server 接受 client 连接,双方建立连接。Client 与 server 完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。

首先说一下 TCP/IP 详解上讲到的 TCP 保活功能,保活功能主要为服务器应用提供,服务器应用希望知道客户主机是否崩溃,从而可以代表客户使用资源。如果客户已经消失,使得服务器上保留一个半开放的连接,而服务器又在等待来自客户端的数据,则服务器将应远等待客户端的数据,保活功能就是试图在服务 器端检测到这种半开放的连接。

如果一个给定的连接在两小时内没有任何的动作,则服务器就向客户发一个探测报文段,客户主机必须处于以下 4 个状态之一:

  1. 客户主机依然正常运行,并从服务器可达。客户的 TCP 响应正常,而服务器也知道对方是正常的,服务器在两小时后将保活定时器复位。
  2. 客户主机已经崩溃,并且关闭或者正在重新启动。在任何一种情况下,客户的 TCP 都没有响应。服务端将不能收到对探测的响应,并在 75 秒后超时。服务器总共发送 10 个这样的探测 ,每个间隔 75 秒。如果服务器没有收到一个响应,它就认为客户主机已经关闭并终止连接。
  3. 客户主机崩溃并已经重新启动。服务器将收到一个对其保活探测的响应,这个响应是一个复位,使得服务器终止这个连接。
  4. 客户机正常运行,但是服务器不可达,这种情况与 2 类似,TCP 能发现的就是没有收到探查的响应。
阅读更多...

思考 8 反套路化趋势

互联网使得信息变得唾手可得,一些套路化的东西大家会越来越越了解,也会变得越来越反感。

比如价格歧视策略,舔狗行为被人不齿,小鲜肉的电影 🎬,综艺节目的强行煽情。

利用信息查赚钱会变得越来越艰难,利用套路诓人也会越会越来越难。

套路会变得不得人心,反套路反而会深受人们喜爱。

比如卖东西一边搞好服务,一边保证物美价廉,就会有人喜爱并不断安利身边的人。

我为何而活-罗素

节选自《罗素自传》序言

原文地址: 我为何而活:What I have lived for

我为何而活思维导图

Three passions, simple but overwhelmingly strong, have governed my life: the longing for love, the search for knowledge, and unbearable pity for the suffering of mankind.

对爱情的渴望,对知识的追求,对人类苦难不可遏制的同情心,这三种纯洁而无比强烈的激情支配着我的一生。

These passions, like great winds, have blown me hither and thither, in a wayward course, over a deep ocean of anguish, reaching to the verge of despair.

这三种激情,就像飓风一样,在深深的苦海上,肆意地把我吹来吹去,吹到濒临绝望的边缘。

I have sought love, first, because it brings ecstasy — ecstasy so great that I would have sacrificed all the rest of life for a few hours of this joy.

我寻求爱情,首先因为爱情给我带来狂喜,它如此强烈以致我经常愿意为了几小时的欢愉而牺牲生命中的其他一切。

I have sought it, next, because it relieves loneliness — that terrible loneliness in which one shivering consciousness looks over the rim of the world into cold unfathomable lifeless abyss.

我寻求爱情,其次是因为爱情可以解除孤寂一-那是一颗震颤的心,在世界的边缘,俯瞰那冰冷死寂、深不可测的深渊。

I have sought it, finally, because in the union of love I have seen, in a mystic miniature, the prefiguring vision of the heaven that saints and poets have imagined.

我寻求爱情,最后是因为在爱情的结合中,我看到圣徒和诗人们所想像的天堂景象的神秘缩影。

This is what I sought, and though it might seem too good for human life, this is what — at last — I have found.

这就是我所寻求的,虽然它对人生似乎过于美好,然而最终我还是得到了它。

With equal passion I have sought knowledge. I have wished to understand the hearts of men. I have wished to know why the stars shine. And I have tried to apprehend the Pythagorean power by which number holds sway above the flux. A little of this, but not much, I have achieved.

我以同样的热情追求知识,我想理解人类的心灵,我想了解星辰为何灿烂,我还试图弄懂毕达哥拉斯学说的力量,是这种力量使我在无常之上高踞主宰地位。我在这方面略有成就,但不多。

Love and knowledge, so far as they were possible, led upward toward the heavens. But always pity brought me back to earth.

爱情和知识只要存在,总是向上导往天堂。但是,怜悯又总是把我带回人间。

Echoes of cries of pain reverberate in my heart. Children in famine, victims tortured by oppressors, helpless old people a burden to their sons, and the whole world of loneliness, poverty, and pain make a mockery of what human life should be.

痛苦的呼喊在我心中反响回荡,孩子们受饥荒煎熬,无辜者被压迫者折磨,孤弱无助的老人在自己的儿子眼中变成可恶的累赘,以及世上触目皆是的孤独、贫困和痛苦--这些都是对人类应该过的生活的嘲弄。

I long to alleviate this evil, but I cannot, and I too suffer.

我渴望能减少罪恶,可我做不到,于是我感到痛苦。

This has been my life. I have found it worth living, and would gladly live it again if the chance were offered me.

这就是我的一生。我觉得这一生是值得活的,如果真有可能再给我一次机会,我将欣然再重活-次。

思考 7 每个人都应该提升个人影响力

我觉得每个人都应该去提升自己的个人影响力,对于宇宙来说每个人的生命都是如此的短暂,又是如此的微不足道,如果不在世界上留下些痕迹,岂不是枉过这一生。

我们应该通过自己的影响力,让世界变得更好。可以是通过自己的文字,也可以是创造些什么 😎,混沌理论不就说过每个微不足道的改变可能引起翻天覆地的变化。

我的尝试:博客和公众号《骇客与画家》

排序算法

参考书籍《算法第四版》

排序算法可以说是算法基本功了

0 排序算法模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Example {
public static void sort(int[] a) {
/* 请见算法2.1、算法2.second、算法2.3、算法2.4、算法2.5、算法2.6或算法2.7*/
}

private static boolean less(int v, int w) {
return v < w;
}

private static void exch(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}

private static void show(int[] a) {
// 在单行中打印数组
for (int i = 0; i < a.length; i++)
System.out.print(a[i] + " ");
System.out.println();
}

private static boolean isSorted(int[] a) {
// 测试数组元素是否有序
for (int i = 1; i < a.length; i++)
if (less(a[i], a[i - 1])) return false;
return true;
}

public static void main(String[] args) {
// 从标准输入读取字符串,将它们排序并输出
int[] a = {5, 6, 9, 1, 2, 3};
sort(a);
assert isSorted(a);
show(a);
}
}

1 选择排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* 时间复杂度: O(n^second)
* 空间复杂度: O(n^second)
* 稳定性: 不稳定
* 复杂性: 简单
*
* @Description: 选择排序
* @author: Gao Hang Hang
* @date 2019/01/18 19:38
*/
public class Selection {
public static void sort(int[] a) {
// 将a[]按升序排列
int N = a.length; // 数组长度
for (int i = 0; i < N; i++) {
// 将a[i]和a[i+1..N]中最小的元素交换
int min = i;
for (int j = i + 1; j < N; j++)
if (less(a[j], a[min])) min = j;
exch(a, i, min);
}
}

private static boolean less(int v, int w) {
return v < w;
}

private static void exch(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}

private static void show(int[] a) {
// 在单行打印数组
for (int i = 0; i < a.length; i++)
System.out.print(a[i] + " ");
System.out.println();
}

private static boolean isSorted(int[] a) {
// 测试数组元素是否有序
for (int i = 1; i < a.length; i++)
if (less(a[i], a[i - 1])) return false;
return true;
}

public static void main(String[] args) {
int[] nums = {6, 3, 5, 9, 1, 2};
sort(nums);
show(nums);
System.out.println(isSorted(nums));
}
}

2 插入排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* 时间复杂度: O(n^2)
* 空间复杂度: O(n^2)
* 稳定性: 稳定
* 复杂性: 简单
*
* @Description: a2_插入排序
* @author: Gao Hang Hang
* @date 2019/01/02 12:04
*/
public class InsertSort {
public static void sort(int[] a) {
// 将a[]按升序排列
int N = a.length;
for (int i = 0; i < N; i++) {
// 将 a[i] 插入到 a[i-1]、a[i-2]、a[i-3]...之中
for (int j = i; j > 0 && less(a[j], a[j - 1]); j--) {
exch(a, j, j - 1);
}
}
}

private static boolean less(int v, int w) {
return v < w;
}

private static void exch(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}

private static void show(int[] a) {
// 在单行中打印数组
for (int i = 0; i < a.length; i++)
System.out.print(a[i] + " ");
System.out.println();
}

private static boolean isSorted(int[] a) {
// 测试数组元素是否有序
for (int i = 1; i < a.length; i++)
if (less(a[i], a[i - 1])) return false;
return true;
}

public static void main(String[] args) {
int[] a = {5, 6, 9, 1, 2, 3};
sort(a);
assert isSorted(a);
show(a);
}
}

3 希尔排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* 时间复杂度: O(n^1.3)
* 空间复杂度: O(1)
* 稳定性: 不稳定
* 复杂性: 较复杂
*
* 思想: 先部分排序,再插入排序
*
* @Description: 希尔排序
* @author: Gao Hang Hang
* @date 2019/01/18 21:44
*/
public class Shell {
public static void sort(int[] a) {
// 将a[]按升序排列
int N = a.length;
int h = 1;
while (h < N / 3) h = 3 * h + 1; // 1, 4, 13, 40, 121, 364, 1093, ...
while (h >= 1) {
// 将数组变为h有序
for (int i = h; i < N; i++) {
// 将a[i] 插入到a[i-h], a[i-second*h], a[i-3*h]... 之中
for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) {
exch(a, j, j - h);
}
}
h = h / 3;
}
}

private static boolean less(int v, int w) {
return v < w;
}

private static void exch(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}

private static void show(int[] a) {
// 在单行中打印数组
for (int i = 0; i < a.length; i++)
System.out.print(a[i] + " ");
System.out.println();
}

private static boolean isSorted(int[] a) {
// 测试数组元素是否有序
for (int i = 1; i < a.length; i++)
if (less(a[i], a[i - 1])) return false;
return true;
}

public static void main(String[] args) {
int[] a = {5, 6, 9, 1, 2, 3, 19};
sort(a);
assert isSorted(a);
show(a);
}
}

4 归并排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
* 时间复杂度: O(nlogn)
* 空间复杂度: O(n)
* 稳定性: 稳定
* 复杂性: 较复杂
*
* @Description:
* @author: Gao Hang Hang
* @date 2019/01/19 22:15
*/
public class Merge {

private static int[] aux; // 归并所需的辅助数组

public static void sort(int[] a) {
aux = new int[a.length]; // 一次性分配空间
sort(a, 0, a.length - 1);
}

private static void sort(int[] a, int lo, int hi) {
// 将数组a[lo..hi]排序
if (hi <= lo) return; // 边界条件
int mid = lo + (hi - lo) / 2;
sort(a, lo, mid); // 将左半边排序
sort(a, mid + 1, hi); // 将右半边排序
merge(a, lo, mid, hi); // 归并结果
}

public static void merge(int[] a, int lo, int mid, int hi) {
// 将a[lo..mid] 和 a[mid+1..hi] 归并
int i = lo, j = mid + 1;

for (int k = lo; k <= hi; k++) // 将a[lo..hi]复制到aux[lo..hi]
aux[k] = a[k];

for (int k = lo; k <= hi; k++) { // 归并回到a[lo..hi]
if (i > mid) a[k] = aux[j++];
else if (j > hi) a[k] = aux[i++];
else if (less(aux[j], aux[i])) a[k] = aux[j++];
else a[k] = aux[i++];
}
}

private static boolean less(int v, int w) {
return v < w;
}

private static void exch(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}

private static void show(int[] a) {
// 在单行中打印数组
for (int i = 0; i < a.length; i++)
System.out.print(a[i] + " ");
System.out.println();
}

private static boolean isSorted(int[] a) {
// 测试数组元素是否有序
for (int i = 1; i < a.length; i++)
if (less(a[i], a[i - 1])) return false;
return true;
}

public static void main(String[] args) {
int[] a = {5, 6, 9, 1, 2, 3, 19};
sort(a);
show(a);
}
}

5 快速排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* 时间复杂度: O(nlogn)
* 空间复杂度: O(logn)
* 稳定性: 不稳定
* 复杂性: 较复杂
*
* @Description: 快速排序
* @author: Gao Hang Hang
* @date 2019/01/19 22:43
*/
public class Quick {
public static void sort(int[] a) {
sort(a, 0, a.length -1);
}

private static void sort(int[] a, int lo, int hi) {
if (hi <= lo) return;
int j = prrtion(a, lo, hi); // 切分
sort(a, lo, j -1); // 将左半边部分a[lo .. j-1]排序
sort(a, j+1, hi); // 将右半部分a[j+1 .. hi]排序
}

private static int prrtion(int[] a, int lo, int hi) {
// 将数组切分为a[lo..i-1], a[i], a[i+1..hi]
int i = lo, j = hi + 1; // 左右扫描指针
int v = a[lo]; // 切分元素
while (true) {
// 扫描左右,检查扫描是否结束并交换元素
while (less(a[++i], v)) if (i == hi) break;
while (less(v, a[--j])) if (j == lo) break;
if (i >= j) break;
exch(a, i, j);
}
exch(a, lo, j); // 将v = a[j]放入正确的位置
return j; // a[lo..j-1] <= a[j] <= a[j+1..hi] 达成
}

private static boolean less(int v, int w) {
return v < w;
}

private static void exch(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}

private static void show(int[] a) {
// 在单行中打印数组
for (int i = 0; i < a.length; i++)
System.out.print(a[i] + " ");
System.out.println();
}

private static boolean isSorted(int[] a) {
// 测试数组元素是否有序
for (int i = 1; i < a.length; i++)
if (less(a[i], a[i - 1])) return false;
return true;
}

public static void main(String[] args) {
int[] a = {5, 6, 9, 1, 2, 3};
sort(a);
assert isSorted(a);
show(a);
}
}

6 堆排序

  • Copyrights © 2015-2023 高行行
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信