ๆ–‡็ซ 

๐Ÿง‘๐Ÿปโ€๐Ÿ’ป ๆŽ’ๅบ

่ฎฐๅฝ•ไบ† C++ ไธญๅ‡ ็งๆŽ’ๅบๆ–นๆณ•็š„ๅฎž็Žฐใ€‚ไฝœ็ฌ”่ฎฐ็”จใ€‚

๐Ÿง‘๐Ÿปโ€๐Ÿ’ป ๆŽ’ๅบ

Characteristics of sorting algorithms:

  • In-place: a sorting algorithm is in-place if $O(1)$ extra space is needed beyond input.

  • Stability: a sorting algorithm is stable if numbers with the same value appear in the output array in the same order as they do in the input array.

Procedure ๐ˆ๐ง๐ฌ๐ž๐ซ๐ญ๐ข๐จ๐ง-๐’๐จ๐ซ๐ญ1

pic-1

In: An array $A$ of $n$ integers.
Out: A permutation of that array $A$ that is sorted (monotonic).

for $i := 2$ to $A.length$
โ€ƒโ€ƒ$key := A[i]$
โ€ƒโ€ƒ// Insert $A[i]$ into the sorted subarray $A[1 : i - 1]$
โ€ƒโ€ƒ$j := i - 1$
โ€ƒโ€ƒwhile $j > 0$ and $A[j] > key$
โ€ƒโ€ƒโ€ƒโ€ƒ$A[j + 1] := A[j]$
โ€ƒโ€ƒโ€ƒโ€ƒ$j := j - 1$
โ€ƒโ€ƒ$A[j + 1] := key$
โ€ƒโ€ƒreturn $A$


One possible implementation with C++:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main()
{
    int arr[100], len;
    cin >> len;
    for (int t = 0; t < len; t++)
      cin >> arr[t];
    for (int i = 1; i < len; i++)
    {
      int key = arr[i];               // ๆŠŠ่ฆๆฏ”่พƒ็š„็‰ŒไปŽๆ‰‹็‰Œ้‡Œๅ•็‹ฌๆŠฝๅ‡บๆฅ
      int j = i - 1;
      while (j > 0 && arr[j] > key)   // ๅฐ†ๆญค็‰ŒไธŽๅ‰้ข็š„็‰Œๆฏ”่พƒๅคงๅฐ
      {
        arr[j + 1] = arr[j];          // ๆŠŠๅ‰้ข็š„ๅคง็‰Œไพๆฌกๅพ€ๅŽๆŒช๏ผŒ็ฉบๅ‡บไฝ็ฝฎ
        j--;
      }
      arr[j + 1] = key;               // ๆŠŠๆญค็‰Œๆ’ๅ…ฅๅˆ้€‚็š„ไฝ็ฝฎ
    }
    for (int t = 0; t < len; t++)
      cout << arr[t] << " ";
    return 0;
}

Procedure ๐Œ๐ž๐ซ๐ ๐ž-๐’๐จ๐ซ๐ญ

In: An array $A$ of $n$ integers.
Out: A permutation of that array $A$ that is sorted (monotonic).

MergeSort($A[1โ€ฆn]$):
if $n = 1$
โ€ƒโ€ƒ$sol[1โ€ฆn] = [1โ€ฆn]$
else
โ€ƒโ€ƒ$solLeft[1โ€ฆ(n/2)] := MergeSort(A[1โ€ฆ(n/2)])$
โ€ƒโ€ƒ$solRight[1โ€ฆ(n/2)] := MergeSort(A[(n/2 + 1)โ€ฆn])$
โ€ƒโ€ƒ$sol[1โ€ฆn] := Merge(solLeft[1โ€ฆ(n/2)], solRight[1โ€ฆ(n/2)])$
return $sol[1โ€ฆn]$

Merge($A[1โ€ฆn], B[1โ€ฆm]$):
$Aindex := 1, Bindex := 1, Result := [ ]$
// Scan $A$ and $B$ from left to right,
// Append the currently smallest to the result array.
while $Aindex \leq A.length$ and $Bindex \leq B.length$
โ€ƒโ€ƒif $A[Aindex] \leq B[Aindex]$
โ€ƒโ€ƒโ€ƒโ€ƒ$Result.AddLast(A[Aindex])$
โ€ƒโ€ƒโ€ƒโ€ƒ$Aindex := Aindex + 1$
โ€ƒโ€ƒelse
โ€ƒโ€ƒโ€ƒโ€ƒ$Result.AddLast(B[Bindex])$
โ€ƒโ€ƒโ€ƒโ€ƒ$Bindex := Bindex + 1$

