八大排序算法(Java语言)

排序分为内部排序和外部排序

内部排序:数据在内存中进行排序

外部排序:因为排序的数据较大,一次不能容纳全部的数据,在排序过程中需要访问外存



这里的八大排序算法全是内部排序

当n较大时,应该采用时间复杂度为O(nlog2n)的排序算法:快速排序、堆排序和归并排序

快速排序:是目前基于比较的内部排序中最好的排序方法,当待排序的关键字是随机分布时,快速排序的平均时间最短

1.插入排序——直接插入排序

基本思想:

将一个数据插入到已排序好的数据队列中,从而得到一个新的、数据量加1的数据队列。

即:先将序列的第一个数据当成一个有序的序列,然后从第二个数据开始逐个进行插入,直至整个序列有序为止

直接插入排序示例:


如果插入数据时碰到相等的数据,那么把要插入的数据放到相等数据的后面

算法实现:

package 直接插入排序算法;

/**
 * 基本思想
 * 将一个数据插入到已排序好的数据队列中,从而得到一个新的、数据量加1的数据队列。
 * 即:先将序列的第一个数据当成一个有序的序列,然后从第二个数据开始逐个进行插入,直至整个序列有序为止
 */
public class StraightInsertSort {
	public static void print(int[] a, int i) {
		System.out.print(i + ":");
		for(int j = 0; j < a.length; j++) {
			System.out.print(a[j] + "  ");
		}
		System.out.println();
	}

	public static void straightInsertSort(int[] a) {
		for(int i = 1; i < a.length; i++) {
			// 前面的序列已经从小到大排好序了
			// 如果比前面序列的最后一个数字小,则找到位置后插入
			// 如果比前面序列的最后一个数字大,则直接插入
			if(a[i] < a[i - 1]) {
				int x = a[i]; // 要插入的数据
				int j = i - 1;
				while(j >= 0 && a[j] > x) {
					a[j + 1] = a[j];
					j--;
				}
				a[j + 1] = x;
			}
			// 打印每次排序的结果
			print(a, i);	
		}
	}
	
	public static void main(String[] args) {
		int[] a = new int[] {3, 1, 5, 7, 2, 4, 9, 6};
		straightInsertSort(a);
	}
}

2.插入排序——希尔排序(直接插入排序的改进)

基本思想:

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

即:先将要排序的序列按某个增量d(n/2,n为要排序的个数)分成若干组子序列,每组序列中相邻数据下标相差d,对每组序列分别进行直接插入排序,然后再用d/2作为增量重复以上操作,直到增量为1时进行分组直接插入排序(当增量为1时,其实就是将所有数据分为1组并进行直接插入排序。)


算法实现:

package 希尔排序算法;

public class ShellInsertSort {
	public static void print(int[] a, int i) {
		System.out.print(i + ":");
		for(int j = 0; j < a.length; j++) {
			System.out.print(a[j] + "  ");
		}
		System.out.println();
	}

	public static void shellInsertSort(int[] a, int dk) {
		for(int i = dk; i < a.length; i++) {
			// 前面的序列已经从小到大排好序了
			// 如果比前面序列的最后一个数字小,则找到位置后插入
			// 如果比前面序列的最后一个数字大,则直接插入
			if(a[i] < a[i - dk]) {
				int x = a[i]; // 要插入的数据
				int j = i - dk;
				while(j >= 0 && a[j] > x) {
					a[j + dk] = a[j];
					j -= dk;
				}
				a[j + dk] = x;
			}
			// 打印每次排序的结果
			print(a, i);	
		}
	}
	
    public static void shellSort(int[] a) {
    	int dk = a.length / 2;
    	while(dk >= 1) {
    		shellInsertSort(a, dk);
    		dk = dk / 2;
    	}
    	
    }
	
	public static void main(String[] args) {
		int[] a = new int[] {3, 1, 5, 7, 2, 4, 9, 6};
		shellSort(a);
	}
}
3.选择排序——简单选择排序

基本思想:

在要排序的一组数据中,选出最小的数与第1个位置的数进行交换,然后在剩下的数中选择最小的数再找最小的数与第2个位置的数进行交换,依次类推,直到第n-1个数与第n个数进行了比较为止

简单选择排序的示例:


算法实现:

package 选择排序算法;

public class SelectSort {
	public static void print(int[] a, int i) {
		System.out.print("第" + (i + 1) + "趟:");
		for(int j = 0; j < a.length; j++) {
			System.out.print(a[j] + "  ");
		}
		System.out.println();
	}
	
	/**
	 * @param a  数组
	 * @param i  从下标为i这个数开始(包括)
	 * @return 返回最小的数的下标
	 */
	public static int selectMinKey(int[] a, int i) {
		int k = i;
		for(int j = i + 1; j < a.length; j++) {
			if(a[j] < a[k]) {
				k = j;
			}
		}
		return k;
	}
	
	public static void selectSort(int[] a) {
		int key = 0;
		int temp = 0;
		for(int i = 0; i < a.length - 1; i++) {
			key = selectMinKey(a, i);
			if(key != i) {
				temp = a[key];
				a[key] = a[i];
				a[i] = temp;
			}
			print(a, i);
		}
	}

