您的位置:首页 > 编程语言 > Java开发

排序算法—Java实现

2017-10-07 20:18 330 查看

排序算法分类:

1)插入排序(直接插入排序、希尔排序)

2)交换排序(冒泡排序、快速排序)

3)选择排序(直接选择排序、堆排序)

4)归并排序

5)分配排序(基数排序)

所需辅助空间最多:归并排序

所需辅助空间最少:堆排序

平均速度最快:快速排序

不稳定:快速排序,希尔排序,堆排序。

1、冒泡排序

1.1 冒泡排序思想:

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。



1.2 冒泡算法实现

/**
* 冒泡排序
* 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
* 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
* 针对所有的元素重复以上的步骤,除了最后一个。
* 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
* @param numbers 需要排序的整型数组
*/
public static void bubbleSort(int[] numbers)
{
int temp = 0;
int size = numbers.length;
for(int i = 0 ; i < size-1; i ++)
{
for(int j = 0 ;j < size-1-i ; j++)
{
if(numbers[j] > numbers[j+1])  //交换两数位置
{
temp = numbers[j];
numbers[j] = numbers[j+1];
numbers[j+1] = temp;
}
}
}
}


2、选择排序

2.1 选择排序思想

在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止。


2.2 选择排序实现

/**
* 选择排序算法
* 在未排序序列中找到最小元素,存放到排序序列的起始位置
* 再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。
* 以此类推,直到所有元素均排序完毕。
* @param numbers
*/
public static void selectSort(int[] numbers)
{
int size = numbers.length; //数组长度
int temp = 0 ; //中间变量

for(int i = 0 ; i < size ; i++)
{
int k = i;   //待确定的位置
//选择出应该在第i个位置的数
for(int j = size -1 ; j > i ; j--)
{
if(numbers[j] < numbers[k])
{
k = j;
}
}
//交换两个数
temp = numbers[i];
numbers[i] = numbers[k];
numbers[k] = temp;
}
}


3、插入排序

3.1 插入排序思想

每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止。



3.2 插入排序实现

/**
* 插入排序
*
* 从第一个元素开始,该元素可以认为已经被排序
* 取出下一个元素,在已经排序的元素序列中从后向前扫描
* 如果该元素(已排序)大于新元素,将该元素移到下一位置
* 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
* 将新元素插入到该位置中
* 重复步骤2
* @param numbers  待排序数组
*/
public static void insertSort(int[] numbers)
{
int size = numbers.length;
int temp = 0 ;
int j =  0;

for(int i = 0 ; i < size ; i++)
{
temp = numbers[i];
//假如temp比前面的值小,则将前面的值后移
for(j = i ; j > 0 && temp < numbers[j-1] ; j --)
{
numbers[j] = numbers[j-1];
}
numbers[j] = temp;
}
}


4、快速排序

4.1 快排算法思想

通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分关键字小,则分别对这两部分继续进行排序,直到整个序列有序。

快速排序的示例:

第一趟:



全过程



把整个序列看做一个数组,把第零个位置看做中轴,和最后一个比,如果比它小交换,比它大不做任何处理;交换了以后再和小的那端比,比它小不交换,比他大交换。这样循环往复,一趟排序完成,左边就是比中轴小的,右边就是比中轴大的,然后再用分治法,分别对这两个独立的数组进行排序。

4.2 快排实现

/**
*
* @param numbers 带排序数组
* @param low  开始位置
* @param high 结束位置
*/
public static void quickSort(int[] numbers,int low,int high)
{
if(low < high)
{
//将numbers数组进行一分为二
  int middle = getMiddle(numbers,low,high);
   //对低字段表进行递归排序 
  quickSort(numbers, low, middle-1);
  //对高字段表进行递归排序
  quickSort(numbers, middle+1, high);
}
}
/**
* 查找出中轴(默认是最低位low)的在numbers数组排序后所在位置
*
* @param numbers 带查找数组
* @param low   开始位置
* @param high  结束位置
* @return  中轴所在位置
*/
public static int getMiddle(int[] numbers, int low,int high)
{
int temp = numbers[low]; //数组的第一个作为中轴
while(low < high)
{
while(low < high && numbers[high] > temp)
{
high--;
}
numbers[low] = numbers[high];//比中轴小的记录移到低端
while(low < high && numbers[low] < temp)
{
low++;
}
numbers[high] = numbers[low] ; //比中轴大的记录移到高端
}
numbers[low] = temp ; //中轴记录到尾
return low ; // 返回中轴的位置
}


