OSDN Git Service

update radix-sort and stl-sort
authorouuan <y___o___u@126.com>
Mon, 26 Aug 2019 06:03:50 +0000 (14:03 +0800)
committerouuan <y___o___u@126.com>
Mon, 26 Aug 2019 06:03:50 +0000 (14:03 +0800)
docs/basic/radix-sort.md
docs/basic/stl-sort.md

index 20bccb9..4ae070f 100644 (file)
@@ -1,17 +1,60 @@
\9fºæ\95°æ\8e\92åº\8fæ\98¯å°\86å¾\85æ\8e\92åº\8fç \81æ\8b\86å\88\86æ\88\90å¤\9a个é\83¨å\88\86å\88\86å\88«æ\9d¥æ¯\94è¾\83
\9fºæ\95°æ\8e\92åº\8fæ\98¯å°\86å¾\85æ\8e\92åº\8fç\9a\84å\85\83ç´ æ\8b\86å\88\86为 $k$ ä¸ªå\85³é\94®å­\97ï¼\88æ¯\94è¾\83两个å\85\83ç´ æ\97¶ï¼\8cå\85\88æ¯\94è¾\83第ä¸\80å\85³é\94®å­\97ï¼\8cå¦\82æ\9e\9cç\9b¸å\90\8cå\86\8dæ¯\94è¾\83第äº\8cå\85³é\94®å­\97â\80¦â\80¦ï¼\89ï¼\8cç\84¶å\90\8eå\85\88对第 $k$ å\85³é\94®å­\97è¿\9bè¡\8c稳å®\9aæ\8e\92åº\8fï¼\8cå\86\8d对第 $k-1$ å\85³é\94®å­\97è¿\9bè¡\8c稳å®\9aæ\8e\92åº\8fï¼\8cå\86\8d对第 $k-2$ å\85³é\94®å­\97è¿\9bè¡\8c稳å®\9aæ\8e\92åº\8fâ\80¦â\80¦æ\9c\80å\90\8e对第ä¸\80å\85³é\94®å­\97è¿\9bè¡\8c稳å®\9aæ\8e\92åº\8fï¼\8cè¿\99æ ·å°±å®\8cæ\88\90äº\86对æ\95´ä¸ªå¾\85æ\8e\92åº\8fåº\8få\88\97ç\9a\84稳å®\9aæ\8e\92åº\8f
 
-按照排序码的先后顺序,分为两种:
+基数排序的正确性可以自己感性理解一下,也可以参考 https://walkccc.github.io/CLRS/Chap08/8.3/#83-3 。
 
-1.  高位优先(MSD)
-    先对高位排序,分成若干子序列,对每个子序列根据较低位排序,是一个分、分、……、分、收的过程。
-2.  低位优先(LSD)
-    从低位开始,对于排好的序列用次低位排序,(每次排序的都是全体元素)是一个分、收、分、收、……、分、收的过程。
+一般来说,每个关键字的值域都不大,就可以使用 [计数排序](./counting-sort.md) 作为内层排序,复杂度为 $O(nk+\sum\limits_{i=1}^k w_i)$,其中 $w_i$ 为第 $i$ 关键字的值域大小。
 
-低位优先速度较快,便于处理,更常用。
+(如果关键字值域很大,就可以直接使用基于比较的 $O(nk\log n)$ 排序而无需使用基数排序了。)
 
-基数排序对关键码值进行 $O(\log_r n)$ 次运算,因此处理 n 个不同的关键码时,基数排序的时间代价为 $O(n \log n)$ 。
+伪代码:
 
-### 参考
+$$
+\begin{array}{ll}
+1 & \textbf{Input. } \text{An array } A \text{ consisting of }n\text{ elements, where each element has }k\text{ keys.}\\
+2 & \textbf{Output. } \text{Array }A\text{ will be sorted in nondecreasing order stably.} \\
+3 & \textbf{Method. }  \\
+4 & \textbf{for }i\gets k\textbf{ down to }1\\
+5 & \qquad\text{sort }A\text{ into nondecreasing order by the }i\text{-th key stably}
+\end{array}
+$$
+C++ 代码:
+
+```cpp
+const int N = 100010;
+const int W = 100010;
+const int K = 100;
+
+int n, w[K], k, cnt[W];
+
+struct Element
+{
+    int key[K];
+    bool operator<(const Element& y) const // shows how two elements are compared
+    {
+        for (int i = 1; i <= k; ++i)
+        {
+            if (key[i] == y.key[i]) continue;
+            return key[i] < y.key[i];
+        }
+        return false;
+    }
+} a[N], b[N];
+
+void counting_sort(int p)
+{
+    memset(cnt, 0, sizeof(cnt));
+    for (int i = 1; i <= n; ++i) ++cnt[a[i].key[p]];
+    for (int i = 1; i <= w[p]; ++i) cnt[i] += cnt[i - 1];
+    for (int i = 1; i <= n; ++i) b[cnt[a[i].key[p]]--] = a[i];
+    memcpy(a, b, sizeof(a));
+}
+
+void radix_sort()
+{
+    for (int i = k; i >= 1; --i)
+    {
+        counting_sort(i);
+    }
+}
+```
 