// Copy the remaining elements of A and B
while $Aindex \leq A.length$
โ€ƒโ€ƒ$Result.AddLast(A[Aindex])$
โ€ƒโ€ƒ$Aindex := Aindex + 1$
while $Bindex \leq B.length$
โ€ƒโ€ƒ$Result.AddLast(B[Bindex])$
โ€ƒโ€ƒ$Bindex := Bindex + 1$
return $Result$


One possible implementation with C++:

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
void merge(int a[], int result[], int left, int mid, int right)
{
    int i = left, j = mid + 1, k = left;

    // ๆŒ‰ๅคงๅฐ้กบๅบไพๆฌกๆทปๅŠ ๅˆฐ result ๆ•ฐ็ป„ไธญ
    while (i <= mid && j <= right)
    {
    if (a[i] <= a[j])
        result[k++] = a[i++];
    else
        result[k++] = a[j++];
    }

    // ๅค„็†ๅ‰ฉไฝ™็š„ๆ•ฐ
    while (i <= mid)
        result[k++] = a[i++];
    while (j <= right)
        result[k++] = a[j++];

    for (i = left; i <= right; i++)
        a[i] = result[i];
}

void merge_sort(int a[], int result[], int left, int right)
{
    if (left < right)
    {
        int mid = (left + right) / 2;
        merge_sort(a, result, left, mid);
        merge_sort(a, result, mid + 1, right);
        merge(a, result, left, mid, right);
    }
}

Procedure ๐‡๐ž๐š๐ฉ-๐’๐จ๐ซ๐ญ

  1. Keep a copy of the root item
  2. Remove last item and put it to root
  3. Maintain heap property
  4. Return the copy of the root item

One possible implementation with C++:

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
#include <bits/stdc++.h>
using namespace std;

// maintain heap property
void heapify(int arr[], int n, int i) 
{
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    int largest = i;

    if (left < n && arr[left] > arr[largest])
        largest = left;
    if (right < n && arr[right] > arr[largest]) 
        largest = right;
    if (largest != i) 
    {
        swap(arr[i], arr[largest]);
        heapify(arr, n, largest);
    }
}

void heapSort(int arr[], int n) 
{
    // build max heap   
    for (int i = n / 2 - 1; i >= 0; i--) 
        heapify(arr, n, i);

    // swap the root item with the last item,
    // then heapify the remaining items
    for (int i = n - 1; i >= 0; i--) 
    {
        swap(arr[0], arr[i]);
        heapify(arr, i, 0);
    }
}

Procedure ๐’๐ž๐ฅ๐ž๐œ๐ญ๐ข๐จ๐ง-๐’๐จ๐ซ๐ญ

pic-2

Basic idea: pick out minimum element from input, then recursively sort remaining elements, and finally concatenate the minimum element with sorted remaining elements.

In: An array $A$ of $n$ integers.
Out: A permutation of that array $A$ that is sorted (monotonic).

SelectionSort($A$):
for $i := 1$ to $A.length$
โ€ƒโ€ƒ$minIdx := i$
โ€ƒโ€ƒfor $j := i + 1$ to $A.length$
โ€ƒโ€ƒโ€ƒโ€ƒif $A[j] < A[minIdx]$
โ€ƒโ€ƒโ€ƒโ€ƒโ€ƒโ€ƒ$minIdx := j$
โ€ƒโ€ƒโ€ƒโ€ƒ$Swap(i, minIdx)$


