Add ru version (#1865)

* Add Russian docs site baseline

* Add Russian localized codebase

* Polish Russian code wording

* Update ru code translation.

* Update code translation and chapter covers.

* Fix pythontutor extraction.

* Add README and landing page.

* placeholder of profiles

* Use figures of English version

* Remove chapter paperbook
This commit is contained in:
Yudong Jin
2026-03-28 04:24:07 +08:00
committed by GitHub
parent 2ca570cc33
commit 772183705e
1958 changed files with 108186 additions and 0 deletions
@@ -0,0 +1,38 @@
// File: bubble_sort.go
// Created Time: 2022-12-06
// Author: Slone123c (274325721@qq.com)
package chapter_sorting
/* Пузырьковая сортировка */
func bubbleSort(nums []int) {
// Внешний цикл: неотсортированный диапазон [0, i]
for i := len(nums) - 1; i > 0; i-- {
// Внутренний цикл: переместить максимальный элемент неотсортированного диапазона [0, i] в его правый конец
for j := 0; j < i; j++ {
if nums[j] > nums[j+1] {
// Поменять местами nums[j] и nums[j + 1]
nums[j], nums[j+1] = nums[j+1], nums[j]
}
}
}
}
/* Пузырьковая сортировка (оптимизация флагом) */
func bubbleSortWithFlag(nums []int) {
// Внешний цикл: неотсортированный диапазон [0, i]
for i := len(nums) - 1; i > 0; i-- {
flag := false // Инициализировать флаг
// Внутренний цикл: переместить максимальный элемент неотсортированного диапазона [0, i] в его правый конец
for j := 0; j < i; j++ {
if nums[j] > nums[j+1] {
// Поменять местами nums[j] и nums[j + 1]
nums[j], nums[j+1] = nums[j+1], nums[j]
flag = true // Записать обмен элементов
}
}
if flag == false { // На этой итерации «всплытия» не было ни одного обмена, сразу выйти
break
}
}
}
@@ -0,0 +1,20 @@
// File: bubble_sort_test.go
// Created Time: 2022-12-06
// Author: Slone123c (274325721@qq.com)
package chapter_sorting
import (
"fmt"
"testing"
)
func TestBubbleSort(t *testing.T) {
nums := []int{4, 1, 3, 1, 5, 2}
bubbleSort(nums)
fmt.Println("После пузырьковой сортировки nums =", nums)
nums1 := []int{4, 1, 3, 1, 5, 2}
bubbleSortWithFlag(nums1)
fmt.Println("После пузырьковой сортировки nums1 =", nums1)
}
@@ -0,0 +1,37 @@
// File: bucket_sort.go
// Created Time: 2023-03-27
// Author: Reanon (793584285@qq.com)
package chapter_sorting
import "sort"
/* Сортировка корзинами */
func bucketSort(nums []float64) {
// Инициализировать k = n/2 корзин, предполагая распределение 2 элементов в каждую корзину
k := len(nums) / 2
buckets := make([][]float64, k)
for i := 0; i < k; i++ {
buckets[i] = make([]float64, 0)
}
// 1. Распределить элементы массива по корзинам
for _, num := range nums {
// Входные данные лежат в диапазоне [0, 1); использовать num * k для отображения в диапазон индексов [0, k-1]
i := int(num * float64(k))
// Добавить num в корзину i
buckets[i] = append(buckets[i], num)
}
// 2. Выполнить сортировку внутри каждой корзины
for i := 0; i < k; i++ {
// Использовать встроенную функцию сортировки среза; ее также можно заменить другим алгоритмом сортировки
sort.Float64s(buckets[i])
}
// 3. Обойти корзины и объединить результаты
i := 0
for _, bucket := range buckets {
for _, num := range bucket {
nums[i] = num
i++
}
}
}
@@ -0,0 +1,17 @@
// File: bucket_sort_test.go
// Created Time: 2023-03-27
// Author: Reanon (793584285@qq.com)
package chapter_sorting
import (
"fmt"
"testing"
)
func TestBucketSort(t *testing.T) {
// Пусть входные данные — числа с плавающей точкой из диапазона [0, 1)
nums := []float64{0.49, 0.96, 0.82, 0.09, 0.57, 0.43, 0.91, 0.75, 0.15, 0.37}
bucketSort(nums)
fmt.Println("После сортировки корзинами nums =", nums)
}
@@ -0,0 +1,68 @@
// File: counting_sort.go
// Created Time: 2023-03-20
// Author: Reanon (793584285@qq.com)
package chapter_sorting
type CountingSort struct{}
/* Сортировка подсчетом */
// Простая реализация, не подходит для сортировки объектов
func countingSortNaive(nums []int) {
// 1. Найти максимальный элемент массива m
m := 0
for _, num := range nums {
if num > m {
m = num
}
}
// 2. Подсчитать число появлений каждой цифры
// counter[num] обозначает число появлений num
counter := make([]int, m+1)
for _, num := range nums {
counter[num]++
}
// 3. Обойти counter и заполнить исходный массив nums элементами
for i, num := 0, 0; num < m+1; num++ {
for j := 0; j < counter[num]; j++ {
nums[i] = num
i++
}
}
}
/* Сортировка подсчетом */
// Полная реализация, позволяет сортировать объекты и является стабильной сортировкой
func countingSort(nums []int) {
// 1. Найти максимальный элемент массива m
m := 0
for _, num := range nums {
if num > m {
m = num
}
}
// 2. Подсчитать число появлений каждой цифры
// counter[num] обозначает число появлений num
counter := make([]int, m+1)
for _, num := range nums {
counter[num]++
}
// 3. Вычислить префиксные суммы counter и преобразовать «число появлений» в «конечный индекс»
// То есть counter[num]-1 — это индекс последнего появления num в res
for i := 0; i < m; i++ {
counter[i+1] += counter[i]
}
// 4. Обойти nums в обратном порядке и поместить элементы в результирующий массив res
// Инициализировать массив res для хранения результата
n := len(nums)
res := make([]int, n)
for i := n - 1; i >= 0; i-- {
num := nums[i]
// Поместить num по соответствующему индексу
res[counter[num]-1] = num
// Уменьшить префиксную сумму на 1, чтобы получить индекс следующего размещения num
counter[num]--
}
// Перезаписать исходный массив nums массивом результата res
copy(nums, res)
}
@@ -0,0 +1,20 @@
// File: counting_sort_test.go
// Created Time: 2023-03-20
// Author: Reanon (793584285@qq.com)
package chapter_sorting
import (
"fmt"
"testing"
)
func TestCountingSort(t *testing.T) {
nums := []int{1, 0, 1, 2, 0, 4, 0, 2, 2, 4}
countingSortNaive(nums)
fmt.Println("После сортировки подсчетом (объекты не поддерживаются) nums =", nums)
nums1 := []int{1, 0, 1, 2, 0, 4, 0, 2, 2, 4}
countingSort(nums1)
fmt.Println("После сортировки подсчетом nums1 =", nums1)
}
+44
View File
@@ -0,0 +1,44 @@
// File: heap_sort.go
// Created Time: 2023-05-29
// Author: Reanon (793584285@qq.com)
package chapter_sorting
/* Длина кучи равна n; начиная с узла i, выполнить просеивание сверху вниз */
func siftDown(nums *[]int, n, i int) {
for true {
// Определить узел с максимальным значением среди i, l и r и обозначить его как ma
l := 2*i + 1
r := 2*i + 2
ma := i
if l < n && (*nums)[l] > (*nums)[ma] {
ma = l
}
if r < n && (*nums)[r] > (*nums)[ma] {
ma = r
}
// Если узел i уже максимален или индексы l и r вне границ, дальнейшее просеивание не требуется, выйти
if ma == i {
break
}
// Поменять два узла местами
(*nums)[i], (*nums)[ma] = (*nums)[ma], (*nums)[i]
// Циклическое просеивание вниз
i = ma
}
}
/* Сортировка кучей */
func heapSort(nums *[]int) {
// Построение кучи: выполнить heapify для всех узлов, кроме листовых
for i := len(*nums)/2 - 1; i >= 0; i-- {
siftDown(nums, len(*nums), i)
}
// Извлекать максимальный элемент из кучи в течение n-1 итераций
for i := len(*nums) - 1; i > 0; i-- {
// Поменять корневой узел с самым правым листом местами (поменять первый и последний элементы)
(*nums)[0], (*nums)[i] = (*nums)[i], (*nums)[0]
// Начиная с корневого узла, выполнить просеивание сверху вниз
siftDown(nums, i, 0)
}
}
@@ -0,0 +1,16 @@
// File: heap_sort_test.go
// Created Time: 2023-05-29
// Author: Reanon (793584285@qq.com)
package chapter_sorting
import (
"fmt"
"testing"
)
func TestHeapSort(t *testing.T) {
nums := []int{4, 1, 3, 1, 5, 2}
heapSort(&nums)
fmt.Println("После сортировки кучей nums =", nums)
}
@@ -0,0 +1,20 @@
// File: insertion_sort.go
// Created Time: 2022-12-12
// Author: msk397 (machangxinq@gmail.com)
package chapter_sorting
/* Сортировка вставками */
func insertionSort(nums []int) {
// Внешний цикл: отсортированный диапазон [0, i-1]
for i := 1; i < len(nums); i++ {
base := nums[i]
j := i - 1
// Внутренний цикл: вставить base в правильную позицию отсортированного диапазона [0, i-1]
for j >= 0 && nums[j] > base {
nums[j+1] = nums[j] // Сдвинуть nums[j] на одну позицию вправо
j--
}
nums[j+1] = base // Поместить base в правильную позицию
}
}
@@ -0,0 +1,16 @@
// File: insertion_sort_test.go
// Created Time: 2022-12-12
// Author: msk397 (machangxinq@gmail.com)
package chapter_sorting
import (
"fmt"
"testing"
)
func TestInsertionSort(t *testing.T) {
nums := []int{4, 1, 3, 1, 5, 2}
insertionSort(nums)
fmt.Println("После сортировки вставками nums =", nums)
}
+54
View File
@@ -0,0 +1,54 @@
// File: merge_sort.go
// Created Time: 2022-12-13
// Author: msk397 (machangxinq@gmail.com)
package chapter_sorting
/* Объединить левый и правый подмассивы */
func merge(nums []int, left, mid, right int) {
// Диапазон левого подмассива: [left, mid], диапазон правого подмассива: [mid+1, right]
// Создать временный массив tmp для хранения результата слияния
tmp := make([]int, right-left+1)
// Инициализировать начальные индексы левого и правого подмассивов
i, j, k := left, mid+1, 0
// Пока в левом и правом подмассивах еще есть элементы, сравнивать их и копировать меньший во временный массив
for i <= mid && j <= right {
if nums[i] <= nums[j] {
tmp[k] = nums[i]
i++
} else {
tmp[k] = nums[j]
j++
}
k++
}
// Скопировать оставшиеся элементы левого и правого подмассивов во временный массив
for i <= mid {
tmp[k] = nums[i]
i++
k++
}
for j <= right {
tmp[k] = nums[j]
j++
k++
}
// Скопировать элементы временного массива tmp обратно в соответствующий диапазон исходного массива nums
for k := 0; k < len(tmp); k++ {
nums[left+k] = tmp[k]
}
}
/* Сортировка слиянием */
func mergeSort(nums []int, left, right int) {
// Условие завершения
if left >= right {
return
}
// Этап разбиения
mid := left + (right - left) / 2
mergeSort(nums, left, mid)
mergeSort(nums, mid+1, right)
// Этап слияния
merge(nums, left, mid, right)
}
@@ -0,0 +1,16 @@
// File: merge_sort_test.go
// Created Time: 2022-12-13
// Author: msk397 (machangxinq@gmail.com)
package chapter_sorting
import (
"fmt"
"testing"
)
func TestMergeSort(t *testing.T) {
nums := []int{7, 3, 2, 6, 0, 1, 5, 4}
mergeSort(nums, 0, len(nums)-1)
fmt.Println("После сортировки слиянием nums =", nums)
}
+130
View File
@@ -0,0 +1,130 @@
// File: quick_sort.go
// Created Time: 2022-12-12
// Author: msk397 (machangxinq@gmail.com)
package chapter_sorting
// Быстрая сортировка
type quickSort struct{}
// Быстрая сортировка (оптимизация медианным опорным элементом)
type quickSortMedian struct{}
// Быстрая сортировка (оптимизация глубины рекурсии)
type quickSortTailCall struct{}
/* Разбиение с опорными указателями */
func (q *quickSort) partition(nums []int, left, right int) int {
// Взять nums[left] в качестве опорного элемента
i, j := left, right
for i < j {
for i < j && nums[j] >= nums[left] {
j-- // Идти справа налево в поисках первого элемента меньше опорного
}
for i < j && nums[i] <= nums[left] {
i++ // Идти слева направо в поисках первого элемента больше опорного
}
// Обмен элементов
nums[i], nums[j] = nums[j], nums[i]
}
// Переместить опорный элемент на границу двух подмассивов
nums[i], nums[left] = nums[left], nums[i]
return i // Вернуть индекс опорного элемента
}
/* Быстрая сортировка */
func (q *quickSort) quickSort(nums []int, left, right int) {
// Завершить рекурсию, когда длина подмассива равна 1
if left >= right {
return
}
// Разбиение с опорными указателями
pivot := q.partition(nums, left, right)
// Рекурсивно обработать левый и правый подмассивы
q.quickSort(nums, left, pivot-1)
q.quickSort(nums, pivot+1, right)
}
/* Выбрать медиану из трех кандидатов */
func (q *quickSortMedian) medianThree(nums []int, left, mid, right int) int {
l, m, r := nums[left], nums[mid], nums[right]
if (l <= m && m <= r) || (r <= m && m <= l) {
return mid // m находится между l и r
}
if (m <= l && l <= r) || (r <= l && l <= m) {
return left // l находится между m и r
}
return right
}
/* Разбиение с опорными указателями (медиана трех) */
func (q *quickSortMedian) partition(nums []int, left, right int) int {
// Взять nums[left] в качестве опорного элемента
med := q.medianThree(nums, left, (left+right)/2, right)
// Переместить медиану в крайний левый элемент массива
nums[left], nums[med] = nums[med], nums[left]
// Взять nums[left] в качестве опорного элемента
i, j := left, right
for i < j {
for i < j && nums[j] >= nums[left] {
j-- // Идти справа налево в поисках первого элемента меньше опорного
}
for i < j && nums[i] <= nums[left] {
i++ // Идти слева направо в поисках первого элемента больше опорного
}
// Обмен элементов
nums[i], nums[j] = nums[j], nums[i]
}
// Переместить опорный элемент на границу двух подмассивов
nums[i], nums[left] = nums[left], nums[i]
return i // Вернуть индекс опорного элемента
}
/* Быстрая сортировка */
func (q *quickSortMedian) quickSort(nums []int, left, right int) {
// Завершить рекурсию, когда длина подмассива равна 1
if left >= right {
return
}
// Разбиение с опорными указателями
pivot := q.partition(nums, left, right)
// Рекурсивно обработать левый и правый подмассивы
q.quickSort(nums, left, pivot-1)
q.quickSort(nums, pivot+1, right)
}
/* Разбиение с опорными указателями */
func (q *quickSortTailCall) partition(nums []int, left, right int) int {
// Взять nums[left] в качестве опорного элемента
i, j := left, right
for i < j {
for i < j && nums[j] >= nums[left] {
j-- // Идти справа налево в поисках первого элемента меньше опорного
}
for i < j && nums[i] <= nums[left] {
i++ // Идти слева направо в поисках первого элемента больше опорного
}
// Обмен элементов
nums[i], nums[j] = nums[j], nums[i]
}
// Переместить опорный элемент на границу двух подмассивов
nums[i], nums[left] = nums[left], nums[i]
return i // Вернуть индекс опорного элемента
}
/* Быстрая сортировка (оптимизация глубины рекурсии) */
func (q *quickSortTailCall) quickSort(nums []int, left, right int) {
// Завершить, когда длина подмассива равна 1
for left < right {
// Операция разбиения с опорными указателями
pivot := q.partition(nums, left, right)
// Выполнить быструю сортировку для более короткого из двух подмассивов
if pivot-left < right-pivot {
q.quickSort(nums, left, pivot-1) // Рекурсивно отсортировать левый подмассив
left = pivot + 1 // Оставшийся неотсортированный диапазон: [pivot + 1, right]
} else {
q.quickSort(nums, pivot+1, right) // Рекурсивно отсортировать правый подмассив
right = pivot - 1 // Оставшийся неотсортированный диапазон: [left, pivot - 1]
}
}
}
@@ -0,0 +1,34 @@
// File: quick_sort_test.go
// Created Time: 2022-12-12
// Author: msk397 (machangxinq@gmail.com)
package chapter_sorting
import (
"fmt"
"testing"
)
// Быстрая сортировка
func TestQuickSort(t *testing.T) {
q := quickSort{}
nums := []int{4, 1, 3, 1, 5, 2}
q.quickSort(nums, 0, len(nums)-1)
fmt.Println("После быстрой сортировки nums =", nums)
}
// Быстрая сортировка (оптимизация медианным опорным элементом)
func TestQuickSortMedian(t *testing.T) {
q := quickSortMedian{}
nums := []int{4, 1, 3, 1, 5, 2}
q.quickSort(nums, 0, len(nums)-1)
fmt.Println("После быстрой сортировки (оптимизация медианным опорным элементом) nums =", nums)
}
// Быстрая сортировка (оптимизация глубины рекурсии)
func TestQuickSortTailCall(t *testing.T) {
q := quickSortTailCall{}
nums := []int{4, 1, 3, 1, 5, 2}
q.quickSort(nums, 0, len(nums)-1)
fmt.Println("После быстрой сортировки (оптимизация глубины рекурсии) nums =", nums)
}
+60
View File
@@ -0,0 +1,60 @@
// File: radix_sort.go
// Created Time: 2023-01-18
// Author: Reanon (793584285@qq.com)
package chapter_sorting
import "math"
/* Получить k-й разряд элемента num, где exp = 10^(k-1) */
func digit(num, exp int) int {
// Передача exp вместо k позволяет избежать повторного дорогостоящего вычисления степени
return (num / exp) % 10
}
/* Сортировка подсчетом (сортировка по k-му разряду nums) */
func countingSortDigit(nums []int, exp int) {
// Разряды десятичной системы лежат в диапазоне 0~9, поэтому нужен массив корзин длины 10
counter := make([]int, 10)
n := len(nums)
// Подсчитать число появлений каждой цифры от 0 до 9
for i := 0; i < n; i++ {
d := digit(nums[i], exp) // Получить k-й разряд nums[i], обозначив его как d
counter[d]++ // Подсчитать число появлений цифры d
}
// Вычислить префиксные суммы и преобразовать «число появлений» в «индекс массива»
for i := 1; i < 10; i++ {
counter[i] += counter[i-1]
}
// Выполняя обратный проход, заполнить res элементами по статистике в корзинах
res := make([]int, n)
for i := n - 1; i >= 0; i-- {
d := digit(nums[i], exp)
j := counter[d] - 1 // Получить индекс j цифры d в массиве
res[j] = nums[i] // Поместить текущий элемент по индексу j
counter[d]-- // Уменьшить количество d на 1
}
// Перезаписать исходный массив nums результатом
for i := 0; i < n; i++ {
nums[i] = res[i]
}
}
/* Поразрядная сортировка */
func radixSort(nums []int) {
// Получить максимальный элемент массива, чтобы определить максимальное число разрядов
max := math.MinInt
for _, num := range nums {
if num > max {
max = num
}
}
// Проходить разряды от младшего к старшему
for exp := 1; max >= exp; exp *= 10 {
// Выполнить сортировку подсчетом по k-му разряду элементов массива
// k = 1 -> exp = 1
// k = 2 -> exp = 10
// то есть exp = 10^(k-1)
countingSortDigit(nums, exp)
}
}
@@ -0,0 +1,18 @@
// File: radix_sort_test.go
// Created Time: 2023-01-18
// Author: Reanon (793584285@qq.com)
package chapter_sorting
import (
"fmt"
"testing"
)
func TestRadixSort(t *testing.T) {
/* Поразрядная сортировка */
nums := []int{10546151, 35663510, 42865989, 34862445, 81883077,
88906420, 72429244, 30524779, 82060337, 63832996}
radixSort(nums)
fmt.Println("После поразрядной сортировки nums =", nums)
}
@@ -0,0 +1,24 @@
// File: selection_sort.go
// Created Time: 2023-05-29
// Author: Reanon (793584285@qq.com)
package chapter_sorting
/* Сортировка выбором */
func selectionSort(nums []int) {
n := len(nums)
// Внешний цикл: неотсортированный диапазон [i, n-1]
for i := 0; i < n-1; i++ {
// Внутренний цикл: найти минимальный элемент в неотсортированном диапазоне
k := i
for j := i + 1; j < n; j++ {
if nums[j] < nums[k] {
// Записать индекс минимального элемента
k = j
}
}
// Поменять этот минимальный элемент местами с первым элементом неотсортированного диапазона
nums[i], nums[k] = nums[k], nums[i]
}
}
@@ -0,0 +1,16 @@
// File: selection_sort_test.go
// Created Time: 2023-05-29
// Author: Reanon (793584285@qq.com)
package chapter_sorting
import (
"fmt"
"testing"
)
func TestSelectionSort(t *testing.T) {
nums := []int{4, 1, 3, 1, 5, 2}
selectionSort(nums)
fmt.Println("После сортировки выбором nums =", nums)
}