- <http://atool.org/sort.php> ATool 的排序演示动画  
- <https://www.geeksforgeeks.org/counting-sort/> 
index 5434fdb..d001493 100644 (file)
@@ -14,37 +14,39 @@ Introsort 限制了快速排序的分治深度,当分治达到一定深度之
 
 Introsort 的这个限制使得它的最坏时间复杂度是 $O(n\log n)$ 的。
 
-快速用法:
+用法:
 
 ```cpp
 // a[0] .. a[n - 1] 为需要排序的数列
-std::sort(a, a + n);
-// 这句代码直接修改 a 数组里的元素顺序,使得现在它是从小到大排列的
+std::sort(a, a + n); // 这句代码直接修改 a 数组里的元素顺序,使得现在它是从小到大排列的
+std::sort(a, a + n, cmp); // cmp 为自定义的比较函数
 ```
 
 ## nth_element
 
 作用是找到选定区间内第 $k$ 大的数,并将所有比它小的数与比它大的数分别置于两侧,返回它的地址。
 
-原理是未完成的快速排序
+原理是未完成的快速排序
 
-用法
+用法
 
 ```cpp
 std::nth_element(begin, mid, end);
+std::nth_element(begin, mid, end, cmp);
 ```
 
-时间复杂度:期望 $O(n)$ 
+时间复杂度:期望 $O(n)$ 
 
-常用于构建 K-DTree
+常用于构建 K-DTree
 
 ## stable_sort
 
-稳定的 $O(nlogn)$ 排序,即保证相等元素排序后的相对位置与原序列相同。
+稳定的 $O(n\log n)$ 排序,即保证相等元素排序后的相对位置与原序列相同。
 
 用法
 
 ```cpp
+std::stable_sort(begin, end);
 std::stable_sort(begin, end, cmp);
 ```
 
@@ -52,12 +54,13 @@ std::stable_sort(begin, end, cmp);
 
 将序列中前 $k$ 小元素按顺序置于前 $k$ 个位置,后面的元素不保证顺序。
 
-复杂度: $O(nlogk)$ 
+复杂度: $O(n\log k)$ 
 
 用法:
 
 ```cpp
 std::partial_sort(begin, begin + k, end);
+std::partial_sort(begin, begin + k, end, cmp);
 ```
 
 原理:
@@ -73,8 +76,7 @@ std::partial_sort(begin, begin + k, end);
 int a[1009], n = 10;
 // ......
 std::sort(a + 1, a + 1 + n);  // 不重载,从小到大排序。
-std::sort(a + 1, a + 1 + n,
-          greater<int>());  // 重载小于运算符为大于,从大到小排序。
+std::sort(a + 1, a + 1 + n, greater<int>());  // 重载小于运算符为大于,从大到小排序。
 ```
 
 ```cpp
@@ -88,16 +90,17 @@ bool cmp(const data u1, const data u2) {
   return (u1.a == u2.a) ? (u1.b > u2.b) : (u1.a > u2.a);
 }
 // ......
-std::sort(da + 1, da + 1 + 10, cmp);  // 不重载,从小到大排序。
-std::sort(da + 1, da + 1 + 10, cmp);  // 重载小于运算符为大于,从大到小排序。
+std::sort(da + 1, da + 1 + 10);  // 使用结构体中定义的 < 运算符,从小到大排序。
+std::sort(da + 1, da + 1 + 10, cmp);  // 使用 cmp 函数进行比较,从大到小排序。
 ```
 
 ### 严格弱序
 
-进行排序的运算符必须满足严格弱序( [Strict weak orderings](https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings) ),否则会出现不可预料的情况(如运行时错误)。
+进行排序的运算符必须满足严格弱序( [Strict weak orderings](https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings) ),否则会出现不可预料的情况(如运行时错误、无法正确排序)。
+
 严格弱序的要求:
 
-1.   $x \not< x$ (非自反性)
+1.  $x \not< x$ (非自反性)
 2.  若 $x < y$ ,则 $y \not< x$ (非对称性)
 3.  若 $x < y, y < z$ ,则 $x < z$ (传递性)
 4.  若 $x \not< y, y \not< x, y \not< z, z \not< y$ ,则 $x \not< z, z \not< x$ (不可比性的传递性)
@@ -106,8 +109,4 @@ std::sort(da + 1, da + 1 + 10, cmp);  // 重载小于运算符为大于,从大
 
 -   使用 `<=` 来定义排序中的小于运算符。
 -   在调用排序运算符时,读取外部数值可能会改变的数组。(常见于最短路算法)
--   将多个数的最大最小值进行比较的结果作为排序运算符。
-
-### Reference
-
--    [浅谈邻项交换排序的应用以及需要注意的问题](https://ouuan.github.io/浅谈邻项交换排序的应用以及需要注意的问题/) 
+-   将多个数的最大最小值进行比较的结果作为排序运算符。(如,皇后游戏 / 加工生产调度 中的经典错误,可以参考文章 [浅谈邻项交换排序的应用以及需要注意的问题](https://ouuan.github.io/浅谈邻项交换排序的应用以及需要注意的问题/) )。
\ No newline at end of file