One possible implementation with C++:

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
void SelectSort(int* arr, int n)
{
    // ไฟๅญ˜ๅ‚ไธŽๅ•่ถŸๆŽ’ๅบ็š„็ฌฌไธ€ไธชๆ•ฐๅ’Œๆœ€ๅŽไธ€ไธชๆ•ฐ็š„ไธ‹ๆ ‡
    int begin = 0, end = n - 1;
    while (begin < end)
    {
        int maxIndex = begin; // ไฟๅญ˜ๆœ€ๅคงๅ€ผ็š„ไธ‹ๆ ‡
        int minIndex = begin; // ไฟๅญ˜ๆœ€ๅฐๅ€ผ็š„ไธ‹ๆ ‡

        // ๆ‰พๅ‡บๆœ€ๅคงๅ€ผๅ’Œๆœ€ๅฐๅ€ผๅฏนๅบ”็š„ไธ‹ๆ ‡
        for (int i = begin; i <= end; ++i)
        {
            if (arr[i] < arr[minIndex])
                minIndex = i;
            if (arr[i] > arr[maxIndex])
                maxIndex = i;
        }

        // ๆœ€ๅฐๅ€ผๆ”พๅœจๅบๅˆ—ๅผ€ๅคด
        swap(&arr[minIndex], &arr[begin]); 

        // ๅฆ‚ๆžœๆœ€ๅคงๅ€ผๅŽŸๆœฌๅœจ begin ไฝ็ฝฎ๏ผŒๅณ maxIndex == begin๏ผŒ
        // ้‚ฃไนˆไธŠ้ข็š„ๆ“ไฝœไผšๅฐ†ๆœ€ๅคงๅ€ผๆขๅˆฐไธ‹ๆ ‡ไธบ minIndex ๅค„๏ผŒ
        // ๆญคๆ—ถ maxIndex ไธๅ†ๆ˜ฏๆœ€ๅคงๅ€ผ็š„ไธ‹ๆ ‡๏ผŒ่€Œๆ˜ฏๆœ€ๅฐๅ€ผ็š„ใ€‚
        // ไธบไบ†้˜ฒๆญขไปฅไธŠๆƒ…ๅ†ตๅ‘็”Ÿ๏ผŒๆˆ‘ไปฌ้œ€่ฆๆ›ดๆ–ฐไธ€ไธ‹ maxIndexใ€‚
        if (begin == maxIndex)
            maxIndex = minIndex;
        
        // ๆœ€ๅคงๅ€ผๆ”พๅœจๅบๅˆ—็ป“ๅฐพ
        swap(&arr[maxIndex], &arr[end]); 

        ++begin;
        --end;
    }
}

Procedure ๐’๐ก๐ž๐ฅ๐ฅ-๐’๐จ๐ซ๐ญ

pic-3

One possible implementation with C++:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits/stdc++.h>
using namespace std;

void ShellSort(vector<int> &nums)
{
    int size = nums.size();
    for (int gap = size / 2; gap > 0; gap /= 2)
    {
        // ไฝฟ็”จๆ’ๅ…ฅๆŽ’ๅบๅฏนๅฝ“ๅ‰้—ด้š”่ฟ›่กŒๆŽ’ๅบ
        for (int i = gap; i < size; i++)
        {
            int key = nums[i];
            int j = i;
            while (j >= gap && nums[j - gap] > key)
            {
                nums[j] = nums[j - gap];
                j -= gap;
            }
            nums[j] = key;
        }
    }
}

Procedure ๐๐ฎ๐›๐›๐ฅ๐ž-๐’๐จ๐ซ๐ญ

pic-4

Basic idea: repeatedly step through the array, compare adjacent pairs and swaps them if they are in the wrong order. Thus, larger elements โ€œbubbleโ€ to the โ€œtopโ€.

In: An array $A$ of $n$ integers.
Out: A permutation of that array $A$ that is sorted (monotonic).

BubbleSort($A$):
for $i := A.length$ down to $2$
โ€ƒโ€ƒfor $j := 1$ to $i - 1$
โ€ƒโ€ƒโ€ƒโ€ƒif $A[j] > A[j + 1]$
โ€ƒโ€ƒโ€ƒโ€ƒโ€ƒโ€ƒ$Swap(A[j], A[j + 1])$


One possible implementation with C++:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void BubbleSort(int arr[], int len) 
{
        int i, j;
        for (i = 0; i < len - 1; i++)
        {
            for (j = 0; j < len - 1 - i; j++)
            {
                if (arr[j] > arr[j + 1])
                {
                    swap(arr[j], arr[j + 1]);
                }
            }
        }
}

Procedure ๐๐ฎ๐ข๐œ๐ค-๐’๐จ๐ซ๐ญ

pic-5

Basic idea: Given an array $A$ of $n$ items.

  • Choose one item $x$ in $A$ as the pivot.
  • Use the pivot to partition the input into $B$ and $C$, so that items in $B$ are $\leq x$, and items in $C$ are $> x$.
  • Recursively sort $B$ and $C$.
  • Output $โŸจB, x, CโŸฉ$.

