交换排序算法

交换排序

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();
    }
}
相关推荐
敲代码的嘎仔2 小时前
Java后端开发——多线程面试题
java·开发语言·面试·多线程·八股·threadlocal·
NGC_66112 小时前
深度解析 ConcurrentHashMap 1.8:put 与 get 核心流程全解
java·开发语言
杭州杭州杭州2 小时前
J2EE实验
java·java-ee
福运常在2 小时前
股票数据API如何获取(20)炸板股池数据
java·python·maven
穿条秋裤到处跑2 小时前
每日一道leetcode(2026.03.27):循环移位后的矩阵相似检查
算法·leetcode·矩阵
CODE_RabbitV2 小时前
STM32 开发中 C 语言结构体复习(精简版)
c语言·stm32·嵌入式硬件
Cathy Bryant2 小时前
拓扑学-毛球定理
笔记·线性代数·算法·矩阵·拓扑学·高等数学
2301_788770552 小时前
模拟OJ3
数据结构·算法
靠沿2 小时前
【递归、搜索与回溯算法】专题二——二叉树的dfs
算法·深度优先