2026/2/18 22:39:13
网站建设
项目流程
影视网站源码建设,网站的倒计时怎么做,网上营销手段,大数据营销获客本篇将详细讲解插入排序、希尔排序和堆排序三种经典排序算法#xff0c;包括算法原理、执行过程、易错点分析#xff0c;并为每种算法提供三道例题及详细解析。 一、插入排序#xff08;Insertion Sort#xff09;
算法原理
插入排序的核心思想是将待排序数组分为已排序和…本篇将详细讲解插入排序、希尔排序和堆排序三种经典排序算法包括算法原理、执行过程、易错点分析并为每种算法提供三道例题及详细解析。一、插入排序Insertion Sort算法原理插入排序的核心思想是将待排序数组分为已排序和未排序两部分。初始时已排序部分仅包含第一个元素。随后从未排序部分取出一个元素在已排序部分从后向前扫描找到合适的位置插入该元素(比取出元素大或小时)直到所有元素有序。时间复杂度最优O(n)数组已有序最差O(n^2)数组逆序平均O(n^2)。空间复杂度$O(1)原地排序。执行过程从第二个元素开始遍历索引i 1。将当前元素arr[i]暂存为key。从j i - 1向前扫描已排序部分若arr[j] key则将arr[j]后移一位。否则跳出循环。将key插入到正确位置。易错点边界条件循环索引从1开始内层循环终止条件为j 0。元素移动需用while而非for循环确保及时终止扫描。稳定性遇到相等元素时不移动保证稳定性。例题1对整数数组升序排序public void insertionSort(int[] arr) { for (int i 1; i arr.length; 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; } }解析经典实现注意内层循环需同时判断j 0和arr[j] key。例题2对链表排序使用插入排序public ListNode insertionSortList(ListNode head) { ListNode dummy new ListNode(0); ListNode cur head; while (cur ! null) { ListNode prev dummy; while (prev.next ! null prev.next.val cur.val) { prev prev.next; } ListNode next cur.next; cur.next prev.next; prev.next cur; cur next; } return dummy.next; }解析链表需使用虚拟头节点dummy简化插入操作注意断开原节点的链接。例题3对浮点数数组降序排序public void insertionSortDesc(double[] arr) { for (int i 1; i arr.length; i) { double key arr[i]; int j i - 1; while (j 0 arr[j] key) { // 降序将 改为 arr[j 1] arr[j]; j--; } arr[j 1] key; } }解析仅需将内层循环条件改为arr[j] key即可实现降序。二、希尔排序Shell Sort算法原理希尔排序是插入排序的改进版通过分组插入提升效率。设定一个增量序列如$n/2, n/4, \dots, 1$对每个增量间隔的分组进行插入排序最后增量减至1时整体排序。时间复杂度约O(n^1.3)依赖增量序列。空间复杂度O(1)。执行过程选择初始增量gap n/2。对每个增量分组执行插入排序。缩小增量如gap / 2重复步骤2直到gap 1。易错点增量序列常用Knuth序列h 3h 1而非简单折半。分组方式每个分组是间隔gap的子序列而非连续子数组。终止条件增量需覆盖到gap 1。例题1整数数组升序排序Knuth增量public void shellSort(int[] arr) { int n arr.length; int gap 1; while (gap n / 3) gap 3 * gap 1; // Knuth序列1, 4, 13, ... while (gap 1) { for (int i gap; i n; i) { int key arr[i]; int j i; while (j gap arr[j - gap] key) { // 注意索引 j-gap arr[j] arr[j - gap]; j - gap; } arr[j] key; } gap / 3; // 缩小增量 } }解析使用Knuth序列优化效率内层循环索引需以gap为步长移动。例题2字符串按长度排序public void shellSortStrings(String[] arr) { int n arr.length; int gap 1; while (gap n / 3) gap 3 * gap 1; while (gap 1) { for (int i gap; i n; i) { String key arr[i]; int j i; while (j gap arr[j - gap].length() key.length()) { arr[j] arr[j - gap]; j - gap; } arr[j] key; } gap / 3; } }解析比较条件改为字符串长度注意稳定性不受影响。例题3自定义对象按字段排序class Person { String name; int age; } public void shellSortPersons(Person[] arr) { int n arr.length; int gap 1; while (gap n / 3) gap 3 * gap 1; while (gap 1) { for (int i gap; i n; i) { Person key arr[i]; int j i; while (j gap arr[j - gap].age key.age) { arr[j] arr[j - gap]; j - gap; } arr[j] key; } gap / 3; } }解析对自定义对象按age字段排序需确保比较逻辑正确。三、堆排序Heap Sort算法原理堆排序基于二叉堆数据结构建堆将无序数组构建成最大堆父节点值 ≥ 子节点。排序交换堆顶最大值与末尾元素缩小堆范围重新调整堆结构。时间复杂度建堆O(n)排序O(n log n)总O(n \log n)。空间复杂度O(1)。执行过程建堆从最后一个非叶子节点索引n/2 - 1开始自底向上执行heapify。排序交换堆顶与末尾元素。堆大小减1对堆顶执行heapify。重复直到堆大小为1。易错点索引计算父子节点索引关系为left 2*i 1right 2*i 2。堆调整范围每次交换后堆大小减1调整范围需随之缩小。堆调整方向heapify需从根节点向下递归调整。例题1整数数组升序排序public void heapSort(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--) { swap(arr, 0, i); heapify(arr, i, 0); // 调整剩余堆 } } private 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) { swap(arr, i, largest); heapify(arr, n, largest); // 递归调整子树 } }解析注意建堆时需从n/2 - 1开始倒序调整。例题2数组降序排序最小堆public void heapSortDesc(int[] arr) { int n arr.length; // 建最小堆将比较改为 for (int i n / 2 - 1; i 0; i--) { heapifyMin(arr, n, i); } // 排序逻辑相同 for (int i n - 1; i 0; i--) { swap(arr, 0, i); heapifyMin(arr, i, 0); } } private void heapifyMin(int[] arr, int n, int i) { int smallest i; int left 2 * i 1; int right 2 * i 2; if (left n arr[left] arr[smallest]) smallest left; if (right n arr[right] arr[smallest]) smallest right; if (smallest ! i) { swap(arr, i, smallest); heapifyMin(arr, n, smallest); } }解析将heapify中的比较改为即可实现最小堆从而得到降序序列。例题3对二维数组按行首元素排序public void heapSort2D(int[][] matrix) { int n matrix.length; // 建堆按每行第一个元素 for (int i n / 2 - 1; i 0; i--) { heapify2D(matrix, n, i); } for (int i n - 1; i 0; i--) { swapRows(matrix, 0, i); heapify2D(matrix, i, 0); } } private void heapify2D(int[][] matrix, int n, int i) { int largest i; int left 2 * i 1; int right 2 * i 2; if (left n matrix[left][0] matrix[largest][0]) largest left; if (right n matrix[right][0] matrix[largest][0]) largest right; if (largest ! i) { swapRows(matrix, i, largest); heapify2D(matrix, n, largest); } }解析比较逻辑改为matrix[i][0]交换时需整行交换swapRows方法需自定义。总结插入排序小规模数据高效实现简单但$O(n^2)$复杂度。希尔排序通过分组插入提升效率增量序列影响性能。堆排序O(n log n)复杂度且原地排序适合大规模数据。三种排序各有适用场景理解其原理及易错点对编写正确代码至关重要。