In: An array $A$ of $n$ integers.
Out: A permutation of that array $A$ that is sorted (monotonic).

QuickSortAbs($A$):
$x := GetPivot(A)$
$<B, C> := Partition(A, x)$
$QuickSortAbs(B)$
$QuickSortAbs(C)$
return $Concatenate(B, x, C)$


One possible implementation with C++:

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
#include <bits/stdc++.h>
using namespace std;

// ้€‰ๅฎšไธ€ไธช pivot ๅนถๅฐ† left ๅˆฐ right ไน‹้—ด็š„ๅ…ƒ็ด ้€š่ฟ‡ pivot ๅˆ’ๅˆ†ๆˆไธค้ƒจๅˆ†๏ผŒ
// ไฝฟๅพ— pivot ๅทฆ่พน็š„ๅ…ƒ็ด ้ƒฝๅฐไบŽๅฎƒ๏ผŒๅณ่พน็š„ๅ…ƒ็ด ้ƒฝๅคงไบŽๅฎƒ๏ผŒ็„ถๅŽ่ฟ”ๅ›ž pivot ็š„ไธ‹ๆ ‡
int _partition(vector<int> &nums, int left, int right)
{
  int pivot = nums[left]; // ไปค pivot ไธบๅทฆ่พน็ฌฌไธ€ไธชๅ…ƒ็ด 
  while (left < right)
  {
    // ๅฆ‚ๆžœๅณ่พนๅ…ƒ็ด ๅคงไบŽ pivot๏ผŒไธ็ฎก๏ผ›ๅไน‹๏ผŒๅˆ™่ฎฉๅฎƒๅˆฐๅทฆ่พนๅŽป
    while (left < right && nums[right] >= pivot)
      right--;
    nums[left] = nums[right];
    // ๅฆ‚ๆžœๅทฆ่พนๅ…ƒ็ด ๅฐไบŽ pivot๏ผŒไธ็ฎก๏ผ›ๅไน‹๏ผŒๅˆ™่ฎฉๅฎƒๅˆฐๅณ่พนๅŽป
    while (left < right && nums[left] <= pivot)
      left++;
    nums[right] = nums[left];
  }
  nums[left] = pivot; // ๆœ€ๅŽ่ฎฐๅพ—ๆขๅค pivot๏ผ๏ผ๏ผ
  return left;
}

// ๅฐ† nums ้€š่ฟ‡ _partition ๅˆ’ๅˆ†ๆˆไธค้ƒจๅˆ†๏ผŒๅฏนๆฏไธช้ƒจๅˆ†่ฐƒ็”จ _quick_sort
void _quick_sort(vector<int> &nums, int left, int right)
{
  if (left < right)
  {
    int p = _partition(nums, left, right);
    _quick_sort(nums, left, p - 1);
    _quick_sort(nums, p + 1, right);
  }
}

void QuickSort(vector<int> &nums)
{
  _quick_sort(nums, 0, nums.size() - 1);
}

Procedure ๐๐ฎ๐œ๐ค๐ž๐ญ-๐’๐จ๐ซ๐ญ

  1. Create $d$ empty lists. (These are the buckets.)
  2. Scan through input, for each item, append it to the end of the corresponding list.
  3. Concatenate all lists.

This algorithm only takes $\Theta(n)$ time.

  • This is not a comparison based algorithm.
  • No comparison between items are made.
  • Instead the algorithm uses actual values of the items.
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
void bucketSort(vector<int> &nums) 
{
    int n = nums.size();
    int mn = nums[0], mx = nums[0];
    for (int i = 1; i < n; i++) 
    {
        mn = min(mn, nums[i]);
        mx = max(mx, nums[i]);
    }
    int size = (mx - mn) / n + 1;   // size ่‡ณๅฐ‘่ฆไธบ1
    int cnt = (mx - mn) / size + 1; // ๆกถ็š„ไธชๆ•ฐ่‡ณๅฐ‘่ฆไธบ1
    vector<vector<int>> buckets(cnt);
    for (int i = 0; i < n; i++) 
    {
        int idx = (nums[i] - mn) / size;
        buckets[idx].push_back(nums[i]);
    }
    for (int i = 0; i < cnt; i++) 
    {
        sort(buckets[i].begin(), buckets[i].end());
    }
    int index = 0;
    for (int i = 0; i < cnt; i++) 
    {
        for (int j = 0; j < buckets[i].size(); j++) 
        {
            nums[index++] = buckets[i][j];
        }
    }
}

