十大经典算法详解

Algorithm

Posted by Ekko on July 29, 2020

这个博客总结的不错。

algorithm.png

结合其他博客与相关内容,此篇文章逐渐被完善,便于读者理解。

[TOC]


冒泡排序

时间复杂度O(n^2),空间复杂度O(1),稳定。

从头开始,每次两个相邻的元素,若大者在前,则交换两元素直至数组末尾 此时最大元素为数组最后的元素 重复以上步骤,从头开始至上一轮比较的末尾元素;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void bubbleSort(int[] array) {
    if (array.length < 2) {
        return;
    }
    // 每轮询一遍,都将最大的元素移到最后一位,所以end--
    for (int end = array.length - 1; end > 0; end--) {
        // 每次都从数组的第一位开始遍历
        for (int i = 0; i < end; i++) {
            if (array[i] > array[i + 1]) {
                swap(array, i, i + 1);
            }
        }
    }
}

数据的顺序排好之后,冒泡算法仍然会继续进行下一轮的比较,直到arr.length-1次,后面的比较没有意义的 设置标志位,防止后面不必要的遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 public static void bubbleSort2(int[] array) {
    if (array.length < 2) {
        return;
    }
    // 每轮询一遍,都将最大的元素移到最后一位,所以end--
    for (int end = array.length - 1; end > 0; end--) {
        // 这样当一轮比较结束后如果flag仍为false,即:这一轮没有发生交换,说明数据的顺序已经排好,没有必要继续进行下去。
        boolean ixExchanged = false;
        // 每次都从数组的第一位开始遍历
        for (int i = 0; i < end; i++) {
            if (array[i] > array[i + 1]) {
                swap(array, i, i + 1);
                ixExchanged = true;
            }
        }
        if (!ixExchanged) {
            break;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
/**
 * 交换元素位置
 *
 * @param array
 * @param index
 * @param nextIndex
*/
private static void swap(int[] array, int index, int nextIndex) {
    int temp = array[index];
    array[index] = array[nextIndex];
    array[nextIndex] = temp;
}

选择排序

时间复杂度O(n^2),空间复杂度O(1),不稳定。

选择排序(Selection sort)是一种简单直观的排序算法 第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置 然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾 以此类推,直到全部待排序的数据元素的个数为零 选择排序是不稳定的排序方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void selectSort(int array[]) {
    // 每次遍历将最小值放到在下标 i 上
    for (int i = 0; i < array.length; i++) {
        int minIndex = i;
        for (int j = i + 1; j < array.length; j++) {
            if (array[minIndex] > array[j]) {
                // 选出出最小值的下标
                minIndex = j;
            }
        }
        if (minIndex != i) {
            swap(array, i, minIndex);
        }
    }
}

插入排序

时间复杂度O(n^2),空间复杂度O(1),稳定。

插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法 将第一个元素看作有序序列,后续元素当作无需序列,依次将无序序列元素插入有序序列当中; 在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动

InsertionSort 和打扑克牌时,从牌桌上逐一拿起扑克牌,在手上排序的进程相同。举例: Input: {4, 3, 8, 5, 2, 6, 1, 7} 首先拿起第一张牌, 手上有 {4}。 拿起第二张牌 3, 把 3 insert 到手上的牌 {4}, 得到 {3 ,4}。 拿起第三张牌 8, 把 8 insert 到手上的牌 {3,4 }, 得到 {3 ,4,8}

插入排序由N-1趟排序组成。对于p=1到N-1趟排序后,插入排序保证从位置0到位置p上的元素为已排序状态。即插入排序利用了从位置0到p-1位置上已经有序的条件,将位置p上的元素向前查找适当的位置插入此元素。

1
2
3
4
5
6
7
8
9
10
11
12
public static void insertionSort(int[] array) {
    if (array.length <= 1) {
        return;
    }
    // i = 0 默认有序,所以从 i = 1 开始遍历
    for (int i = 1; i < array.length; i++) {
        // 其实就是一个往后插入的逆向冒泡排序的过程
        for (int j = i - 1; j >= 0 && array[j] > array[j + 1]; j--) {
            swap(array, j, j + 1);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 为后面的希尔排序铺垫
public static void insertionSort2(int[] array) {
    if (array.length <= 1) {
        return;
    }
    // 待插入元素,array[0]默认排号序,作为前序子数组
    int i = 1;
    while (i < array.length) {
        // 起始位置:获取前序子数组最后一位
        int j = i - 1;
        int waitInsert = array[i];
        while (j >= 0 && array[j] > waitInsert) {
            // j-- 遍历,找到需要插入的位置
            array[j + 1] = array[j];
            j--;
        }
        // 插入前序子数组的末尾
        array[j + 1] = waitInsert;
        i++;
    }
}

希尔排序

时间复杂度O(nlogn),空间复杂度O(1),不稳定。

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止

选择一个增量序列,初始增量gap=length/2,后续元素依次为前一元素除2,直至gap=1;每轮以gap为步长,在列表上进行采样,将列表分为gap个小组,在每个小组内进行选择排序;重复第二步,直至gap=1;辅助理解参考博客园

shellSort.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 直接插入排序增量是 1 ,希尔排序增量是 gap
public static void shellSort(int[] array) {
    int length = array.length;
    for (int gap = length / 2; gap >= 1; gap /= 2) {
        for (int i = gap; i < length; i++) {
            // 使用插入排序算法,将元素依次插入所在小组的已排序列表中
            // 待插入元素
            int waitInsert = array[i];
            int j = i - gap;
            // 前序子数组已经排好序,反向遍历,找到合适的位置插入元素
            while (j >= 0 && array[j] >= waitInsert) {
                array[j + gap] = array[j];
                j -= gap;
            }
            array[j + gap] = waitInsert;
        }
    }
}

归并排序

时间复杂度O(nlogn),空间复杂度O(n),稳定。

归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用

将列表从正中间分为两个子列表;按照第一步,递归拆分每个子列表,直至子列表最大长度为1;按照拆分层级,依次按大小合并各子列表,直至全部合并完成。

基本思想

归并排序是用分治思想,分治模式在每一层递归上有三个步骤:

  1. 分解(Divide):将n个元素分成个含n/2个元素的子序列。
  2. 解决(Conquer):用合并排序法对两个子序列递归的排序。
  3. 合并(Combine):合并两个已排序的子序列已得到排序结果

实现逻辑:

迭代法

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并间,并移动指针到下一位置
  4. 重复 步骤3 直到某一指针到达序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

递归法

  1. 将序列每相邻两个数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素
  2. 将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
  3. 重复 步骤2,直到所有元素排序完毕

两两合并,先比较左右子数组头部元素(大或小),将小的头部元素移动到新数组(此时移动的旧子数组头部因为已经为空,所以同数组的下一个元素晋升为新的头元素) 当一个子数组为空的时候,依次将另一个子数组剩余元素移动到新数组末尾

MergeSort.png

递归版本:

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
public static void main(String[] args) {
    int[] array = { 6, 4, 8, 9, 2, 3, 1};
    mergeSort(array);
    for (int num : array) {
        System.out.print(num + " ");
    }
}

 // 递归版,入口方法-1
public static void mergeSort(int[] array) {
    // 先开辟一个大小相同的空数组,防止频繁申请空间
    int[] tempArray = new int[array.length];
    mergeSort(array, 0, array.length - 1, tempArray);
}

// 递归版 方法-2
public static void mergeSort(int[] array, int left, int rigth, int[] tempArray) {
    if (left < rigth) {
        int mid = (left + rigth) >> 1;
        // 分为左右两个子数组
        // { 6, 4, 8, 9, 2, 3, 1}  left = 0 ,right = 6 , mid = 3
        // {6,4,8,9} left = 0, right = 3, mid = 1; {2,3,1} left = 4, right = 6, mid = 5;
        // {6,4} {8,9} {2,3} {1}
        // {6} {4} {8} {9} {2} {3} {1} 本应该分成这样,但此时left = right, 不满足 if 条件,所以执行上面的merge合并操作
        // 上一步merger操作的 left = 0;right = 1; mid = 0
        mergeSort(array, left, mid, tempArray);
        mergeSort(array, mid + 1, rigth, tempArray);
        merge(array, left, mid, rigth, tempArray);
    }
}

// 递归版 方法-3
// 子数组合并实现
public static void merge(int[] array, int left, int mid, int rigth, int[] tempArray) {
    int i = left; // 左数组头元素
    int j = mid + 1; // 右数组头元素
    int k = 0; // 中间数组有序存储合并后的元素
    // 将小的头部元素移动到新数组(此时移动的旧子数组头部因为已经为空,所以下一个元素晋升为新的头元素)
    // 合并[left,mid]、[mid+1,right]两个数组
    // 所以 i 的边界是 = mid,大于mid表示左数组遍历完毕(为空)
    // j 的边界是 = right,大于 right 表示右数组遍历完毕(为空)
    while (i <= mid && j <= rigth) {
        // 比较两个子数组的头部元素,小的移动到中间数组,下标 ++ 表示后一位晋升为头部元素
        if (array[i] < array[j]) {
            tempArray[k++] = array[i++];
        } else {
            tempArray[k++] = array[j++];
        }
    }
    // 将未遍历完的子数组,依次添加到中间数组
    while (i <= mid) {
        tempArray[k++] = array[i++];
    }

    while (j <= rigth) {
        tempArray[k++] = array[j++];
    }

    // 将合并的有序中间数组替换到原数组位置中
    // 因为每次中间数组都是以 0 下标开始,所以先对 k 作初始化
    k = 0;
    while (left <= rigth) {
        array[left++] = tempArray[k++];
    }
}

迭代版本

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
// 第一次比2个元素,第二次4个元素,第三次8个元素,第四次16个元素,两两归并
public static void mergeSortIteration(int[] array) {
    int length = array.length;
    // 创建一个大小和目标数组相同大小的中间数组
    int[] tempArray = new int[length];
    for (int block = 1; block < length; block *= 2) { // 步长
        for (int leftMin = 0; leftMin < length - 1; leftMin += block) {
            int leftMax = leftMin + block; // 左子数组的边界
            int rightMin = leftMin + block; // 右子数组的起始位
            int rightMax = rightMin + block; // 右子数组的边界
            if (rightMax > length) { // 右子数组的边界不能超过原数组长度
                rightMax = length;
            }
            // 中间数组下标
            int k = 0;
            while (leftMin < leftMax && rightMin < rightMax) {
                if (array[leftMin] < array[rightMin]) { // 左右两子数组头部元素比较
                    tempArray[k++] = array[leftMin++];
                } else {
                    tempArray[k++] = array[rightMin++];
                }
            }
            while (leftMin < leftMax) { // 左子数组没遍历完
                tempArray[k++] = array[leftMin++];
            }

            while (rightMin < rightMax) { // 右子数组没遍历完
                tempArray[k++] = array[rightMin++];
            }
            // 将中间数组元素复制到目标数组[leftMin,rightMax]位置
            while (k > 0) {
                array[--rightMax] = tempArray[--k];
            }
        }
    }
}

迭代结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
交换前9 8 6 4 3 2 1 
block:1 leftMin:0 leftMax:1 rightMin:1 rightMax:2
交换后8 9 6 4 3 2 1 

交换前8 9 6 4 3 2 1 
block:1 leftMin:2 leftMax:3 rightMin:3 rightMax:4
交换后8 9 4 6 3 2 1 

交换前8 9 4 6 3 2 1 
block:1 leftMin:4 leftMax:5 rightMin:5 rightMax:6
交换后8 9 4 6 2 3 1 

交换前8 9 4 6 2 3 1 
block:2 leftMin:0 leftMax:2 rightMin:2 rightMax:4
交换后4 6 8 9 2 3 1 

交换前4 6 8 9 2 3 1 
block:2 leftMin:4 leftMax:6 rightMin:6 rightMax:7
交换后4 6 8 9 1 2 3 

交换前4 6 8 9 1 2 3 
block:4 leftMin:0 leftMax:4 rightMin:4 rightMax:7
交换后1 2 3 4 6 8 9 

快速排序

最差时间复杂度为O(n^2),最优时间复杂度O(nlogn),空间复杂度O(logn)【每次记录断点值,最好的情况就每次记录一半的断点,类似于二分法,左侧用完的断点用完可以释放,右侧同理,如果是最差的情况,即选择了边界值,所以最差的空间复杂度为O(n)】,不稳定。(论文级别的可以做到稳定性《01 stable sort》)

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序

  1. 从列表中选出一个元素,作为“基准”pivot,基准一般随机选择,或采用最左端、最右端和中间位置3元素的中值;
  2. 将小于基准的元素排在基准前面,大于基准的元素排在基准后面,此时基准元素所在位置即为其最终排序完成时的位置;
  3. 以基准元素为界,将列表分为两个子列表;
  4. 递归地对子列表重复上述操作。

在工程上是使用随机选择,从概率的角度讲,时间复杂度为O(nlogn)。划分值的值尽量可以等分数组,如果为边界值,则算法会退化成O(n^2)

左程云算法:利用一个左边界小于区,最开始在-1位置,然后与最后一个数进行比较,然后左边的指针与最后一个数进行比较,如果比他更小,则当前的值与小于区的下一个位置进行交换,再将小于区的位置增加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
// 简易版,选数组的第一个元素作为基准
public static int[] quicksort(int[] array) {
    return quicksort(array, 0, array.length - 1);
}

public static int[] quicksort(int[] array, int left, int right) {
    int basic = array[left];
    int i = left;
    int j = right;
    // 第一趟排序,根据基准划分左右序列
    while (i < j) {
        while (i < j && array[j] > basic) { // 说明右值大于基准值,指针向前移动
            j--;
        }
        while (i < j && array[i] < basic) { // 说明左值大于基准值,指针后移
            i++;
        }
        if (i < j && array[i] == array[j]) {
            i++;
        } else {
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    }
    if (i - 1 > left) {
        array = quicksort(array, left, i - 1);
    }
    if (j + 1 < right) {
        array = quicksort(array, j + 1, right);
    }
    return array;
}

左程云版本:

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
public static void quickSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }
    quickSort(arr, 0, arr.length - 1);
}

public static void quickSort(int[] arr, int l, int r) {
    if (l < r) {
        swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
        int[] p = partition(arr, l, r);
        quickSort(arr, l, p[0] - 1);
        quickSort(arr, p[1] + 1, r);
    }
}

public static int[] partition(int[] arr, int l, int r) {
    int less = l - 1;
    int more = r;
    while (l < more) {
        if (arr[l] < arr[r]) {
            swap(arr, ++less, l++);
        } else if (arr[l] > arr[r]) {
            swap(arr, --more, l);
        } else {
            l++;
        }
    }
    swap(arr, more, r);
    return new int[] { less + 1, more };
}

迭代版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 快速排序 非递归(迭代版)
void quickSortIteration(vector<int>& array) {
	stack<vector<int>> boundaries;
	int left = 0, right = array.size() - 1;
	while (left < right || !boundaries.empty()) {
		if (left >= right) {
			vector<int> boundary = boundaries.top();
			boundaries.pop();
			left = boundary[0];
			right = boundary[1];
		}
		int pivotLoction = partition(array, left, right);
		if (pivotLoction + 1 < right) {
			boundaries.push({ pivotLoction + 1, right });
		}
		right = pivotLoction - 1;
	}
}

堆排序

时间复杂度O(nlogn),空间复杂度O(1),不稳定。

建立堆的过程是O(n)的时间复杂度。

按层将堆转换成数组后: 大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] 小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

堆排序的基本思想: 将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了(一般升序采用大顶堆,降序采用小顶堆)

  1. 将数字转化为一个堆;堆是具有以下两属性的二叉树:(1)每个节点的值大于等于其子节点的值;(2)树完全平衡,即最底层叶子节点都位于左侧(完全),且左右子树高度相差不超过1(平衡);因为,堆是完全平衡树,因此可以用数组直接表示:堆也被称为优先队列,具有先进先出的特性,在堆底插入元素,在堆顶取出元素。
  2. 取出堆顶元素(最大元素),作为有序数数组末尾元素,并对二叉树进行调整使其满足堆的特性;
  3. 重复上一步骤,依次取出堆顶元素,并插入到有序数组中,上一插入元素之前的位置,直到堆空为止;

Heapsort.png

左程云算法:

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
public static void heapSort(int[] arr) {
    if (arr == null || arr.length < 2) {
        return;
    }
    for (int i = 0; i < arr.length; i++) {
        // 插入,建立一个大根堆
        heapInsert(arr, i);
    }
    int size = arr.length;
    // 让第一个数和最后一个数交换位置
    // 最后一个元素不再动了
    swap(arr, 0, --size);
    // 重复调整大根堆
    while (size > 0) {
        heapify(arr, 0, size);
        swap(arr, 0, --size);
    }
}

public static void heapInsert(int[] arr, int index) {
    // 当前节点位置比父节点位置更大,则循环交换
    while (arr[index] > arr[(index - 1) / 2]) {
        swap(arr, index, (index - 1) / 2);
        index = (index - 1) / 2;
    }
}
// 跟自己的左右孩子比较,将大的数上移
public static void heapify(int[] arr, int index, int size) {
    // 左孩子下标
    int left = index * 2 + 1;
    while (left < size) {
        // 在右孩子不越界,然后找到左右孩子中更大的孩子下标
        int largest = left + 1 < size && arr[left + 1] > arr[left]
                            ? left + 1 
                            : left;
        // 获取左右孩子下标后,判断与父节点的大小,返回一个最大值的下标
        largest = arr[largest] > arr[index] ? largest : index;
        // 如果最大值为父节点,即无需调整,退出循环
        if (largest == index) {
            break;
        }
        // 需要调整大根堆
        swap(arr, largest, index);
        // 更换父节点的下标值,继续执行左右孩子的大小比较
        index = largest;
        left = index * 2 + 1;
    }
}

计数排序

时间复杂度O(n+k),空间复杂度O(k),稳定。

  1. 遍历待排序数组A,找出其最小值min和最大值max;
  2. 创建一个长度为max-min+1的数组B,其所有元素初始化为0,数组首位对应数组A的min元素,索引为i位置对应A中值为min+i的元素;
  3. 遍历数组A,在B中对应位置记录A中各元素出现的次数;
  4. 遍历数组B,按照之前记录的出现次数,输出几次对应元素;
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
// 计数排序
void countSort(vector<int>& array){
    if (array.empty()){
        return;
    }
    //找出最大最小值
    int min = array.front(),max = array.front();
    for (int i = 1; i < array.size(); i++){
        if (min > array[i]){
            min = array[i];
        }
        else if (max < array[i]){
            max = array[i];
        }
    }

    // 记录各元素出现次数
    vector<int> counts(max - min + 1);
    for (int i = 0; i < array.size(); i++){
        counts[array[i] - min]++;
    }

    // 根据记录的次数输出对应元素
    int index = 0;
    for (int j = 0; j < counts.size(); j++){
        int n = counts[j];
        while (n--){
            array[index] = j + min;
            index++;
        }
    }
}

桶排序

时间复杂度O(n+k),空间复杂度O(n+k),稳定。

  1. 设置固定数量的空桶;
  2. 找出待排序数组的最大值和最小值;
  3. 根据最大最小值平均划分各桶对应的范围,并将待排序数组放入对应桶中;
  4. 为每个不为空的桶中数据进行排序(例如,插入排序);
  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
// 桶排序
void bucketSort (vector<int>& array, int bucketCount){
    if (array.empty()){
        return;
    }
    // 找出最大最小值
    int max = array.front(), min = array.front();
    for (int i = 1; i < array.size(); i++){
        if (min > array[i]){
            min = array[i];
        }
        else if (max < array[i]){
            max = array[i];
        }
    }

    // 将待排序的各元素分入对应桶中
    vector<vector<int>> buckets(bucketCount);
    int bucketSize = ceil((double)(max - min + 1) / bucketCount);
    for (int i = 0; i < array.size(); i++){
        int bucketIndex = (array[i] - min) / bucketSize;
        buckets[bucketIndex].push_back(array[i]);
    }

    // 对各桶中元素进行选择排序
    int index = 0;
    for (vector<int> bucket : buckets){
        if (!bucket.empty()){
            // 使用选择排序算法对桶内元素进行排序
            selectSort(bucket);
            for (int value : bucket){
                array[index] = value;
                index++;
            }
        }
    }

}
// 桶排序
void bucketSort (vector<int>& array){
    bucketSort (array, array.size() / 2);
}

基数排序

时间复杂度O(n*k),空间复杂度O(n+k),稳定。

  1. 将各待比较元素数值统一数位长度,即对数位短者在前补零;
  2. 根据个位数值大小,对数组进行排序;
  3. 重复上一步骤,依次根据更高位数值进行排序,直至到达最高位;

jishu

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
// 基数排序 (只适用于正数,此处不适用)
void radixSort(vector<int>& array){
    // 当前位数
    int curdigit = 10;
    // 当前位是否已超过最高为
    bool isOverHighest = false;
    while (!isOverHighest){
        isOverHighest = true;
        // 利用分桶的思想来实现按各位进行排序
        vector<vector<int>> buckets(10);
        for (int curVal : array){
            int bucketIndex = curVal % curdigit - curVal % (curdigit / 10);
            buckets[bucketIndex].push_back(curVal);
            if (isOverHighest && curVal / curdigit){
                isOverHighest = false;
            }
        }
        // 按照桶的顺序,将各桶内元素拼接起来
        int index = 0;
        for (vector<int> bucket : buckets){
            for (int value : bucket){
                array[index] = value;
                index++;
            }
        }
        curdigit *= 10;
    }
}