文档版本 | 开发工具 | 测试平台 | 工程名字 | 日期 | 作者 | 备注 |
---|---|---|---|---|---|---|
V1.0 | 2016.04.06 | lutianfei | none | |||
V1.1 | 2016.07.19 | lutianfei | 增加了快速排序算法 |
- 排序
- 外排序:需要在内外存之间多次交换数据才能进行
- 内排序:
- 插入类排序
- 直接插入排序
- 希尔排序
- 选择类排序
- 简单选择排序
- 堆排序
- 交换类排序
- 冒泡排序
- 快速排序
- 归并类排序
- 归并排序
- 插入类排序
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
简单选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 稳定 |
直接插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
希尔排序 | O(nlogn)~O(n^2) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
快速排序 | O(nlogn) | O(nlogn) | O(n^2) | O(logn)~O(n) | 不稳定 |
- 测试工程说明
- 下文都是用以下测试用例进行测试
public static void main(String[] args) {
int[] A = new int[] { 11, 2, 3, 22, 4, 1, 11, 10, 6, 5, 22, 13, 21 };
int n = A.length;
int[] A_sort = new int[n];
System.out.println("排序前:");
arrayPrint(A);
System.out.println("排序后:");
mSort(A,A_sort,0, n-1);
// System.out.println(A);
arrayPrint(A_sort);
System.out.println("标准答案");
Arrays.sort(A);
arrayPrint(A);
}
/**
*
* <p>@MethodName : arrayPrint</p>
* <p>@Description: 打印数组至控制台</p>
* @date : 2016-7-6 ,下午4:51:43
* @param : @param 待打印数组A
* @Version : v1.0
*/
public static void arrayPrint(int[] A) {
System.out.print("[ ");
for (int i = 0; i < A.length; i++) {
System.out.print(A[i] + " ");
}
System.out.println("]");
System.out.println();
}
简单排序算法
冒泡算法
初级冒泡算法
public class BubbleSort {
public int[] bubbleSort(int[] A, int n) {
for(int i=0;i<n-1;i++){
for(int j = n-1;j>i;j--){
if(A[j]<A[j-1]){
int temp = A[j-1];
A[j-1] = A[j];
A[j] = temp;
}
}
}
return A;
}
}
冒泡排序优化
import java.util.*;
public class BubbleSort {
public int[] bubbleSort(int[] A, int n) {
boolean flag =true;
for(int i=0;i<n-1 && flag;i++){
flag = false;
for(int j = n-1;j>i;j--){
if(A[j]<A[j-1]){
int temp = A[j-1];
A[j-1] = A[j];
A[j] = temp;
flag = true;
}
}
}
return A;
}
}
简单选择排序
import java.util.*;
public class SelectionSort {
public int[] selectionSort(int[] A, int n) {
int min = 0;
for(int i =0 ; i<n;i++){
min = i;
for(int j=i;j<n;j++){
if(A[j]<A[min]){
min = j;
}
}
if(min != i){
int temp = A[i];
A[i] = A[min];
A[min] = temp;
}
}
return A;
}
}
直接插入排序算法
package SortTest;
public class SortTest {
public static void main(String[] args) {
int[] A = new int[]{11,2,3,22,4,1,11,10};
int n = 8;
arrayPrint(A);
A = insertionSort(A,n);
//System.out.println(A);
arrayPrint(A);
}
public static int[] insertionSort(int[] A,int n) {
int temp = 0;
for (int i = 1; i < n; i++) {
if (A[i] < A[i - 1]) {
temp = A[i];
int j ;
for (j = i-1; j>=0 && A[j] > temp ; j--) { //最后一次j--可能会为负值,这是为了配合下面A[j+1]
A[j+1] = A[j];
}
A[j+1] = temp;
}
}
return A;
}
public static void arrayPrint(int[] A){
System.out.print("[ ");
for(int i=0;i<A.length;i++){
System.out.print(A[i]+" ");
}
System.out.println("]");
}
}
改进排序算法
希尔排序算法
public static int[] shellSort(int[] A, int n){
int inc = n;
do{
inc = inc/4+1;
for(int i=inc;i<n;i++){
if(A[i-inc]>A[i]){
int temp = A[i];
int j;
for(j=i-inc; j>=0 && A[j]>temp;j-=inc){
A[j+inc] = A[j];
}
A[j+inc] = temp;
}
}
}
while(inc>1);
return A;
}
堆排序
实现步骤
- 假设有数组长度为n,下标0~n-1
- 先将数组按照
大堆顶
格式的完全二叉树
进行排序。 以下两步循环n-1次后,可完成排序。
- 将大顶堆的
根节点
与最后一个节点
交换位置。 - 将剩余的n-1个元素重新构造成一个大顶堆。
- 将大顶堆的
注:大堆顶完全二叉树数学定义
- K[i] >= K[2i+1] , i=0…n-1
- K[i] >= K[2i+2] , i=0…n-1
public static int[] heapSort(int[] A, int n) {
for (int i = (n - 1) / 2; i >= 0; i--) {
//循环从最后一个拥有子节点的 节点下标 为(n-1)/2,这样循环可以实现从下到上,从左到右,将较大的数向上推,最终实现大顶堆。
//循环到0结束,因为根节点0,也有两个子节点要比较。
heapAdjust(A, i, n-1);
}
for(int i = n-1;i>0;i--){
//循环从n-1开始,因为每次要交换根节点A[0],与最后一个节点A[i-1]
//循环到1结束,因为此时只剩下A[0],A[1]进行比较
int temp = A[0];
A[0] = A[i];
A[i] = temp;
heapAdjust(A, 0, i-1);//此时只有上两层不满足大顶堆要求。
}
return A;
}
static void heapAdjust(int[] A, int s, int m) {//大顶堆排序实现
int temp = A[s];
for(int j=2*s+1;j<=m;j=j*2+1){
//循环从左子节点2*s+1开始
//直到数组末尾m结束
//步进:j*2+1为下一个左子节点。
if(j<m && A[j]<A[j+1]){ //j<m 是为了保证j+1不会超出边界;比较左右两个子节点谁大记录谁的下标。
j++;
}
if(temp > A[j]){//将父节点A[s]与上面得到的较大子节点进行对比,若为true说明已经是大堆顶,退出循环。
break;
}
else{ //否则将较大子节点的值赋值给父节点。并将原父节点的下标s变为较大节点的下标j,也就是说下次循环的父节点可能变成本次循环的较大子节点。由此实现了将较大数向上推,较小数向下踢。
A[s] = A[j];
s = j;
}
}
A[s] = temp;//当整个循环完成后,将最初父节点的值,赋值到循环后相应的节点上。
}
归并排序
- 原理:假设初始序列含有n个记录,则可以看成是n个能有序的子序列,每个子序列的长度为1,然后两两归并,得到┎n/2┒(┎x┒表示不小于x的最小整数)个长度为2或1的有序子序列;再两两归并,…,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。
递归实现归并排序
/**
*
* <p>@MethodName : mSort</p>
* <p>@Description: 递归型归并排序算法</p>
* @date : 2016-7-6 ,下午4:27:19
* @param : @param sR :原始数组
* @param : @param tR1 :排序后数组
* @param : @param s :数组起始下标
* @param : @param t :数组结尾下标
* @Version : v1.0
*/
static void mSort(int[] sR,int[] tR1, int s , int t){
int m; //将数组二等分坐标
int[] tR2 = new int[sR.length]; //二分后数组的临时排序缓冲数组
if(s==t){
tR1[s]=sR[s];
}
else {
m=(s+t)/2; //将sR[s..t] 平分为 sR[s..m]和sR[m+1..t]
mSort(sR, tR2, s, m); //递归将sR[s..m]归并为有序的tR2[s..m]
mSort(sR, tR2, m+1, t); //递归将sR[m+1,t]归并为有序tR2[m+1..t]
merge(tR2,tR1,s,m,t); //将tR2[s..m]和tR2[m+1,t]归并到tR1[s..t]
}
}
/**
*
* <p>@MethodName : merge</p>
* <p>@Description: 将部分归并数组进行最后排序</p>
* @date : 2016-7-6 ,下午4:40:33
* @param : @param sR :部分归并数组
* @param : @param tR :最终有序数组
* @param : @param i :数组起始下标
* @param : @param m :数组中间下标
* @param : @param n :数组结尾下标
* @Version : vx.x
*/
static void merge(int sR[],int tR[] ,int i ,int m ,int n){
int j,k,l;
for(j=m+1,k=i;i<=m && j<=n;k++){
if(sR[i] < sR[j]){
tR[k] = sR[i++];
}else {
tR[k] = sR[j++];
}
}
if(i<=m){
for(l=0;l<=m-i;l++){
tR[k+l]=sR[i+l];
}
}
if(j<=n){
for(l=0;l<=n-j;l++){
tR[k+l]=sR[j+l];
}
}
}
非递归方式实现归并排序
- 非递归的迭代方法,避免了递归时深度为log2^n的栈空间,所需空间只是用到申请归并临时用的tR数组,因此空间复杂度为O(n),并且避免递归也在时间性能上有一定提升。
/**
*
* <p>@MethodName : mergeSort</p>
* <p>@Description: 非递归方式的合并排序</p>
* @param : @param sR 带排序的数组
* @date : 2016-7-6 ,下午9:15:30
* @Version : v1.0
*/
static int[] mergeSort(int[] sR){
int m; //将数组二等分坐标
int[] tR = new int[sR.length]; //二分后数组的临时排序缓冲数组
int k=1; //子序列长度加倍因子
while(k < sR.length){
MergePass(sR,tR,k,sR.length-1);
k=k*2;
MergePass(tR,sR,k,sR.length-1);
k=k*2;
}
return sR;
}
/**
*
* <p>@MethodName : MergePass</p>
* <p>@Description: 将sR数组中相邻长度为s的子序列两两归并到tR数组中</p>
* @date : 2016-7-6 ,下午9:20:15
* @param : @param sR :原始数组
* @param : @param tR :排序后数组
* @param : @param s :带排序子数组的长度
* @param : @param n :原始数组长度
* @Version : v1.0
*/
static void MergePass(int sR[] , int tR[], int s , int n){
int i = 0;
while(i <= n-2*s+1){ //步进为2s的情况下:能循环的最大次数
merge(sR, tR, i, i+s-1, i+2*s-1);
i=i+2*s;
}
if(i<n-s+1){//将最后剩下的不够2s长度的子序列进行排序
merge(sR, tR, i, i+s-1, n);
}
else{
for(int j=i;j<=n;j++){
tR[j] = sR[j];
}
}
}
/**
*
* <p>@MethodName : merge</p>
* <p>@Description: 将部分归并数组进行最后排序</p>
* @date : 2016-7-6 ,下午4:40:33
* @param : @param sR :部分归并数组
* @param : @param tR :最终有序数组
* @param : @param i :数组起始下标
* @param : @param m :数组中间下标
* @param : @param n :数组结尾下标
* @Version : vx.x
*/
static void merge(int sR[],int tR[] ,int i ,int m ,int n){
int j,k,l;
for(j=m+1,k=i;i<=m && j<=n;k++){
if(sR[i] < sR[j]){
tR[k] = sR[i++];
}else {
tR[k] = sR[j++];
}
}
if(i<=m){
for(l=0;l<=m-i;l++){
tR[k+l]=sR[i+l];
}
}
if(j<=n){
for(l=0;l<=n-j;l++){
tR[k+l]=sR[j+l];
}
}
}
快速排序算法
- 基本思想:通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可以分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
/**
*
* <p>@MethodName : quickSort</p>
* <p>@Description: 进行快速排序</p>
* @date : 2016年7月18日 ,下午7:19:20
* @param : @param A
* @return
* @Version : vx.x
*/
public static void quickSort(int[] A,int startIdx,int endIdx){
int pivot;
if(startIdx < endIdx){
pivot = partition(A,startIdx,endIdx); //将待排序数组一分为二,返回枢轴值pivot
quickSort(A,startIdx,pivot-1); //对于弟子表递归排序
quickSort(A,pivot+1,endIdx); //对高子表递归排序
}
}
/**
*
* <p>@MethodName : partition</p>
* <p>@Description: 获取枢轴值位置</p>
* @date : 2016年7月18日 ,下午11:18:15
* @param : @param A 待排序数组
* @param : @param startIdx 开始坐标
* @param : @param endIdx 结束坐标
* @param : @return 枢纽坐标位置
* @Version : vx.x
*/
public static int partition(int[] A,int startIdx,int endIdx){
int pivotkey;
pivotkey = A[startIdx]; //这里用子表的第一个记录做枢轴记录
while(startIdx < endIdx){
while(startIdx < endIdx && A[endIdx] >= pivotkey){ //此时pivotkey=A[startIdx]
endIdx--;
}
swap(A,startIdx,endIdx);
//pivotkey = A[endIdx]
while(startIdx < endIdx && A[startIdx] <= pivotkey){
startIdx++;
}
swap(A,startIdx,endIdx);
pivotkey=A[startIdx]
}
return startIdx;
}
/**
*
* <p>@MethodName : swap</p>
* <p>@Description: 交换数组a,b位置的数据</p>
* @date : 2016年7月18日 ,下午11:37:53
* @param : @param A
* @param : @param a
* @param : @param b
* @Version : vx.x
*/
static void swap(int[] A , int a, int b){
int temp = A[a];
A[a] = A[b];
A[b] = temp;
}