Procedure ๐‘๐š๐๐ข๐ฑ-๐’๐จ๐ซ๐ญ

pic

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
int maxbit(int data[], int n) // ่พ…ๅŠฉๅ‡ฝๆ•ฐ๏ผŒๆฑ‚ๆ•ฐๆฎ็š„ๆœ€ๅคงไฝๆ•ฐ
{
    int maxData = data[0]; // ๆœ€ๅคงๆ•ฐ
    // ๅ…ˆๆฑ‚ๅ‡บๆœ€ๅคงๆ•ฐ๏ผŒๅ†ๆฑ‚ๅ…ถไฝๆ•ฐ๏ผŒ่ฟ™ๆ ทๆœ‰ๅŽŸๅ…ˆไพๆฌกๆฏไธชๆ•ฐๅˆคๆ–ญๅ…ถไฝๆ•ฐ๏ผŒ็จๅพฎไผ˜ๅŒ–็‚นใ€‚
    for (int i = 1; i < n; ++i)
    {
        if (maxData < data[i])
            maxData = data[i];
    }
    int d = 1;
    int p = 10;
    while (maxData >= p)
    {
        maxData /= 10;
        ++d;
    }
    return d;
}

void radixsort(int data[], int n) // ๅŸบๆ•ฐๆŽ’ๅบ
{
    int d = maxbit(data, n);
    int *tmp = new int[n];
    int *count = new int[10]; // ่ฎกๆ•ฐๅ™จ
    int i, j, k;
    int radix = 1;
    for (i = 1; i <= d; i++) // ่ฟ›่กŒ d ๆฌกๆŽ’ๅบ
    {
        for (j = 0; j < 10; j++)
            count[j] = 0; // ๆฏๆฌกๅˆ†้…ๅ‰ๆธ…็ฉบ่ฎกๆ•ฐๅ™จ
        for (j = 0; j < n; j++)
        {
            k = (data[j] / radix) % 10; // ็ปŸ่ฎกๆฏไธชๆกถไธญ็š„่ฎฐๅฝ•ๆ•ฐ
            count[k]++;
        }
        for (j = 1; j < 10; j++)
            count[j] = count[j - 1] + count[j]; // ๅฐ† tmp ไธญ็š„ไฝ็ฝฎไพๆฌกๅˆ†้…็ป™ๆฏไธชๆกถ
        for (j = n - 1; j >= 0; j--)            // ๅฐ†ๆ‰€ๆœ‰ๆกถไธญ่ฎฐๅฝ•ไพๆฌกๆ”ถ้›†ๅˆฐ tmp ไธญ
        {
            k = (data[j] / radix) % 10;
            tmp[count[k] - 1] = data[j];
            count[k]--;
        }
        for (j = 0; j < n; j++) // ๅฐ†ไธดๆ—ถๆ•ฐ็ป„็š„ๅ†…ๅฎนๅคๅˆถๅˆฐ data ไธญ
            data[j] = tmp[j];
        radix = radix * 10;
    }
    delete[] tmp;
    delete[] count;
}

Summary

AlgorithmsBestWorstAverageIn/Out-PlaceStability
Insertion-Sort$O(n^2)$$O(n^2)$$O(n^2)$In-placeTrue
Merge-Sort$O(n\log{n})$$O(n\log{n})$$O(n\log{n})$Out-placeTrue
Heap-Sort$O(n\log{n})$$O(n\log{n})$$O(n\log{n})$In-placeFalse
Selection-Sort$O(n^2)$$O(n^2)$$O(n^2)$In-placeFalse
Shell-Sort$O(n\log{n})$$O(n^2)$-In-placeFalse
Bubble-Sort$O(n)$$O(n^2)$$O(n^2)$In-placeTrue
Quick-Sort$O(n\log{n})$$O(n\log{n})$$O(n\log{n})$In-placeFalse
Bucket-Sort$O(n + k)$$O(n^2)$$O(n + k)$Out-placeTrue
Radix-Sort$O(n \cdot k)$$O(n \cdot k)$$O(n \cdot k)$Out-placeTrue
  1. All the images in this article are derived from this blog.ย ↩︎

ๆœฌๆ–‡็”ฑไฝœ่€…ๆŒ‰็…ง CC BY 4.0 ่ฟ›่กŒๆŽˆๆƒ