/** * 排序算法学习之希尔排序 * 实现方法: * 希尔排序是特殊的插入排序 * 插入排序按照处理的子数组长度递增分为多趟插入操作 * 而每一趟插入操作处理的子数组元素索引间隔都是1(即步长) * 希尔排序每趟处理的子数组元素索引间隔是动态的。 */ package 排序算法.shellSort; public class ShellSort { /** * 对输入数组进行希尔排序 * @param a 待排序数组,索引有效范围从0开始 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static void shellSort(Comparable[] a) { int incr = a.length / 2;//初始步长 while (incr >= 1) { for (int i = incr; i < a.length; i++) { Comparable insertElement = a[i]; int j; for (j = i - incr; j >= 0 && insertElement.compareTo(a[j]) < 0; j -= incr) a[j + incr] = a[j]; a[j + incr] = insertElement; } if (incr == 2) incr = 1; else incr = (int) (incr / 2.2); } } }
/** * 排序算法学习之选择排序 * 实现方法: * 每次选择子数组中最大的元素依次排列在子数组末尾, * 子数组的长度依次递减。 */ package 排序算法.selectionSort; public class SelectionSort { @SuppressWarnings({ "rawtypes", "unchecked" }) /** * 查找指定长度的数组中最大元素的索引 * @param a 指定数组 * @param n 数组长度 * @return 最大元素的索引 */ public static int max(Comparable[] a, int n) { if (n < 0) throw new IllegalArgumentException("数组为空"); int positionOfCurrentMax = 0; for (int i = 1; i <= n; i++) if (a[positionOfCurrentMax].compareTo(a[i]) < 0) positionOfCurrentMax = i; return positionOfCurrentMax; } /** * 交换指定数组中指定索引的元素 * @param a 指定数组 * @param i 第一个索引 * @param j 第二个索引 */ public static void swap(Object[] a, int i, int j) { Object temp = a[i]; a[i] = a[j]; a[j] = temp; } /** * 对输入数组执行一般选择排序 * @param a 输入数组 */ @SuppressWarnings("rawtypes") public void selectionSort(Comparable[] a) { //按照子数组长度递减的顺序进行每趟的选择操作 for (int size = a.length; size > 1; size--) { int j = max(a, size - 1); swap(a, j, size - 1); } } /** * 对输入数组执行早结束版本的选择排序 * @param a 输入数组,数组索引有效范围从0开始 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public void selectionSort2(Comparable[] a) { boolean sorted = false;//当前子数组是否有序的标志 for (int size = a.length; !sorted && (size > 1); size--) { int pos = 0; sorted = true; for (int i = 1; i < size; i++) if (a[pos].compareTo(a[i]) <= 0) pos = i; else sorted = false; swap(a, pos, size - 1); } } }
/** * 秩排序实现2 * @author Sking */ package 排序算法.rankSort; public class RankSort2 { /** * 对指定数组执行秩排序 * @param a 待排序数组 */ @SuppressWarnings("rawtypes") private void rankSort2(Comparable[] a) { int[] r = new int[a.length]; rank(a, r);//求数组元素的秩 rearrange(a, r);//移动元素到索引为其秩的位置 } /** * 交换指定数组中的两个元素 * @param a 指定数组 * @param i 第一个元素的索引值 * @param j 第二个元素的索引值 */ @SuppressWarnings("rawtypes") private static void swap(Comparable[] a, int i, int j) { Comparable temp = a[i]; a[i] = a[j]; a[j] = temp; } /** * 交换秩数组中的两个元素 * @param a 指定的秩数组 * @param i 第一个元素的索引值 * @param j 第二个元素的索引值 */ private static void swap(int[] a, int i, int j)//重载交换方法 { int temp = a[i]; a[i] = a[j]; a[j] = temp; } /** * 根据秩数组对待排序数组进行排序 * @param a 待排序数组 * @param r 待排序数组的秩数组 */ @SuppressWarnings("rawtypes") private static void rearrange(Comparable[] a, int[] r) { for (int i = 0; i < a.length; i++) //依次检查位置i,如果有i=r[i]则表示元素i位置正确 //否则交换位置i和位置r[i]上的元素(r[i]指出索引i位置 //上的元素在排序数组中药位于r[i]的位置。 while (r[i] != i) { int t = r[i]; swap(a, i, t); swap(r, i, t); } } /** * 求指定数组元素的秩,存放在给定数组中 * @param a 待排序数组 * @param r 给定的存放元素秩的数组 */ @SuppressWarnings({ "rawtypes", "unchecked" }) private static void rank(Comparable[] a, int[] r) { if (r.length < a.length) throw new IllegalArgumentException("秩数组长度异常!"); for (int i = 0; i < a.length; i++) r[i] = 0; for (int i = 1; i < a.length; i++) for (int j = 0; j < i; j++) //当左边的元素小于等于右边的元素时,右边元素的秩加1 if (a[j].compareTo(a[i]) <= 0) r[i]++; else //当左边的元素不小于右边的元素时,左边的元素的秩加1 r[j]++; } }
/** * 排序算法学习之秩排序 *@author Sking 秩的定义: 一个序列中元素的秩被定义为序列中小于该元素的的元素 个数加上出现在其左边的相等元素的个数。则可知一个元素在排序序列 中的位置索引就是它们的秩。 实现方法: 1.计算待排序数组元素的秩 2.将数组元素移动到索引值为他们的秩的位置 */ package 排序算法.rankSort; public class RankSort1 { /** * 对指定数组执行秩排序 * @param a 待排序的数组 */ @SuppressWarnings("rawtypes") public void rankSort(Comparable[] a) { int[] r = new int[a.length]; rank(a, r);//求待排序数组元素的秩 rearrange(a, r);//移动元素到索引为他们秩的位置,实现排序 } /** * 使用数组元素的秩数组对数组进行排序 * @param a 待排序数组 * @param r 待排序数组元素的秩数组 */ @SuppressWarnings("rawtypes") private static void rearrange(Comparable[] a, int[] r) { Comparable[] u = new Comparable[a.length]; for (int i = 0; i < a.length; i++) //移动元素到索引为他们秩的位置,实现排序 u[r[i]] = a[i]; for (int i = 0; i < a.length; i++) a[i] = u[i]; } /** * 求指定数组元素的秩,存放在给定数组中 * @param a 待排序数组 * @param r 给定的存放元素秩的数组 */ @SuppressWarnings({ "rawtypes", "unchecked" }) private static void rank(Comparable[] a, int[] r) { if (r.length < a.length) throw new IllegalArgumentException("秩数组长度异常!"); for (int i = 0; i < a.length; i++) r[i] = 0; for (int i = 1; i < a.length; i++) for (int j = 0; j < i; j++) //当左边的元素小于等于右边的元素时,右边元素的秩加1 if (a[j].compareTo(a[i]) <= 0) r[i]++; else //当左边的元素不小于右边的元素时,左边的元素的秩加1 r[j]++; } }
/** * 排序算法学习之快速排序的非递归实现,使用辅助栈 * @author Sking */ package 排序算法.quickSort; import 栈.ArrayStack; public class QuickSort2 { /** * 使用子数组的首个元素作为枢纽,划分子数组 划分后, * 枢纽左边的元素均小于枢纽,右边的元素均大于枢纽 * 并返回划分的位置索引,是快速排序的核心方法 * * @param a 待排序数组 * @param p 子数组的开始索引 * @param r 子数组的结束索引 * @return 划分的索引位置 */ @SuppressWarnings({ }) private int partition(int[] a, int p, int r) { int i = p, j = r + 1; int x = a[p];// 使用子数组的首个元素作为枢纽 while (true) { // 从左边开始找到大于枢纽的元素索引 while (a[++i] < x && i < r); // 同时从右边开始找到小于枢纽的元素索引 while (a[--j]> x); if (i >= j) break; // 交换大于枢纽的元素到右边,小于枢纽的元素导左边 swap(a, i, j); }// a[i]>=枢纽,a[j]<=枢纽,i>=j的时候表示已经划分完毕 a[p] = a[j];// 交换枢纽索引位置所在元素和枢纽 a[j] = x; return j;// j为枢纽索引 } /** * 交换指定数组中的两个元素 * @param a 指定数组 * @param i 第一个元素的索引 * @param j 第二个元素的索引 */ public static void swap(int[] a, int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } /** * 快速排序算法的非递归实现,使用辅助栈 * @param a 待排序的数组段 * @param p 数组段的左端索引 * @param r 数组段的右端索引 */ @SuppressWarnings({ "unused" }) private void qSort(int[] a, int p, int r) { ArrayStack stack = new ArrayStack();// 栈 if (p < r) { int mid = partition(a, p, r); if (p < mid - 1) { stack.push(p); stack.push(mid - 1); } if (mid + 1 < r) { stack.push(mid + 1); stack.push(r); } while (!stack.empty()) { int m = (int) stack.pop(); int n = (int) stack.pop(); int l = partition(a, n, m); if (n < l - 1) { stack.push(n); stack.push(l - 1); } if (l + 1 < m) { stack.push(l + 1); stack.push(m); } } } } }
/** * 排序算法学习之快速排序 * @author Sking 实现方法: 通过把一个数组递归的划分为两个子数组。 递归的基本步骤: 1. 把数组划分成以一个元素为枢纽的左右两个子数组。 2. 调用自身的左边和右边以步骤1递归。 性能障碍: 对枢纽数据的选择是影响排序的效率。 性能: 最坏时间复杂度:O(n^2) 平均时间复杂度:O(n*log(n)) 辅助空间:O(n)或O(log(n)) 稳定性:不稳定 时间复杂度是O(n*log(n)) */ package 排序算法.quickSort; public class QuickSort { @SuppressWarnings("rawtypes") /** * 递归划分子数组a[p....r] * @param a 指定数组,索引有效范围从0开始 * @param p 子数组的开始索引 * @param r 子数组的结束索引 */ private void qSort(Comparable[] a,int p, int r) { if (p < r) { int q = partition(a,p, r);//划分的位置索引 qSort(a,p, q - 1);//递归划分 qSort(a,q + 1, r); } } /** * 使用子数组的首个元素作为枢纽,划分子数组 * 划分后,枢纽左边的元素均小于枢纽,右边的元素均大于枢纽 * 并返回划分的位置索引,是快速排序的核心方法 * @param a 待排序数组 * @param p 子数组的开始索引 * @param r 子数组的结束索引 * @return 划分的索引位置 */ @SuppressWarnings({ "rawtypes", "unchecked" }) private int partition(Comparable[] a,int p, int r) { int i = p, j = r + 1; Comparable x = a[p];//使用子数组的首个元素作为枢纽 while (true) { //从左边开始找到大于枢纽的元素索引 while (a[++i].compareTo(x) < 0 && i < r); //同时从右边开始找到小于枢纽的元素索引 while (a[--j].compareTo(x) > 0); if (i >= j) break; //交换大于枢纽的元素到右边,小于枢纽的元素导左边 swap(a,i, j); }//a[i]>=枢纽,a[j]<=枢纽,i>=j的时候表示已经划分完毕 a[p] = a[j];//交换枢纽索引位置所在元素和枢纽 a[j] = x; return j;//j为枢纽索引 } /** * 交换指定数组中的两个元素 * @param a 指定数组 * @param i 第一个元素的索引 * @param j 第二个元素的索引 */ @SuppressWarnings("rawtypes") public static void swap(Comparable[] a,int i, int j) { Comparable temp = a[i]; a[i] = a[j]; a[j] = temp; } }
/** * 排序算法学习之一般合并排序 * @author Sking 实现方法: 将待排序数组分为大小大致相等的2个子数组 分别对两个子数组进行排序,如此递归划分排序 最后将排序号的子数组再递归合并为更大的已排序数组 直到整个数组已排序为止。 */ package 排序算法.mergeSort; public class MergeSort2 { /** * 对指定数组的指定范围执行一般合并排序 * * @param a * 指定数组 * @param left * 指定范围的左边索引 * @param right * 指定范围的右边索引 */ @SuppressWarnings("rawtypes") public void mergeSort2(Comparable[] a, int left, int right) { Comparable[] b = new Comparable[a.length]; if (left < right) { int i = (left + right) / 2; mergeSort2(a, left, i);//左子段递归排序 mergeSort2(a, i + 1, right);//右子段递归排序 merge(a, b, left, i, right);//合并子段到新数组中 copy(a, b, left, right);//复制排序后的元素到原数组 } } /** * 合并已排序子数组c[l...m]和c[m+1,r]到新数组中,新数组保持有序 * * @param c存放两个已排序数组的数组 * @param d * 新数组,用于存放合并后的数组 * @param l * 左边子数组的起始索引 * @param m * 左边子数组的结束索引 * @param r * 右边子数组的结束索引 */ @SuppressWarnings({ "rawtypes", "unchecked" }) private static void merge(Comparable[] c, Comparable[] d, int l, int m, int r) { int i = l; int j = m + 1; int k = l; while ((i <= m) && (j <= r)) if (c[i].compareTo(c[j]) <= 0) d[k++] = c[i++]; else d[k++] = c[j++]; if (i > m) for (int q = j; q <= r; q++) d[k++] = c[q]; else for (int q = i; q <= m; q++) d[k++] = c[q]; } /** * 复制源数组中指定索引范围内的元素到目标数组 * * @param a * 源数组 * @param b * 目标数组 * @param left * 指定范围的左边索引 * @param right * 指定范围的右边索引 */ @SuppressWarnings("rawtypes") private static void copy(Comparable[] a, Comparable[] b, int left, int right) { int i = left; int j = left; while (i <= right) a[i++] = b[j++]; } }
/** * 排序算法学习之合并排序 * @author Sking 实现方法: 将待排序数组中相邻元素两两配对作为子数组,排序各个子数组, 构成n/2组长度为2的排序好的子数组;然后将长度为2的子排序 子数组再两两配对,并排序,构成长度为4的已排序子数组。如此递归 直到整个数组是已排序为止。 最坏时间复杂度:O(n*log(n)) 平均时间复杂度:O(n*log(n)) 辅助空间:O(n) 稳定性:稳定 */ package 排序算法.mergeSort; public class MergeSort { @SuppressWarnings("rawtypes") /** * 对输入数组执行合并排序 * @param a 指定数组,索引有效位置从0开始 */ public void mergeSort(Comparable[] a) { Comparable[] b = new Comparable[a.length];// 辅助空间 int s = 1;// 初始子数组长度设置为1 while (s < a.length) { mergePass(a, b, s); s += s; mergePass(b, a, s); s += s; } } /** * 合并相邻的已排序子数组为更大的已排序子数组 * * @param x * 包含了已排序子数组的数组 * @param y * 包含更大的已排序子数组的数组 * @param s * 已排序子数组的长度 */ @SuppressWarnings("rawtypes") public static void mergePass(Comparable[] x, Comparable[] y, int s) { int i = 0; while (i <= x.length - 2 * s) {// 合并两相邻的长度为s的已排序子数组 merge(x, y, i, i + s - 1, i + 2 * s - 1); i = i + 2 * s; }// 当i>x.length-2*s的时候退出while循环 // 处理剩下的部分,可以是s长度的已排序数组+“零片” // 也可能只是“零片“,此时不需要合并。 if (i + s < x.length) { // 合并相邻的长度s的已排序子数组和长度小于s的“零片” merge(x, y, i, i + s - 1, x.length - 1); } else // 处理”零片“ for (int j = i; j < x.length; j++) y[j] = x[j]; } /** * 合并已排序子数组c[l...m]和c[m+1,r]到新数组中,新数组保持有序 * * @param c存放两个已排序数组的数组 * @param d * 新数组,用于存放合并后的数组 * @param l * 左边子数组的起始索引 * @param m * 左边子数组的结束索引 * @param r * 右边子数组的结束索引 */ @SuppressWarnings({ "unchecked", "rawtypes" }) static void merge(Comparable[] c, Comparable[] d, int l, int m, int r) { int i = l;// 第一个子数组的索引指针 int j = m + 1;// 第二个子数组的索引指针 int k = l;// 新数组的索引指针 while ((i <= m) && (j <= r)) if (c[i].compareTo(c[j]) <= 0) d[k++] = c[i++]; else d[k++] = c[j++]; if (i > m)// 将右边子数组剩下的元素添加到新数组中 for (int q = j; q <= r; q++) d[k++] = c[q]; else // 将左边子数组剩下的元素添加到新数组中 for (int q = i; q <= m; q++) d[k++] = c[q]; } }
/** * 排序算法学习之插入排序 * @author Sking */ package 排序算法.insertionSort; public class InsertionSort { @SuppressWarnings({ "rawtypes", "unchecked" }) /** * 一次元素插入的实现,在n长度的子数组中插入元素x到合适位置 * @param a 带排序数组,索引有效范围从0开始 * @param n 子数组元素个数 * @param x 待插入的元素 */ public static void insert(Comparable[] a, int n, Comparable x) { if (a.length < n + 1)//数组已满,不可插入 throw new IllegalArgumentException("数组不够大"); int i;//当前被比较元素索引 for (i = n - 1; i >= 0 && x.compareTo(a[i]) < 0; i--) a[i + 1] = a[i];//比x大的元素后移,寻找x插入的位置 a[i + 1] = x;//插入x } /** * 对输入数组执行插入排序 * @param a 待排序数组,索引有效位置从0开始 */ @SuppressWarnings("rawtypes") public static void insertionSort(Comparable[] a) { //i表示一次元素插入步骤所考虑的子数组长度,大小递增 for (int i = 1; i < a.length; i++) insert(a, i, a[i]); } }
/** * 排序算法学习之冒泡排序 * 使用时,将Comparable类型替换为指定类型,必要时,要定义比较方法 * @author Sking */ package 排序算法.bubbleSort; public class BubbleSort { /** * 交换数组元素方法 * @param a 指定数组 * @param i 第一个索引 * @param j 第二个索引 */ @SuppressWarnings("rawtypes") public static void swap(Comparable[] a, int i, int j) { Comparable t = a[i]; a[i] = a[j]; a[j] = t; } /** * 一次冒泡迭代,分别比较相邻元素选择单趟的最大元素 * @param a 指定数组 * @param n 冒泡子序列的长度 */ @SuppressWarnings({ "rawtypes", "unchecked" }) private static void bubble(Comparable[] a, int n) { for (int i = 0; i < n - 1; i++) if (a[i].compareTo(a[i + 1]) > 0) swap(a, i, i + 1); } /** * 一般冒泡排序 * @param a 指定数组 */ @SuppressWarnings("rawtypes") public static void bubbleSort(Comparable[] a) { for (int i = a.length; i > 1; i--) bubble(a, i); } /** * 早结束版本的冒泡排序方法的一次迭代 * @param a 指定数组 * @param n 冒泡子序列的长度 * @return 如果该次迭代中进行了元素交换位置,则返回true,否则返回false */ @SuppressWarnings({ "rawtypes", "unchecked" }) private static boolean haveBubble(Comparable[] a,int n){ boolean swapped=false;//标志一次冒泡是否进行交换 for(int i=0;i<n-1;i++) if(a[i].compareTo(a[i+1])>0){ swap(a,i,i+1); swapped=true; } return swapped; } /** * 早结束版本的冒泡排序 * @param a 指定排序的数组 */ @SuppressWarnings("rawtypes") public static void bubbleSort2(Comparable[] a){ for(int i=a.length;i>1&&haveBubble(a,i);i--); } }