希尔排序
希尔排序的基本思想是:把待排序的数据元素分成若干个小组,对同一小组内的数据元素用直接插入法排序;小组的个数逐次缩小;当完成了所有数据元素都在一个组内的排序后排序过程结束。希尔排序又称作缩小增量排序。
选择步长,对每个小的分组进行的直接插入排序。
/**
* 希尔排序:我们选择步长为:15,7,3,1 我们选择步长公式为:2^k-1,2^(k-1)-1,……,15,7,3,1
* (2^4-1,2^3-1,2^2-1,2^1-1) 注意所有排序都是从小到大排。
*/
public class ShellSort {
/**
* 排序算法的实现,对数组中指定的元素进行排序
*
* @param array
* 待排序的数组
* @param from
* 从哪里开始排序
* @param end
* 排到哪里
* @param c
* 比较器
*/
public void sort(Integer[] array, int from, int end) {
// 初始步长,实质为每轮的分组数
int step = initialStep(end - from + 1);
// 第一层循环是对排序轮次进行循环。(step + 1) / 2 - 1 为下一轮步长值
for (; step >= 1; step = (step + 1) / 2 - 1) {
// 对每轮里的每个分组进行循环
for (int groupIndex = 0; groupIndex < step; groupIndex++) {
// 对每组进行直接插入排序
insertSort(array, groupIndex, step, end);
}
}
}
/**
* 直接插入排序实现
*
* @param array
* 待排序数组
* @param groupIndex
* 对每轮的哪一组进行排序
* @param step
* 步长
* @param end
* 整个数组要排哪个元素止
* @param c
* 比较器
*/
public void insertSort(Integer[] array, int groupIndex, int step, int end) {
int startIndex = groupIndex;// 从哪里开始排序
int endIndex = startIndex;// 排到哪里
/*
* 排到哪里需要计算得到,从开始排序元素开始,以step步长,可求得下元素是否在数组范围内,
* 如果在数组范围内,则继续循环,直到索引超现数组范围
*/
while ((endIndex + step) <= end) {
endIndex += step;
}
// i为每小组里的第二个元素开始
for (int i = groupIndex + step; i <= end; i += step) {
for (int j = groupIndex; j < i; j += step) {
Integer insertedElem = array[i];
// 从有序数组中最一个元素开始查找第一个大于待插入的元素
if ((array[j].compareTo(insertedElem)) >= 0) {
// 找到插入点后,从插入点开始向后所有元素后移一位
move(array, j, i - step, step);
array[j] = insertedElem;
break;
}
}
}
}
/**
* 根据数组长度求初始步长
*
* 我们选择步长的公式为:2^k-1,2^(k-1)-1,...,15,7,3,1 ,其中2^k 减一即为该步长序列,k 为排序轮次
*
* 初始步长:step = 2^k-1 初始步长约束条件:step < len - 1 初始步长的值要小于数组长度还要减一的值(因
* 为第一轮分组时尽量不要分为一组,除非数组本身的长度就小于等于4)
*
* 由上面两个关系试可以得知:2^k - 1 < len - 1 关系式,其中k为轮次,如果把 2^k 表 达式 转换成 step 表达式,则
* 2^k-1 可使用 (step + 1)*2-1 替换(因为 step+1 相当于第k-1 轮的步长,所以在 step+1 基础上乘以 2
* 就相当于 2^k 了),即步长与数组长度的关系不等式为 (step + 1)*2 - 1 < len -1
*
* @param len
* 数组长度
* @return
*/
public static int initialStep(int len) {
/*
* 初始值设置为步长公式中的最小步长,从最小步长推导出最长初始步长值,即按照以下公式来推:
* 1,3,7,15,...,2^(k-1)-1,2^k-1
* 如果数组长度小于等于4时,步长为1,即长度小于等于4的数组不用分组,此时直接退化为直接插入排序
*/
int step = 1;
// 试探下一个步长是否满足条件,如果满足条件,则步长置为下一步长
while ((step + 1) * 2 - 1 < len - 1) {
step = (step + 1) * 2 - 1;
}
System.out.println("初始步长 : " + step);
return step;
}
/**
* 以指定的步长将数组元素后移,步长指定每个元素间的间隔
*
* @param array
* 待排序数组
* @param startIndex
* 从哪里开始移
* @param endIndex
* 到哪个元素止
* @param step
* 步长
*/
protected final void move(Integer[] array, int startIndex, int endIndex,
int step) {
for (int i = endIndex; i >= startIndex; i -= step) {
array[i + step] = array[i];
}
}
/**
* 测试
*
* @param args
*/
public static void main(String[] args) {
Integer[] intgArr = { 65, 34, 25, 87, 12, 38, 56, 46, 14, 77, 92, 23 };
ShellSort shellSort = new ShellSort();
shellSort.sort(intgArr, 0, intgArr.length - 1);
for (Integer intObj : intgArr) {
System.out.print(intObj + " ");
}
}
}
快速排序
快速排序的基本思想:通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分关键字小,则分别对这两部分继续进行排序,直到整个序列有序。把整个序列看做一个数组,把第零个位置看做中轴,和最后一个比,如果比它小交换,比它大不做任何处理;交换了以后再和小的那端比,比它小不交换,比他大交换。这样循环往复,一趟排序完成,左边就是比中轴小的,右边就是比中轴大的,然后再用分治法,分别对这两个独立的数组进行排序。
是一种快速的交换排序
自己写出的:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
int[] array = {49,38,65,97,76,13,27,33,25,78,1,10,23,34,97};
int start = 0;
int end = array.length-1;
quickSort(array,start,end);
System.out.println(Arrays.toString(array));
}
public static void quickSort(int[] array , int head ,int bottom)
{
if(head==bottom)
return;
int start = head;
int end = bottom;
int pivot = array[start];
while(start<end)
{
if(array[start]==pivot)
{
for(;end>start;end--)
{
if(array[end]<array[start])
{
int temp = array[end];
array[end] = array[start];
array[start] = temp;
break;
}
}
}else {
for(;start<end;start++)
{
if(array[end]<array[start])
{
int temp = array[end];
array[end] = array[start];
array[start] = temp;
break;
}
}
}
}
int middle = start;
System.out.println("one lun: " + pivot + "..." + end);
if(head<middle)
{
quickSort(array, head, middle-1);
}
if(bottom>middle)
{
quickSort(array, middle+1, bottom);
}
}
}
参考的:
import java.util.Arrays;
//快速排序算法
public class QuickSort {
public static void quickSort(int arr[], int low, int high)
{
if(low < high)
{
//按照第一个数进行切分。左边放小于【0】的数,右边放大于[0]的数
int mid = partition(arr, low, high);
//对关键字左边的数据进行分区
quickSort(arr, low, mid - 1);
//对关键字右边的数据进行分区
quickSort(arr, mid + 1, high);
}
}
public static int partition(int array[], int low, int high) {
// 设置两个变量i、j,排序开始的时候i:=0,j:=N-1;
// 以第一个数组元素作为关键数据,赋值给pivotKey,即pivotKey:=array[0];
int pivotKey = array[low];
int i= low, j = high;
if(low < high)
{
while (i < j)
{
// 从J开始向前搜索,即由后开始向前搜索(J:=J-1),找到第一个小于X的值,两者交换;
while (i < j && array[j] >= pivotKey)
{
j--;
}
if (i < j)
{
array[i] = array[j];
i++;
}
// 从I开始向后搜索,即由前开始向后搜索(I:=I+1),找到第一个大于X的值,两者交换;
while (i < j && array[i] <= pivotKey)
{
i++;
}
if (i < j)
{
array[j] = array[i];
j--;
}
}
array[i] = pivotKey;
//打印每次分区后的结果
System.out.println("每次排序:" + Arrays.toString(array));
//将这个分区结束时的坐标i返回,用于下次执行时当做前分区的尾坐标,当做后分区的头坐标
}
return i;
}
public static void main(String[] args)
{
int[] list={49,38,65,97,76,13,27,49};
System.out.println("排序前:" +Arrays.toString(list));
quickSort(list, 0, list.length - 1);
System.out.println("排序后:" + Arrays.toString(list));
}
}
堆排序
在直接选择排序中,放在数组中的n个数据元素排成一个线性序列(即线性结构),要从有n个数据元素的数组中选择出一个最小的数据元素需要比较n-1次。如果能把待排序的数据元素集合构成一个完全二叉树结构,则每次选择出一个最大(或最小)的数据元素只需比较完全二叉树的高度次,即log2n次,则排序算法的时间复杂度就是O(nlog2n)。这就是堆排序的基本思想。
最大堆的定义如下:
设数组a中存放了n个数据元素,数组下标从0开始,如果当数组下标2i+1<n时有:a[i]≥a[2i+1];如果当数组下标2i+2<n时有:a[i]≥a[2i+2],则这样的数据结构称为最大堆。
图说明见附件
//堆排序算法
public class HeapSort {
//带入数组,n是最大长度额下标,h是起点
//因为初始化下已经是最大堆,头和尾交换后,只需把新的头加入到树的最小端即可,树还是保持最大堆
public static void createHeap(int[] a, int n, int h)
{
int i, j, flag;
int temp;
i = h; // i为要建堆的二叉树根结点下标
j = 2 * i + 1; // j为i结点的左孩子结点的下标
temp = a[i];
flag = 0;
// 沿左右孩子中值较大者重复向下筛选
while (j < n && flag != 1) {
// 寻找左右孩子结点中的较大者,j为其下标
if (j < n - 1 && a[j] < a[j + 1])
{
j++;
}
// 标记结束筛选条件
if (temp > a[j])
flag = 1;
else {
// 否则把a[j]上移
a[i] = a[j];
i = j;
j = 2 * i + 1;
}
}
a[i] = temp; // 把最初的a[i]赋予最后的a[j]
}
//初始化数组,使其对应的二叉树是最大堆,初始化下二叉树是最大堆的情况
public static void initCreateHeap(int[] a) {
int n = a.length;
for (int i = (n - 1) / 2; i >= 0; i--)
createHeap(a, n, i);
}
public static void heapSort(int[] a) {
int temp;
int n = a.length;
initCreateHeap(a); // 初始化创建最大堆
for (int i = n - 1; i > 0; i--) {
// 当前最大堆个数每次递减1
// 把堆顶a[0]元素和当前最大堆的最后一个元素交换
temp = a[0];
a[0] = a[i];
a[i] = temp;
//交换后,把交换的头在向下找最大的,换上去,形成最大堆
createHeap(a, i, 0); // 调整根结点满足最大堆
}
}
public static void main(String[] args) {
int[] test = { 10, 50, 32, 5, 76, 9, 40, 88 };
int n = test.length;
heapSort(test);
for (int i = 0; i < n; i++)
System.out.print(test[i] + " ");
}
}
这是一种基于二叉树的排序。
归并排序
//归并排序算法
public class MergeSort {
//归并排序算法
public static void merge(int[] a, int[] swap, int k) {
int n = a.length;
int m = 0, u1, l2, i, j, u2;
int l1 = 0; // 第一个有序子数组下界为0
//l1为第一组最小,u1为第一组最大,l2为第二组最小,u2为第二组最大
while (l1 + k <= n - 1) {
l2 = l1 + k; // 计算第二个有序子数组下界
u1 = l2 - 1; // 计算第一个有序子数组上界
u2 = (l2 + k - 1 <= n - 1) ? l2 + k - 1 : n - 1; // 计算第二个有序子数组上界
//循环找最小的插入到swap中
for (i = l1, j = l2; i <= u1 && j <= u2; m++) {
if (a[i] <= a[j]) {
swap[m] = a[i];
i++;
} else {
swap[m] = a[j];
j++;
}
}
// 子数组2已归并完,将子数组1中剩余的元素存放到数组swap中
while (i <= u1) {
swap[m] = a[i];
m++;
i++;
}
// 子数组1已归并完,将子数组2中剩余的元素存放到数组swap中
while (j <= u2) {
swap[m] = a[j];
m++;
j++;
}
l1 = u2 + 1;
}
// 将原始数组中只够一组的数据元素顺序存放到数组swap中
for (i = l1; i < n; i++, m++)
{
swap[m] = a[i];
}
}
public static void mergeSort(int[] a) {
int i;
int n = a.length;
int k = 1; // 归并长度从1开始
int[] swap = new int[n];
while (k < n) {
//k是每组的长度
merge(a, swap, k); // 调用函数merge()
for (i = 0; i < n; i++)
{
a[i] = swap[i]; // 将元素从临时数组swap放回数组a中
}
//每次长度增为2倍
k = 2 * k; // 归并长度加倍
}
}
public static void main(String[] args) {
int[] test = { 72, 73, 71, 23, 94, 16, 5, 68, 64 };
int n = test.length;
mergeSort(test);
for (int i = 0; i < n; i++)
System.out.print(test[i] + " ");
}
}
基数排序
//基数排序算法
public class RadixSort{
public static void radixSort(int[] a, int m, int d) throws Exception{
//a为要排序的数据元素,d为进制的基数,m为数据元素的最大位数
int n = a.length;
int i, j, k, l, power = 1;
//这里是指十个桶,存放10个
Queue<Integer>[] myQueue = new LinkedList[d];
//创建链式队列数组对象
for(i = 0; i < d; i++){
//这里对十个桶里,对每个桶做个链表
Queue<Integer> temp = new LinkedList<Integer>();
myQueue[i] = temp;
}
//进行m次排序
for(i = 0; i < m; i++){
//依次加位
if(i==0)
power = 1;
else
power = power * d;
//依次将n个数据元素按第k位的大小放到相应的队列中
for(j = 0; j < n; j++)
{
//对每个数,找对应的桶
k = a[j] / power - (a[j] / (power * d)) * d; //计算k值
//把对应的数放到桶中
myQueue[k].add(new Integer(a[j])); // a[j]入队列k
}
//顺序回收各队列中的数据元素到数组a中
l = 0;
for(j = 0; j < d; j++){
//把桶里面的数依次在放回到数组中
while(!myQueue[j].isEmpty()){
a[l] = ((Integer)myQueue[j].remove()).intValue();
l++;
}
}
}
}
public static void main(String[] args){
int[] test = {710,342,45,686,6,841,429,134,68,264};
int n = test.length;
int m = 3, d = 10;
try{
radixSort(test, m, d);
}
catch (Exception e){
e.printStackTrace();
}
for(int i = 0; i < n; i++)
System.out.print(test[i] + " ");
}
}
转载于:https://blog.51cto.com/wukong0716/1694440