/**
* 排序算法学习之希尔排序
* 实现方法:
* 希尔排序是特殊的插入排序
* 插入排序按照处理的子数组长度递增分为多趟插入操作
* 而每一趟插入操作处理的子数组元素索引间隔都是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--);
}
}