常见排序算法


以下是一份使用Java实现的常见排序算法总结资料,包含了各种排序算法的实现代码、原理说明和复杂度分析:

1. 冒泡排序(Bubble Sort)

原理:重复比较相邻元素,将较大的元素逐步"冒泡"到数组末端

public class BubbleSort {
    public static void sort(int[] arr) {
        int n = arr.length;
        for (int i = 0; i < n - 1; i++) {
            // 标记本轮是否有交换,优化已排序情况
            boolean swapped = false;
            for (int j = 0; j < n - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    // 交换元素
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    swapped = true;
                }
            }
            // 如果没有交换,说明数组已排序,提前退出
            if (!swapped) break;
        }
    }
}
  • 时间复杂度:O(n²),最好情况O(n)(已排序时)
  • 空间复杂度:O(1)
  • 稳定性:稳定

2. 选择排序(Selection Sort)

原理:每次从剩余元素中找到最小值,放到已排序部分的末尾

public class SelectionSort {
    public static void sort(int[] arr) {
        int n = arr.length;
        for (int i = 0; i < n - 1; i++) {
            // 找到最小元素的索引
            int minIndex = i;
            for (int j = i + 1; j < n; j++) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }
            // 交换最小元素与当前位置元素
            int temp = arr[minIndex];
            arr[minIndex] = arr[i];
            arr[i] = temp;
        }
    }
}
  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

3. 插入排序(Insertion Sort)

原理:构建有序序列,将未排序元素插入到已排序序列的合适位置

public class InsertionSort {
    public static void sort(int[] arr) {
        int n = arr.length;
        for (int i = 1; i < n; i++) {
            int key = arr[i];//当前要插入的元素(未排序部分的第一个元素)
            int j = i - 1;//j 从已排序部分的最后一个元素开始向前移动
            
            // 将已排序的部分中大于key的元素向后移动
            while (j >= 0 && arr[j] > key) {
                arr[j + 1] = arr[j];
                j--;
            }
            arr[j + 1] = key;//将 key 插入到找到的合适位置(j+1)
        }
    }
}
  • 时间复杂度:O(n²),最好情况O(n)(已排序时)
  • 空间复杂度:O(1)
  • 稳定性:稳定

4. 希尔排序(Shell Sort)

原理:插入排序的改进版,通过分组进行排序,逐步减小间隔

public class ShellSort {
    public static void sort(int[] arr) {
        int n = arr.length;
        
        // 初始间隔设为n/2,逐渐减小
        for (int gap = n / 2; gap > 0; gap /= 2) {
            // 对每个间隔组进行插入排序
            for (int i = gap; i < n; i++) {
                int temp = arr[i];// 保存当前要插入的元素
                int j;
                // 移动同组中之前的元素
                for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
                    arr[j] = arr[j - gap];
                }
                arr[j] = temp;
            }
        }
    }
}
  • 时间复杂度:O(n log n) ~ O(n²)(取决于间隔序列)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

5. 归并排序(Merge Sort)

原理:分治法,将数组分成两半分别排序,再合并结果

public class MergeSort {
    public static void sort(int[] arr) {
        if (arr.length > 1) {
            int mid = arr.length / 2;
            
            // 分成两个子数组
            int[] left = new int[mid];
            int[] right = new int[arr.length - mid];
            
            System.arraycopy(arr, 0, left, 0, mid);
            System.arraycopy(arr, mid, right, 0, arr.length - mid);
            
            // 递归排序子数组
            sort(left);
            sort(right);
            
            // 合并排序后的子数组
            merge(arr, left, right);
        }
    }
    
    private static void merge(int[] result, int[] left, int[] right) {
        int i = 0, j = 0, k = 0;
        
        // 合并两个有序数组
        while (i < left.length && j < right.length) {
            if (left[i] <= right[j]) {
                result[k++] = left[i++];
            } else {
                result[k++] = right[j++];
            }
        }
        
        // 复制剩余元素
        while (i < left.length) {
            result[k++] = left[i++];
        }
        while (j < right.length) {
            result[k++] = right[j++];
        }
    }
}
  • 时间复杂度:O(n log n)
  • 空间复杂度:O(n)
  • 稳定性:稳定

