交换排序
1. 冒泡排序
1.1 无优化
C 实现:
c
static void swap(int* table, int index1, int index2) {
int temp = table[index1];
table[index1] = table[index2];
table[index2] = temp;
}
// 每轮发现一个最大值,第一次遍历[0...n - 1),第二次遍历[0...n - 2)
// 经过n - 1轮
void bubbleSort1(int* table, int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (table[j] > table[j + 1]) {
swap(table, j, j + 1);
}
}
}
}
Java 实现:
java
private static void swap(int[] table, int index1, int index2) {
int temp = table[index1];
table[index1] = table[index2];
table[index2] = temp;
}
public static void bubbleSort1(int[] table, int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (table[j] > table[j + 1]) {
swap(table, j, j + 1);
}
}
}
}
1.2 优化一
C 实现:
c
// 由于冒泡思想,每轮都把尽量大的元素往后移动,小的元素尽量往前移动,某一轮后,整个元素都有序了
void bubbleSort2(int* table, int n) {
for (int i = 0; i < n - 1; i++) {
int flag = 1;
for (int j = 0; j < n - 1 - i; j++) {
if (table[j] > table[j + 1]) {
swap(table, j, j + 1);
flag = 0;
}
}
// 没有进行交换
if (flag) {
return;
}
}
}
Java 实现:
java
public static void bubbleSort2(int[] table, int n) {
for (int i = 0; i < n - 1; i++) {
boolean flag = true;
for (int j = 0; j < n - i - 1; j++) {
if (table[j] > table[j + 1]) {
swap(table, j, j + 1);
flag = false;
}
}
if (flag) return;
}
}
1.3 优化二
C 实现:
c
// 循环多少轮不是固定的,而是由冒泡过程最后那个索引号为依据
// 引入newIndex来标记最后一次交换的位置
void bubbleSort3(int* table, int n) {
int newIndex;
do {
newIndex = 0;
for (int i = 0; i < n - 1; i++) {
if (table[i] > table[i + 1]) {
swap(table, i, i + 1);
newIndex = i + 1;
}
}
n = newIndex;
} while (newIndex != 0);
}
Java 实现:
java
public static void bubbleSort3(int[] table, int n) {
int newIndex;
do {
newIndex = 0;
for (int i = 0; i < n - 1; i++) {
if (table[i] > table[i + 1]) {
swap(table, i, i + 1);
newIndex = i + 1;
}
}
n = newIndex;
} while (newIndex != 0);
}
2. 快速排序
2.1 双边循环法
C 实现 :
c
// 双边循环寻找基点
static int partitionDouble(int* table, int start, int end) {
// 随机选取基准值,防止在数组极度有序的情况下时间复杂度退化为 O(N^2)
swap(table, start, rand() % (end - start + 1) + start);
// 记录基准值
int pivot = table[start];
int left = start;
int right = end;
while (left < right) {
// 移动左右指针
// 必须让right指针先走
// 这样可以保证 left 和 right 相遇的位置,其值一定小于或等于 privot
// 注意:要包含等于号来避免死循环
while (left < right && pivot <= table[right]) {
right--;
}
while (left < right && pivot >= table[left]) {
left++;
}
// 如果两个指针还没有相遇,说明找到了需要互换的两个元素
if (left < right) {
swap(table, left, right);
}
}
// 循环结束此时 left == right。
// 将存放在首位的基准值与相遇点互换
swap(table, start, left);
// 此时 left 左边的值都 <= 基准值
// 右边都 >= 基准值
return left;
}
static void quickSortTable1(int* table, int start, int end) {
if (start >= end) {
return;
}
// 获取基准点
int pivotIndex = partitionDouble(table, start, end);
quickSortTable1(table, start, pivotIndex - 1);
quickSortTable1(table, pivotIndex + 1, end);
}
// 双边
void quickSort1(int* table, int n) {
// 传入的 end 索引必须是n - 1,防止右侧指针越界访问
quickSortTable1(table, 0, n - 1);
}
Java 实现:
java
public static void quickSort1(int[] table, int n) {
quickSortTable1(table, 0, n - 1);
}
private static void quickSortTable1(int[] table, int start, int end) {
if (start >= end) return;
int pivotIndex = partitionDouble(table, start, end);
quickSortTable1(table, start, pivotIndex - 1);
quickSortTable1(table, pivotIndex + 1, end);
}
private static int partitionDouble(int[] table, int start, int end) {
Random random = new Random();
swap(table, start, random.nextInt(start, end));
int pivot = table[start];
int left = start;
int right = end;
while (left < right) {
while (left < right && pivot <= table[right]) {
right--;
}
while (left < right && pivot >= table[left]) {
left++;
}
if (left < right) {
swap(table, left, right);
}
}
swap(table, start, left);
return left;
}
2.2 单边循环法
C 实现:
c
static int partitionSingle(int* table, int start, int end) {
// 1. 随机主元优化:在当前区间内随机选择一个元素,并将其交换到起始位置。
// 这可以有效避免数组在基本有序时,快速排序退化为 O(n^2) 的最坏时间复杂度。
swap(table, start, rand() % (end - start + 1) + start);
// 2. 取起始位置的元素作为比较的基准值(主元)
int pivot = table[start];
// 3. mark 用于记录"小于主元区域"的右边界。
// 初始时指向主元所在位置,随着遍历推移,mark 及它左边的元素(除主元外)将严格小于 pivot。
int mark = start;
// 4. 从主元的下一个位置开始遍历整个区间
for (int i = start + 1; i <= end; i++) {
// 如果发现当前元素小于主元
if (table[i] < pivot) {
// 将"小于主元区域"的边界向右扩展一位
// 并将当前小于主元的元素与扩展出的新边界位置的元素进行交换
swap(table, ++mark, i);
}
}
// 5. 遍历结束后,mark 指向的是最后一个小于主元的元素。
// 将主元(当前仍在 start 位置)与 mark 位置的元素交换,使其落入最终的排序位置。
swap(table, mark, start);
// 6. 返回主元的最终索引。此时主元左侧的元素均小于它,右侧的元素均大于等于它。
return mark;
}
static void quickSortTable2(int* table, int start, int end) {
if (start >= end) return;
// 获取基点
int pivotIndex = partitionSingle(table, start, end);
quickSortTable2(table, start, pivotIndex - 1);
quickSortTable2(table, pivotIndex + 1, end);
}
// 单边
void quickSort2(int* table, int n) {
quickSortTable2(table, 0, n - 1);
}
Java 实现:
java
public static void quickSort2(int[] table, int n) {
quickSortTable2(table, 0, n - 1);
}
private static void quickSortTable2(int[] table, int start, int end) {
if (start >= end) return;
int pivotIndex = partitionSingle(table, start, end);
quickSortTable2(table, start, pivotIndex - 1);
quickSortTable2(table, pivotIndex + 1, end);
}
private static int partitionSingle(int[] table, int start, int end) {
Random random = new Random();
swap(table, start, random.nextInt(start, end));
int pivot = table[start];
int mark = start;
for (int i = start + 1; i <= end; i++) {
if (table[i] <= pivot) {
swap(table, ++mark, i);
}
}
swap(table, start, mark);
return mark;
}
3. 完整实现
C 实现:
swapSort.h
c
#pragma once
void bubbleSort1(int* table, int n);
void bubbleSort2(int* table, int n);
void bubbleSort3(int* table, int n);
void quickSort1(int* table, int n); // 双边循环法
void quickSort2(int* table, int n); // 单边循环法
swapSort.c
c
#include <stdio.h>
#include <stdlib.h>
#include "swapSort.h"
static void swap(int* table, int index1, int index2) {
int temp = table[index1];
table[index1] = table[index2];
table[index2] = temp;
}
// 每轮发现一个最大值,第一次遍历[0...n - 1),第二次遍历[0...n - 2)
// 经过n - 1轮
void bubbleSort1(int* table, int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (table[j] > table[j + 1]) {
swap(table, j, j + 1);
}
}
}
}
// 由于冒泡思想,每轮都把尽量大的元素往后移动,小的元素尽量往前移动,某一轮后,整个元素都有序了
void bubbleSort2(int* table, int n) {
for (int i = 0; i < n - 1; i++) {
int flag = 1;
for (int j = 0; j < n - 1 - i; j++) {
if (table[j] > table[j + 1]) {
swap(table, j, j + 1);
flag = 0;
}
}
// 没有进行交换
if (flag) {
return;
}
}
}
// 循环多少轮不是固定的,而是由冒泡过程最后那个索引号为依据
// 引入newIndex来标记最后一次交换的位置
void bubbleSort3(int* table, int n) {
int newIndex;
do {
newIndex = 0;
for (int i = 0; i < n - 1; i++) {
if (table[i] > table[i + 1]) {
swap(table, i, i + 1);
newIndex = i + 1;
}
}
n = newIndex;
} while (newIndex != 0);
}
// 双边循环寻找基点
static int partitionDouble(int* table, int start, int end) {
// 随机选取基准值,防止在数组极度有序的情况下时间复杂度退化为 O(N^2)
swap(table, start, rand() % (end - start + 1) + start);
// 记录基准值
int pivot = table[start];
int left = start;
int right = end;
while (left < right) {
// 移动左右指针
// 必须让right指针先走
// 这样可以保证 left 和 right 相遇的位置,其值一定小于或等于 privot
// 注意:要包含等于号来避免死循环
while (left < right && pivot <= table[right]) {
right--;
}
while (left < right && pivot >= table[left]) {
left++;
}
// 如果两个指针还没有相遇,说明找到了需要互换的两个元素
if (left < right) {
swap(table, left, right);
}
}
// 循环结束此时 left == right。
// 将存放在首位的基准值与相遇点互换
swap(table, start, left);
// 此时 left 左边的值都 <= 基准值
// 右边都 >= 基准值
return left;
}
static void quickSortTable1(int* table, int start, int end) {
if (start >= end) {
return;
}
// 获取基准点
int pivotIndex = partitionDouble(table, start, end);
quickSortTable1(table, start, pivotIndex - 1);
quickSortTable1(table, pivotIndex + 1, end);
}
// 双边
void quickSort1(int* table, int n) {
// 传入的 end 索引必须是n - 1,防止右侧指针越界访问
quickSortTable1(table, 0, n - 1);
}
static int partitionSingle(int* table, int start, int end) {
// 1. 随机主元优化:在当前区间内随机选择一个元素,并将其交换到起始位置。
// 这可以有效避免数组在基本有序时,快速排序退化为 O(n^2) 的最坏时间复杂度。
swap(table, start, rand() % (end - start + 1) + start);
// 2. 取起始位置的元素作为比较的基准值(主元)
int pivot = table[start];
// 3. mark 用于记录"小于主元区域"的右边界。
// 初始时指向主元所在位置,随着遍历推移,mark 及它左边的元素(除主元外)将严格小于 pivot。
int mark = start;
// 4. 从主元的下一个位置开始遍历整个区间
for (int i = start + 1; i <= end; i++) {
// 如果发现当前元素小于主元
if (table[i] < pivot) {
// 将"小于主元区域"的边界向右扩展一位
// 并将当前小于主元的元素与扩展出的新边界位置的元素进行交换
swap(table, ++mark, i);
}
}
// 5. 遍历结束后,mark 指向的是最后一个小于主元的元素。
// 将主元(当前仍在 start 位置)与 mark 位置的元素交换,使其落入最终的排序位置。
swap(table, mark, start);
// 6. 返回主元的最终索引。此时主元左侧的元素均小于它,右侧的元素均大于等于它。
return mark;
}
static void quickSortTable2(int* table, int start, int end) {
if (start >= end) return;
// 获取基点
int pivotIndex = partitionSingle(table, start, end);
quickSortTable2(table, start, pivotIndex - 1);
quickSortTable2(table, pivotIndex + 1, end);
}
// 单边
void quickSort2(int* table, int n) {
quickSortTable2(table, 0, n - 1);
}
main.c
c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "swapSort.h"
int* createTable(char name[], int n) {
printf("%s:\n", name);
int* table = malloc(sizeof(int) * n);
if (table == NULL) return NULL;
for (int i = 0; i < n; i++) {
table[i] = rand() % 100 + 1;
printf("%d\t", table[i]);
}
printf("\n");
return table;
}
void showTable(int* table, int n) {
for (int i = 0; i < n; i++) {
printf("%d\t", table[i]);
}
printf("\n");
}
int main() {
srand((unsigned int)time(NULL));
int n = 10;
int* bubbleTable1 = createTable("bubbleSort1", n);
bubbleSort1(bubbleTable1, n);
showTable(bubbleTable1, n);
int* bubbleTable2 = createTable("bubbleSort2", n);
bubbleSort2(bubbleTable2, n);
showTable(bubbleTable2, n);
int* bubbleTable3 = createTable("bubbleSort3", n);
bubbleSort3(bubbleTable3, n);
showTable(bubbleTable3, n);
int* quickTable1 = createTable("quickTable1", n);
quickSort1(quickTable1, n);
showTable(quickTable1, n);
int* quickTable2 = createTable("quickTable2", n);
quickSort2(quickTable2, n);
showTable(quickTable2, n);
return 0;
}
Java 实现:
MyUtil
java
package com.sonnet.util;
import java.util.Random;
public class MyUtil {
private static void swap(int[] table, int index1, int index2) {
int temp = table[index1];
table[index1] = table[index2];
table[index2] = temp;
}
public static void bubbleSort1(int[] table, int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (table[j] > table[j + 1]) {
swap(table, j, j + 1);
}
}
}
}
public static void bubbleSort2(int[] table, int n) {
for (int i = 0; i < n - 1; i++) {
boolean flag = true;
for (int j = 0; j < n - i - 1; j++) {
if (table[j] > table[j + 1]) {
swap(table, j, j + 1);
flag = false;
}
}
if (flag) return;
}
}
public static void bubbleSort3(int[] table, int n) {
int newIndex;
do {
newIndex = 0;
for (int i = 0; i < n - 1; i++) {
if (table[i] > table[i + 1]) {
swap(table, i, i + 1);
newIndex = i + 1;
}
}
n = newIndex;
} while (newIndex != 0);
}
public static void quickSort1(int[] table, int n) {
quickSortTable1(table, 0, n - 1);
}
private static void quickSortTable1(int[] table, int start, int end) {
if (start >= end) return;
int pivotIndex = partitionDouble(table, start, end);
quickSortTable1(table, start, pivotIndex - 1);
quickSortTable1(table, pivotIndex + 1, end);
}
private static int partitionDouble(int[] table, int start, int end) {
Random random = new Random();
swap(table, start, random.nextInt(start, end));
int pivot = table[start];
int left = start;
int right = end;
while (left < right) {
while (left < right && pivot <= table[right]) {
right--;
}
while (left < right && pivot >= table[left]) {
left++;
}
if (left < right) {
swap(table, left, right);
}
}
swap(table, start, left);
return left;
}
public static void quickSort2(int[] table, int n) {
quickSortTable2(table, 0, n - 1);
}
private static void quickSortTable2(int[] table, int start, int end) {
if (start >= end) return;
int pivotIndex = partitionSingle(table, start, end);
quickSortTable2(table, start, pivotIndex - 1);
quickSortTable2(table, pivotIndex + 1, end);
}
private static int partitionSingle(int[] table, int start, int end) {
Random random = new Random();
swap(table, start, random.nextInt(start, end));
int pivot = table[start];
int mark = start;
for (int i = start + 1; i <= end; i++) {
if (table[i] <= pivot) {
swap(table, ++mark, i);
}
}
swap(table, start, mark);
return mark;
}
}
Test
java
package com.sonnet.test;
import com.sonnet.util.MyUtil;
import java.util.Random;
public class Test {
public static void main(String[] args) {
int n = 10;
int[] bubbleTable1 = createTable("bubbleSort1", n);
MyUtil.bubbleSort1(bubbleTable1, n);
showTable(bubbleTable1);
int[] bubbleTable2 = createTable("bubbleSort2", n);
MyUtil.bubbleSort2(bubbleTable2, n);
showTable(bubbleTable2);
int[] bubbleTable3 = createTable("bubbleSort3", n);
MyUtil.bubbleSort3(bubbleTable3, n);
showTable(bubbleTable3);
int[] quickTable1 = createTable("quickSort1", n);
MyUtil.quickSort1(quickTable1, n);
showTable(quickTable1);
int[] quickTable2 = createTable("quickSort2", n);
MyUtil.quickSort2(quickTable2, n);
showTable(quickTable2);
}
public static int[] createTable(String name, int n) {
Random random = new Random();
int[] table = new int[n];
System.out.println(name + " :");
for (int i = 0; i < n; i++) {
table[i] = random.nextInt(1, 100);
System.out.print(table[i] + "\t");
}
System.out.println();
return table;
}
public static void showTable(int[] table) {
for (int i : table) {
System.out.print(i + "\t");
}
System.out.println();
System.out.println();
}
}