	public static void main(String[] args) {
		int[] a = new int[] {3, 1, 5, 7, 2, 4, 9, 6};
		selectSort(a);
	}
}

4.选择排序——堆排序(简单选择排序的改进)

堆的定义如下:具有n个元素的序列(k1,k2........kn),当且仅当满足


时称之为堆。

k1 <= k2  且 k1 <= k3   

k2 <= k4  且 k2 <= k5   

k3 <= k6  且 k3 <= k7


若以一维数组存储一个堆,则堆对应一棵完全二叉树

根节点(堆顶元素)的值必定是最大(最小)的

所有非叶节点的值均大于等于(小于等于)其子女的值

堆排序:

初始时把要排序的n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),当不一定是堆,调整他们的存储序,使之成为一个堆。将堆顶元素输出,得到n个数中最小(最大)的数。然后调整剩下的n-1个数,使之成为堆,输出堆顶元素,得到n个数中次小(次大)的数。依次类推,最后得到n个数的有序序列。

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

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

2.输出堆顶元素后,怎么调整剩余的元素,使他们成为一个新堆

先解决第二个问题:


如图是调整小顶堆的方法

主要是:将根节点与左、右子树中较小元素进行交换

对n个元素初始建堆的过程:

对初始序列建堆得过程,其实就是一个反复筛选的过程


算法实现:

package 堆排序算法;

public class HeapSort {
    public static int[] heapSort(int[] A, int n) {
        //堆排序算法

        int i;
        //先把A[]数组构建成一个大顶堆。
        //从完全二叉树的最下层最右边的非终端结点开始构建。
        for(i=n/2-1;i>=0;i--){
            HeapAdjust(A,i,n);
        }

        //开始遍历
        for(i=n-1;i>0;i--){
            swap(A,0,i);
            //每交换一次得到一个最大值然后丢弃
            HeapAdjust(A,0,i);
        }
        return A;

    }
    //A[i]代表的是下标为i的根结点
    private static void HeapAdjust(int[] A,int i,int n){
        //【注意】这里下标从0开始
        int temp;
        //存储根结点
        temp = A[i];
        //沿根结点的左右孩子中较大的往下遍历,由于完全二叉树特性 i的左子节点2i+1  i的右子节点2i+2
        for(int j=2*i+1;j<n;j=j*2+1){

            if(j<n-1&&A[j]<A[j+1]){
                ++j;
            }

            if(temp>=A[j]){
                break;
            }
            //将子节点赋值给根结点
            A[i] = A[j];
            //将子节点下标赋给i
            i = j;
        }
        //将存储的根结点的值赋给子节点
        A[i] = temp;

    }
    private static void swap(int[] A,int i,int j){
        int temp = A[i];
        A[i]=A[j];
        A[j] = temp;

    }
    public static void main(String[] args) {
		int[] a = new int[] {1,3,4,7,2,5,9,6};
		int[] b = heapSort(a, a.length);
		for(int i = 0; i < b.length; i++) {
			System.out.print(b[i] + "  ");
		}
	}
}
5.交换排序——冒泡排序

基本思想:

在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的数往上冒。即:每当两相邻的数比较后发现他们的排序与排序要求相反时,就将他们互换。

冒泡排序示例:

算法实现:

package 冒泡排序;

/**
 * 从小到大排序
 */
public class BubbleSort {
	public static void bubbleSort(int[] a) {
		// i表示第几趟排序    排序(a.length - 1)趟即可
		for(int i = 0; i < a.length - 1; i++) {
			// j表示数据的下标
			for(int j = 0; j < a.length - i - 1; j++) {
				if(a[j] >  a[j + 1]) {
					int temp = a[j];
					a[j] = a[j + 1];
					a[j + 1] = temp;
				}
			}
		}
	}

	public static void main(String[] args) {
		int[] a = new int[] {9, 6, 7, 9, 2, 3};
		bubbleSort(a);
		for(int i = 0; i < a.length; i++) {
			System.out.print(a[i] + "  ");
		}
	}
}

6.交换排序——快速排序

基本思想:

(1)选择一个基准数据,通常选择第一个数据或者最后一个数据

(2)通过一趟排序,将带排序的序列分为两部分,其中一部分数据比基准数据小,另一部分数据比基准数据大。

(3)此时基准数据排在正确的位置

(4)然后对这两部分记录用同样的方法进行排序,直至整个序列有序。

快速排序的示例:


左边的比基准数据小,右边的比基准数据大

27比49小,交换27和49的位置

65比49大,交换65和49的位置


算法实现:

package 快速排序;

public class QuickSort {
    public static int[] quickSort(int[] A, int n) {
        //快速排序

        qSort(A,0,n-1);

        return A;

    }
    public static void qSort(int[] A,int left,int right){

         //枢轴
        int pivot;
        if(left<right){

            pivot = partition(A,left,right);

            qSort(A,left,pivot-1);
            qSort(A,pivot+1,right);

        }      

    }

