From 820cb53c8af89d95ce06e8b1e15b00caa6eb6937 Mon Sep 17 00:00:00 2001 From: Abdullah Meda Date: Fri, 30 Apr 2021 17:04:47 +0400 Subject: [PATCH 1/4] Adding ShellSort Algorithm --- basic/sorting/MergeSort/MergeSort.java | 2 +- basic/sorting/ShellSort/README.md | 77 ++++++++++++++++++++++++++ basic/sorting/ShellSort/ShellSort.java | 25 +++++++++ basic/sorting/ShellSort/ShellSort.js | 21 +++++++ 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 basic/sorting/ShellSort/README.md create mode 100644 basic/sorting/ShellSort/ShellSort.java create mode 100644 basic/sorting/ShellSort/ShellSort.js diff --git a/basic/sorting/MergeSort/MergeSort.java b/basic/sorting/MergeSort/MergeSort.java index 32dbcd327c042..d1f781ca9d347 100644 --- a/basic/sorting/MergeSort/MergeSort.java +++ b/basic/sorting/MergeSort/MergeSort.java @@ -16,7 +16,7 @@ private static void merge(int[] nums, int low, int mid, int high, int[] temp) { } } - System.arraycopy(tmp, low, nums, low, high - low + 1); + System.arraycopy(temp, low, nums, low, high - low + 1); } private static void mergeSort(int[] nums, int low, int high, int[] temp) { diff --git a/basic/sorting/ShellSort/README.md b/basic/sorting/ShellSort/README.md new file mode 100644 index 0000000000000..e2909b1190fcc --- /dev/null +++ b/basic/sorting/ShellSort/README.md @@ -0,0 +1,77 @@ +# 选择排序 + +选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。 + +## 代码示例 + + + +### **Java** + +```java +import java.util.Arrays; + +public class ShellSort { + + private static int[] shellSort(int[] arr) { + int n = arr.length; + + for (int gap = n / 2; gap > 0; gap /= 2) { + for (int i = gap; i < n; i++) { + int key = arr[i]; + int j = i; + while (j >= gap && arr[j - gap] > key) { + arr[j] = arr[j - gap]; + j -= gap; + } + arr[j] = key; + } + } + return arr; + } + + public static void main(String[] args) { + System.out.println(Arrays.toString(shellSort(new int[]{1, 2, 7, 9, 5, 8}))); + } +} +``` + +### **JavaScript** + +```javascript +function shellSort(arr){ + var len = arr.length; + var gapSize = Math.floor(len/2); + + while(gapSize > 0){ + for(var i = gapSize; i < len; i++) { + + var temp = arr[i]; + var j = i; + + while(j >= gapSize && arr[j - gapSize] > temp) { + arr[j] = arr[j - gapSize]; + j -= gapSize; + } + arr[j] = temp; + } + gapSize = Math.floor(gapSize/2); + } + return arr; +} + +let arr = [6, 3, 2, 1, 5]; +console.log(shellSort(arr)) +``` + + + +## 算法分析 + +空间复杂度 O(1),时间复杂度 O(n²)。 + +那选择排序是稳定的排序算法吗? + +答案是否定的,**选择排序是一种不稳定的排序算法**。选择排序每次都要找剩余未排序元素中的最小值,并和前面的元素交换位置,这样破坏了稳定性。 + +比如 5,8,5,2,9 这样一组数据,使用选择排序算法来排序的话,第一次找到最小元素 2,与第一个 5 交换位置,那第一个 5 和中间的 5 顺序就变了,所以就不稳定了。正是因此,相对于冒泡排序和插入排序,选择排序就稍微逊色了。 diff --git a/basic/sorting/ShellSort/ShellSort.java b/basic/sorting/ShellSort/ShellSort.java new file mode 100644 index 0000000000000..9d9ed1f169951 --- /dev/null +++ b/basic/sorting/ShellSort/ShellSort.java @@ -0,0 +1,25 @@ +import java.util.Arrays; + +public class ShellSort { + + private static int[] shellSort(int[] arr) { + int n = arr.length; + + for (int gap = n / 2; gap > 0; gap /= 2) { + for (int i = gap; i < n; i++) { + int key = arr[i]; + int j = i; + while (j >= gap && arr[j - gap] > key) { + arr[j] = arr[j - gap]; + j -= gap; + } + arr[j] = key; + } + } + return arr; + } + + public static void main(String[] args) { + System.out.println(Arrays.toString(shellSort(new int[]{1, 2, 7, 9, 5, 8}))); + } +} \ No newline at end of file diff --git a/basic/sorting/ShellSort/ShellSort.js b/basic/sorting/ShellSort/ShellSort.js new file mode 100644 index 0000000000000..24a295c9c173f --- /dev/null +++ b/basic/sorting/ShellSort/ShellSort.js @@ -0,0 +1,21 @@ +function shellSort(arr){ + var len = arr.length; + var gapSize = Math.floor(len/2); + + while(gapSize > 0){ + for(var i = gapSize; i < len; i++) { + var temp = arr[i]; + var j = i; + while(j >= gapSize && arr[j - gapSize] > temp) { + arr[j] = arr[j - gapSize]; + j -= gapSize; + } + arr[j] = temp; + } + gapSize = Math.floor(gapSize/2); + } + return arr; +} + +let arr = [6, 3, 2, 1, 5]; +console.log(shellSort(arr)) \ No newline at end of file From b0ee85b1c7b948fa86d327811f7a229dcde58dae Mon Sep 17 00:00:00 2001 From: Abdullah Meda Date: Fri, 30 Apr 2021 17:12:14 +0400 Subject: [PATCH 2/4] Updating Englisg READMEs' --- README_EN.md | 1 + basic/README_EN.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README_EN.md b/README_EN.md index 267331c982eec..18d0c59af5d10 100644 --- a/README_EN.md +++ b/README_EN.md @@ -39,6 +39,7 @@ Complete solutions to [LeetCode](https://leetcode-cn.com/problemset/all/), [LCOF - [Selection Sort](/basic/sorting/SelectionSort/README.md) - [Merge Sort](/basic/sorting/MergeSort/README.md) - [Quick Sort](/basic/sorting/QuickSort/README.md) +- [Shell Sort](/basic/sorting/ShellSort/README.md) ### Searching diff --git a/basic/README_EN.md b/basic/README_EN.md index 8889d6a527bad..d4db69b840819 100644 --- a/basic/README_EN.md +++ b/basic/README_EN.md @@ -7,6 +7,7 @@ - [Selection Sort](./sorting/SelectionSort/README.md) - [Merge Sort](./sorting/MergeSort/README.md) - [Quick Sort](./sorting/QuickSort/README.md) +- [Shell Sort](./sorting/ShellSort/README.md) ## Searching From 538f52e5b280e1ed9d1c83ea599743e36f040d6c Mon Sep 17 00:00:00 2001 From: Yang Libin Date: Fri, 30 Apr 2021 21:47:11 +0800 Subject: [PATCH 3/4] Update README.md --- basic/sorting/ShellSort/README.md | 36 +++++++++++++++++-------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/basic/sorting/ShellSort/README.md b/basic/sorting/ShellSort/README.md index e2909b1190fcc..070276a739c5c 100644 --- a/basic/sorting/ShellSort/README.md +++ b/basic/sorting/ShellSort/README.md @@ -1,6 +1,11 @@ -# 选择排序 +# 希尔排序 -选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。 +希尔排序,也被称为递减增量排序,是简单插入排序的一种改进版本。 + +- 在插入排序中,如果待排序列中的某个元素,距离有序数列中待插入位置非常远,就需要比较很多次才可以到达插入位置,这是因为待插入元素局部非常无序,比如说`[2, 3, 4, 5, 6, 7, 8, 1, ...]`,我们要插入 1,就必须将 1 和前面的 2-8 每个值都比较一下,就是因为 1 附近非常无序,想象一下,如果待插入元素附近比较有序,那么在进行插入排序的时候就只需要比较非常少的几次就可以插入到正确位置。 +- 希尔排序就是先把整个序列排得相对比较有序,再进行插入排序的时候,需要比较的次数就会变得很少。 +- 插入排序的增量(间隔)为 1,希尔排序相当于将这个间隔从最大为数组长度的一半一直降到 1,这一点在程序中体现的很清楚。当间隔很大时,比较的次数也会很少,在进行了几次大间隔的插入排序后,序列已经部分有序,这样再进行小间隔的插入排序也自然会比较很少的次数。 +- 希尔排序就是将处在相同间隔的元素提取出来单独进行插入排序,然后逐步将间隔减小到 1 的过程。 ## 代码示例 @@ -39,39 +44,38 @@ public class ShellSort { ### **JavaScript** ```javascript -function shellSort(arr){ - var len = arr.length; - var gapSize = Math.floor(len/2); - - while(gapSize > 0){ - for(var i = gapSize; i < len; i++) { - +function shellSort(arr) { + var len = arr.length; + var gapSize = Math.floor(len / 2); + while (gapSize > 0) { + for (var i = gapSize; i < len; i++) { var temp = arr[i]; var j = i; - while(j >= gapSize && arr[j - gapSize] > temp) { + while (j >= gapSize && arr[j - gapSize] > temp) { arr[j] = arr[j - gapSize]; j -= gapSize; } arr[j] = temp; } - gapSize = Math.floor(gapSize/2); + gapSize = Math.floor(gapSize / 2); } return arr; } let arr = [6, 3, 2, 1, 5]; -console.log(shellSort(arr)) +console.log(shellSort(arr)); ``` ## 算法分析 -空间复杂度 O(1),时间复杂度 O(n²)。 +时间复杂度: -那选择排序是稳定的排序算法吗? +希尔排序的时间性能取决于所取“增量”序列的函数,这涉及到一些数学上尚未解决的难题。但是有人通过大量的实验,给出了较好的结果:当 n 较大时,比较和移动的次数约在 `n^1.25` 到 `(1.6n)^1.25` 之间。所以可以这样简单记忆: -答案是否定的,**选择排序是一种不稳定的排序算法**。选择排序每次都要找剩余未排序元素中的最小值,并和前面的元素交换位置,这样破坏了稳定性。 +- 当 n 较小时,希尔排序和插入排序相差不大,都为 n² 左右。 +- 当 n 很大时,时间增长幅度逐渐放缓,平均复杂度是 nlogn。 -比如 5,8,5,2,9 这样一组数据,使用选择排序算法来排序的话,第一次找到最小元素 2,与第一个 5 交换位置,那第一个 5 和中间的 5 顺序就变了,所以就不稳定了。正是因此,相对于冒泡排序和插入排序,选择排序就稍微逊色了。 +空间复杂度:O(1)。 From 0b5474f05a9e9dc64ac245061e04f39238967088 Mon Sep 17 00:00:00 2001 From: Yang Libin Date: Fri, 30 Apr 2021 21:47:45 +0800 Subject: [PATCH 4/4] Update ShellSort.js --- basic/sorting/ShellSort/ShellSort.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/basic/sorting/ShellSort/ShellSort.js b/basic/sorting/ShellSort/ShellSort.js index 24a295c9c173f..774741a53658b 100644 --- a/basic/sorting/ShellSort/ShellSort.js +++ b/basic/sorting/ShellSort/ShellSort.js @@ -1,21 +1,21 @@ -function shellSort(arr){ - var len = arr.length; - var gapSize = Math.floor(len/2); - - while(gapSize > 0){ - for(var i = gapSize; i < len; i++) { +function shellSort(arr) { + var len = arr.length; + var gapSize = Math.floor(len / 2); + while (gapSize > 0) { + for (var i = gapSize; i < len; i++) { var temp = arr[i]; var j = i; - while(j >= gapSize && arr[j - gapSize] > temp) { + + while (j >= gapSize && arr[j - gapSize] > temp) { arr[j] = arr[j - gapSize]; j -= gapSize; } arr[j] = temp; } - gapSize = Math.floor(gapSize/2); + gapSize = Math.floor(gapSize / 2); } return arr; } let arr = [6, 3, 2, 1, 5]; -console.log(shellSort(arr)) \ No newline at end of file +console.log(shellSort(arr))