6. 快速排序(Quick Sort)

原理:选择基准元素,将数组分为小于和大于基准的两部分,递归排序

public class QuickSort {
	private void swap(int[] arr, int i, int j) {
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
	
	public void quickSort(int[] arr, int start, int end) {
		if (start >= end)
			return;
		int k = arr[start];
		int i = start, j = end;
		while (i != j) {
			while (i < j && arr[j] >= k)
				--j;
			swap(arr, i, j);
			while (i < j && arr[i] <= k)
				++i;
			swap(arr, i, j);
		}
		quickSort(arr, start, i - 1);
		quickSort(arr, i + 1, end);
	}
	
	public static void main(String[] args) {
		int[] arr = {5, 2, 6, 9, 1, 3, 4, 8, 7, 10};
		new QuickSort().quickSort(arr, 0, arr.length - 1);
		System.out.println(Arrays.toString(arr));
	}
}
  • 时间复杂度:O(n log n),最坏情况O(n²)
  • 空间复杂度:O(log n) ~ O(n)(递归栈)
  • 稳定性:不稳定

7. 堆排序(Heap Sort)

原理:利用堆数据结构,先构建最大堆,再依次提取最大值

public class HeapSort {
    public static void sort(int[] arr) {
        int n = arr.length;
        
        // 构建最大堆
        for (int i = n / 2 - 1; i >= 0; i--) {
            heapify(arr, n, i);
        }
        
        // 从堆中提取元素
        for (int i = n - 1; i > 0; i--) {
            // 将根节点(最大值)与当前最后一个元素交换
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            
            // 对剩余元素重建堆
            heapify(arr, i, 0);
        }
    }
    
    // 维护堆的性质
    private static void heapify(int[] arr, int n, int i) {
        int largest = i; // 根节点
        int left = 2 * i + 1; // 左子节点
        int right = 2 * i + 2; // 右子节点
        
        // 如果左子节点大于根节点
        if (left < n && arr[left] > arr[largest]) {
            largest = left;
        }
        
        // 如果右子节点大于当前最大值
        if (right < n && arr[right] > arr[largest]) {
            largest = right;
        }
        
        // 如果最大值不是根节点
        if (largest != i) {
            int swap = arr[i];
            arr[i] = arr[largest];
            arr[largest] = swap;
            
            // 递归维护受影响的子树
            heapify(arr, n, largest);
        }
    }
}
  • 时间复杂度:O(n log n)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

8. 计数排序(Counting Sort)

原理:基于整数范围,统计每个元素出现次数,再重建有序数组

public class CountingSort {
    public static void sort(int[] arr) {
        if (arr.length == 0) return;
        
        // 找到数组中的最大值和最小值
        int min = arr[0], max = arr[0];
        for (int num : arr) {
            if (num < min) min = num;
            if (num > max) max = num;
        }
        
        // 创建计数数组
        int range = max - min + 1;
        int[] count = new int[range];
        int[] output = new int[arr.length];
        
        // 统计每个元素出现的次数
        for (int num : arr) {
            count[num - min]++;
        }
        
        // 计算累积计数
        for (int i = 1; i < count.length; i++) {
            count[i] += count[i - 1];
        }
        
        // 构建输出数组
        for (int i = arr.length - 1; i >= 0; i--) {
            output[count[arr[i] - min] - 1] = arr[i];
            count[arr[i] - min]--;
        }
        
        // 复制结果到原数组
        System.arraycopy(output, 0, arr, 0, arr.length);
    }
}
  • 时间复杂度:O(n + k),k是数据范围
  • 空间复杂度:O(n + k)
  • 稳定性:稳定

总结:排序算法选择建议

  1. 小规模数据或接近有序:插入排序
  2. 中等规模数据:快速排序或归并排序
  3. 要求稳定排序:归并排序、冒泡排序、插入排序、计数排序
  4. 内存受限场景:堆排序、希尔排序等原地排序算法
  5. 整数排序且范围不大:计数排序

每种排序算法都有其适用场景,实际应用中需根据数据规模、数据特性和性能要求选择合适的算法。

posted @ 2025-09-19 14:10  Filament  阅读(9)  评论(0)    收藏  举报
返回顶端