5、堆排序

堆排序是一种树形选择排序,是对直接选择排序的有效改进。

若以一维数组存储一个堆,则堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的。如:

(a)大顶堆序列:(96, 83,27,38,11,09)

(b) 小顶堆序列:(12,36,24,85,47,30,53,91)



5.1 堆排序思想

初始时把要排序的n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元素。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。称这个过程为堆排序。

因此,实现堆排序需解决两个问题

1. 如何将n 个待排序的数建堆

2. 输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆

第一个问题 :建堆方法:

对初始序列建堆的过程,就是一个反复进行筛选的过程。

1)n 个结点的完全二叉树,则最后一个结点是第个结点的子树。

2)筛选从第个结点为根的子树开始,该子树成为堆。

3)之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。

如图建堆初始过程:无序序列:(49,38,65,97,76,13,27,49)



第二个问题:建新堆

输出堆顶元素后,对剩余n-1元素重新建成堆的调整过程。

调整小顶堆的方法:

1)设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶((最后一个元素与堆顶进行交换),堆被破坏,其原因仅是根结点不满足堆的性质。

2)将根结点与左、右子树中较小元素的进行交换。

3)若与左子树交换:如果左子树堆被破坏,即左子树的根结点不满足堆的性质,则重复方法 (2).

4)若与右子树交换,如果右子树堆被破坏,即右子树的根结点不满足堆的性质。则重复方法 (2).

5)继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。

称这个自根结点到叶子结点的调整过程为筛选。如图:



5.2 推排序实现

package com;