    //优化选取一个枢轴,想尽办法把它放到一个位置,使它左边的值都比它小,右边的值都比它大
    public static int partition(int[] A,int left,int right){

        //优化选取枢轴,采用三数取中的方法
        int pivotKey = median3(A,left,right);
        //从表的俩边交替向中间扫描
        //枢轴用pivotKey给备份了
        while(left<right){
           while(left<right&&A[right]>=pivotKey){
               right--;
           }
            //用替换方式,因为枢轴给备份了,多出一个存储空间
            A[left]=A[right];
           while(left<right&&A[left]<=pivotKey){
               left++;
           }
           A[right]=A[left];

        }

        //把枢轴放到它真正的地方
        A[left]=pivotKey;
        return left;
    }
    //三数取中
    public static int median3(int[] A,int left,int right){

        int mid=(right-left)/2;
        if(A[left]>A[right]){
            swap(A,left,right);
        }
        if(A[mid]>A[left]){
            swap(A,mid,left);
        }
        if(A[mid]>A[right]){
            swap(A,mid,right);
        }

        return A[left];
    }

    public static void swap(int[] A,int i,int j){
        int temp =A[i];
        A[i]=A[j];
        A[j]=temp;
    }
    
    public static void main(String[] args) {
    	int[] a = new int[] {1, 7, 6, 4, 9};
    	int[] b = quickSort(a, a.length);
    	for(int i = 0; i < b.length; i++) {
    		System.out.print(b[i] + "  ");
    	}
	}
}
7.归并排序

基本思想:

把待排序序列分为若干个子序列,每个子序列是有序的,然后再把有序子序列合并为整体有序序列。

归并排序示例:


算法实现:

package 归并排序;

public class MergeSort {
    public int[] mergeSort(int[] A, int n) {
       //归并排序,递归做法,分而治之

        mSort(A,0,n-1);
        return A;
    }

    private void mSort(int[] A,int left,int right){
        //分而治之,递归常用的思想,跳出递归的条件
        if(left>=right){
            return;
        }
        //中点
        int mid = (left+right)/2;
        //有点类似后序遍历!
        mSort(A,left,mid);
        mSort(A,mid+1,right);

        merge(A,left,mid,right);



    }

    //将左右俩组的按序子序列排列成按序序列
    private void merge(int[] A,int left,int mid,int rightEnd){
        //充当tem数组的下标
        int record = left;
        //最后复制数组时使用
        int record2 = left;
        //右子序列的开始下标
        int m =mid+1;
        int[] tem = new int[A.length];

        //只要left>mid或是m>rightEnd,就跳出循环
        while(left<=mid&&m<=rightEnd){

            if(A[left]<=A[m]){

                tem[record++]=A[left++];
            }else{
                tem[record++]=A[m++];
            }

        }
        while(left<=mid){
            tem[record++]=A[left++];
        }
        while(m<=rightEnd){
            tem[record++]=A[m++];
        }
        //复制数组
        for( ;record2<=rightEnd;record2++){
            A[record2] = tem[record2];
        }
    }
    
    public static void main(String[] args) {
		int[] a = new int[] {1, 5, 3, 7, 5};
		int[] b = new MergeSort().mergeSort(a, a.length);
		for(int i = 0; i < b.length; i++) {
			System.out.print(b[i] + "  ");
		}
	}

}
8.基数排序

基本思想:

先按照低位排序,然后收集;然后再按照高位排序,然后收集;依次类推,直到最高位。

算法实现:

package 基数排序;

import java.util.ArrayList;

public class RadixSort {
    public int[] radixSort(int[] A, int n) {
        //基于桶排序的基数排序

        //确定排序的趟数,即排序数组中最大值为809时,趟数为3
         int max=A[0];
        for(int i=0;i<n;i++){
            if(A[i]>max){
                max= A[i];
            }
        }
        //算出max的位数
        int time=0;
        while(max>0){
            max/=10;
            time++;
        }
        //【桶】初始化十个链表作为桶,用户分配时暂存
        ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
        for(int i=0;i<10;i++){
            ArrayList<Integer> Item = new ArrayList<Integer>();
            list.add(Item);
        }

        //进行time次分配和收集
        for(int i=0;i<time;i++){

            //分配元素,按照次序优先,从个位数开始
            for(int j=0;j<n;j++){
                int index = A[j]%(int)Math.pow(10,i+1)/(int)Math.pow(10,i);
                list.get(index).add(A[j]);
            }
            //收集元素,一个一个桶地收集
            int count=0;
            //10个桶
            for(int k=0;k<10;k++){
                //每个桶收集
                if(list.get(k).size()>0){

                    for(int a: list.get(k)){
                        A[count]=a;
                        count++;   
                    }
                  //清除数据,以便下次收集
                    list.get(k).clear();
                }
            }
        }
        return A; 
    }
    
    public static void main(String[] args) {
    	int[] a = new int[] {1, 6, 3, 4, 9};
		int[] b = new RadixSort().radixSort(a, a.length);
		for(int i = 0; i < b.length; i++) {
			System.out.print(b[i] + "  ");
		}
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值