/*
* Java实现快速排序算法
* 由大到小排序
* author:wyr
* 2016-7-14
*两个步骤:1,建堆  2,对顶与堆的最后一个元素交换位置
*/
public class HeapSort {

public static void main(String[] args) {
int a[] = {3,1,5,7,2,4,9,6,10,8};
HeapSort  obj=new HeapSort();
System.out.println("初始值:");
obj.print(a);

for(int i=0;i<a.length;i++){
obj.createLittleHeap(a,a.length-1-i);
//创建堆,创建的是小顶堆。每次循环完,二叉树的根节点都是最小
//值,所以与此时的未排好部分最后一个值交换位置
//与最后一个值交换位置,最小值找到了位置
obj.print(a);
System.out.println();

}

System.out.println("\n排序后:");
obj.print(a);

}
/*
* 创建小顶堆:双亲节点小于子节点的值。从叶子节点开始,直到根节点。这 样建立的堆定位最小值
*/
private void createLittleHeap(int[] data, int last) {
for (int i = (last- 1) / 2; i >= 0; i--) {
//找到最后一个叶子节点的双亲节点
// 保存当前正在判断的节点
int parent = i;
// 若当前节点的左子节点存在,即子节点存在
while (2 * parent + 1 <= last) {
// biggerIndex总是记录较大节点的值,先赋值为当前判断节点的左子节点
//bigger指向左子节点
if (bigger < last) {
//说明存在右子节点

if (data[bigger] > data[bigger+ 1]) {       //右子节点>左子节点时

bigger=bigger+1;
}
}
if (data[parent] > data[bigger]) {
//若双亲节点值大于子节点中最大的
// 若当前节点值比子节点最大值小,则交换2者得值,交换后将biggerIndex值赋值给k
swap(data, parent, bigger);
parent = bigger;
} else {
break;
}
}
}
}
public void print(int a[]){
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
public  void swap(int[] data, int i, int j) {
if (i == j) {
return;
}
data[i] = data[i] + data[j];
data[j] = data[i] - data[j];
data[i] = data[i] - data[j];
}
}


6、希尔排序

6.1 希尔排序思想

先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;

按增量序列个数k,对序列进行k 趟排序;

每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序 列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

示例:



6.2 希尔算法实现

/**希尔排序的原理:根据需求,如果你想要结果从大到小排列,它会首先将数组进行分组,然后将较大值移到前面,较小值
* 移到后面,最后将整个数组进行插入排序,这样比起一开始就用插入排序减少了数据交换和移动的次数,可以说希尔排序是加强
* 版的插入排序
* 拿数组5, 2, 8, 9, 1, 3,4来说,数组长度为7,当increment为3时,数组分为两个序列
* 5,2,8和9,1,3,4,第一次排序,9和5比较,1和2比较,3和8比较,4和比其下标值小increment的数组值相比较
* 此例子是按照从大到小排列,所以大的会排在前面,第一次排序后数组为9, 2, 8, 5, 1, 3,4
* 第一次后increment的值变为3/2=1,此时对数组进行插入排序,
*实现数组从大到小排
*/

public static void shellSort(int[] data)
{
int j = 0;
int temp = 0;
//每次将步长缩短为原来的一半
for (int increment = data.length / 2; increment > 0; increment /= 2)
{
for (int i = increment; i < data.length; i++)
{
temp = data[i];
for (j = i; j >= increment; j -= increment)
{
if(temp > data[j - increment])//如想从小到大排只需修改这里
{
data[j] = data[j - increment];
}
else
{
break;
}

}
data[j] = temp;
}

}
}


7、归并排序

7.1 归并排序思想

归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

归并排序示例:



合并方法:

设r[i…n]由两个有序子表r[i…m]和r[m+1…n]组成,两个子表长度分别为n-i +1、n-m。

1. j=m+1;k=i;i=i; //置两个子表的起始下标及辅助数组的起始下标
2. 若i>m 或j>n,转⑷ //其中一个子表已合并完,比较选取结束
3.  //选取r[i]和r[j]较小的存入辅助数组rf
如果 r[i]<r[j],rf[k]=r[i]; i++; k++; 转⑵
否则,rf[k]=r[j]; j++; k++; 转⑵
4. //将尚未处理完的子表中元素存入rf
如果i<=m,将r[i…m]存入rf[k…n] //前一子表非空
如果j<=n ,  将r[j…n] 存入rf[k…n] //后一子表非空
5. 合并结束。


7.2 归并代码实现

public class MergeSortTest {

public static void main(String[] args) {
int[] data = new int[] { 5, 3, 6, 2, 1, 9, 4, 8, 7 };
print(data);
mergeSort(data);
System.out.println("排序后的数组:");
print(data);
}

public static void mergeSort(int[] data) {
sort(data, 0, data.length - 1);
}

public static void sort(int[] data, int left, int right) {
if (left >= right)
return;
// 找出中间索引
int center = (left + right) / 2;
// 对左边数组进行递归
sort(data, left, center);
// 对右边数组进行递归
sort(data, center + 1, right);
// 合并
merge(data, left, center, right);
print(data);
}

/**
* 将两个数组进行归并,归并前面2个数组已有序,归并后依然有序
*
* @param data
*            数组对象
* @param left
*            左数组的第一个元素的索引
* @param center
*            左数组的最后一个元素的索引,center+1是右数组第一个元素的索引
* @param right
*            右数组最后一个元素的索引
*/
public static void merge(int[] data,int left,int center,int right) {
// 临时数组
int[] tmpArr = new int[data.length];
// 右数组第一个元素索引
int mid = center + 1;
// third 记录临时数组的索引
int third = left;
// 缓存左数组第一个元素的索引
int tmp = left;
while (left <= center && mid <= right) {
// 从两个数组中取出最小的放入临时数组
if (data[left] <= data[mid]) {
tmpArr[third++] = data[left++];
} else {
tmpArr[third++] = data[mid++];
}
}
// 剩余部分依次放入临时数组(实际上两个while只会执行其中一个)
while (mid <= right) {
tmpArr[third++] = data[mid++];
}
while (left <= center) {
tmpArr[third++] = data[left++];
}
// 将临时数组中的内容拷贝回原数组中
// (原left-right范围的内容被复制回原数组)
while (tmp <= right) {
data[tmp] = tmpArr[tmp++];
}
}

public static void print(int[] data) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + "\t");
}
System.out.println();
}

}


8、基数排序

9、总结

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息