2020-08-17


排序算法(基于Java)的介绍与实现

1、排序算法的介绍 排序也称排序算法,排序是将一位数组,依照指定的顺序进行排列的过程。

2.排序算法的分类

在这里插入图片描述

3、算法复杂度

算法复杂度分为时间复杂度和空间复杂度。其作用: 时间复杂度是指执行算法所需要的计算工作量;而空间复杂度是指执行这个算法所需要的内存空间。(算法的复杂性体运行该算法时的计算机所需资源的多少上,计算机资源最重要的是时间和空间(即寄存器)资源,因此复杂度分为时间和空间复杂度。)

各排序的时间复杂度如图:

在这里插入图片描述


一、冒泡排序

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。

冒泡排序算法的原理如下: (1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。
 (2)对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 
 (3)针对所有的元素重复以上的步骤,除了最后一个。
  (4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

对arr{126,80,98,158,204}进行冒泡排序
在这里插入图片描述
在这里插入图片描述

冒泡排序的稳定性:

  冒泡排序就是把小的元素往前调或者把大的元素往后调。 比较是相邻的两个元素比较,
   交换也发生在这两个元素之间。所以,如果两个元素相等,是不会再交换的; 如果两个
   相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来, 这时候也不会交换,
   所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

实现如下:

public class Test1 {
    public static void main(String[] args) {
		int[] arr = {126,80,98,158,204};
		bucketSort(arr);
		 System.out.println(Arrays.toString(arr));
    }
    
    
    //冒泡排序
        public static void bubbleSort(int[] arr)
        {
            int temp = 0;//定义一个临时变量
            boolean flag = false;//定义一个标识变量,判断是否进行过交换
            for (int i = 0; i < arr.length - 1; i++) {
                for (int j = 0; j < arr.length - 1 - i; j++) {
                    //比较两个值,如果前面的数比后面的数大,则交换
                    if (arr[i] > arr[j])
                    {
                        temp = arr[i];
                        arr[i] = arr[j];
                        arr[j] = temp;
                        flag = true;//表明交换过
                    }
                    //代码的优化
                    if (flag) flag = false;//当交换过,则把标识改为false
                    else break;//若没交换,则此时数组排序完毕,退出循环。
                }
            }
        }

二、选择排序

选择排序(Selection sort)是一种简单直观的排序算法。 它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。

选择排序的实现思想: 第一次从arr[0] - arr[n-1]中找到最小值,然后与arr[0]交换 第二次从arr[1] - arr[n-1]中找到最小值,然后与arr[1]交换 … 第n次从arr[n] - arr[n-1]中找到最小值,然后与arr[n]交换。

 对arr{126,80,98,158,204}进行选择排序:
 
 初始: 	126	 80  98	 158 204
 第一次: **80** 126 98 158 204
 第二次: 80 **98** 126 158 204
 第三次: 80 98 **126** 158 204
 第四次:80 98 126 **158**  204
 第五次:80   98 126  158  **204**

算法实现如下:

public class Test1 {
    public static void main(String[] args) {
		int[] arr = {126,80,98,158,204};
		selectSort(arr);
		System.out.println(Arrays.toString(arr));
    }
    
//选择排序
    public static void selectSort(int[] arr)
    {

        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;//初始化最小索引为i
            int min = arr[i];//初始化最小值为arr[i]
            for (int j = i + 1; j < arr.length; j++) {
                //找出每轮的最小值
                if (arr[j] < min)
                {
                    min = arr[j];
                    minIndex = j;
                }
            }
            //将最小值放在最arr[i]上交换
            if (minIndex != i)
            {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }

三、插入排序:

插入排序(InsertSort),一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法,其时间复杂度为O(n2) 。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

插入排序法思想: 插入排序(InsertSort)的基本思想是:把n个待排序的元素开成一个有序表和一个无序表,开始时有序表只包含一个元素,无序表中包含n-1元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。

初始状态: 128 178 4 82 14 22

第一次插入: (128 178) 4 82 14 22

第二次插入: (4 128 178) 82 14 22

第三次插入: (4 82 128 178) 14 22

第四次插入: (4 14 82 128 178) 22

第五次插入: (4 14 22 82 128 178)

public class Test1 {
   	 public static void main(String[] args) {
				int[] arr = {128,178,4,82,14,22 };
				System.out.println(Arrays.toString(arr));
    }
   		//插入排序
        public static void insertSort(int[] arr)
        {
            int insertVal = 0;//初始化插入值
            int insertIndex = 0;//初始化插入索引
            for (int i = 1; i < arr.length; i++) {//
                insertVal = arr[i];//定义准备插入的数值
                insertIndex = i-1;//arr[i]前面数值的下标
                //insertIndex要保证不越界,insertVal<arr[insertIndex]还没找到插入位置
                //继续查找,则可以将arr[insertIndex]后移
                while (insertIndex >= 0 && insertVal < arr[insertIndex]){
                    arr[insertIndex + 1] = arr[insertIndex];
                    insertIndex--;
                }
                //判断是否需要赋值
                if (insertIndex + 1 != i)
                {
                    arr[insertIndex + 1] = insertVal;
                }
            }
        }

四、希尔排序

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort), 是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法,其时间复杂度为O(n^(1.3—2))。

希尔排序的基本思想: 希尔排序是把记录按下标的一定数据进行分组,对分好的每组使用插入排序算法进行排序。随着增量逐渐减少,每组的数据越来越多,当增量减到1的时候,整个数组恰好分为1组,此时退出算法。

图示:
在这里插入图片描述

代码:

public class Test1 {
    public static void main(String[] args) {
		int[] arr = {8,9,1,7,2,3,5,4,6,0};
		shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }
//希尔排序
    public static void shellSort(int[] arr)
    {
        int temp;
        //每次分组比较,每次分组根据arr.length/2
        for (int gap = arr.length/2; gap >0 ; gap/=2) {
            for (int i = gap; i < arr.length; i++) {
                //遍历每个组的元素,增量为gap
                for (int j = i - gap; j >= 0 ; j-=gap) {
                    //如果当前元素大于后面加上步长的元素,则交换
                    if (arr[j] > arr[i])
                    {
                        temp = arr[i];
                        arr[i] = arr[j];
                        arr[j] = temp;
                    }
                }
            }
        }
    }

    }

五、快速排序

快速排序(QuickSort)是对冒泡排序的一种改进。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

排序的流程:

(1)首先设置一个临界值(一般设置为中间值)

(2)通过该值将数组分成左右两部分。此时小于临界值的数据放在临界值的左边,大于临界值的数据放在临界值的右边。在临界值左右两边寻找第i次比临界值小的数与第i次比临界值大的数,然后交换。

(3)左右两边的数据接着取临界值,重复(1)(2)步骤

(4)当递归重复完上述过程后,整个数组的排序也完成了。

对arr{-9,78,0,23,-567,70}进行快速排序

在这里插入图片描述

public class Test1 {
    public static void main(String[] args) {
		int[] arr = {-9,78,0,23,-567,70};
		 quickSort(arr,0,arr.length-1);
		System.out.println(Arrays.toString(arr));
    }
    //快速排序
    public static void quickSort(int[] arr,int left,int right) {
        int l = left; //初始化左索引
        int r = right;//初始化右索引
        int temp;//临时存储变量
        int pivot = (arr[(left+right)/2]);//中轴值
        while (l<r) {
            //寻找左边需要交换的索引
            while (arr[l] < pivot) {
                l++;
            }
            //寻找右边需要交换的索引
            while (arr[r] > pivot) {
                r--;
            }
            //此时已经排好序,无需交换
            if (l >= r) break;
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            //交换完后判断arr[l] == pivot arr[r] == pivot,则需l++,r--
            if (arr[l] == pivot) r--;
            if (arr[r] == pivot) l++;

        }
            //如果左索引等于右索引,需要l++,r--
            if (l == r) {
                l++;
                r--;
            }
            //向左递归
            if (left < r)
            quickSort(arr,left,r);
            //向右递归
            if (right > l )
            quickSort(arr,l,right);

       }
}

**

六、归并排序

**

归并排序(MergeSort)是利用归并的思想来实现排序方法,采用了经典的分治法,分治法将 问题分成一些小的模块再进行递归求解,而治阶段则将分的阶段得到的各种值补在一起,即为 先分而治。其时间复杂度为:o(n log n)

通俗来讲:将需要排序的数组递归拆分成最小模块,然后在治递归合并的时候每次进行排序。

例子:

分:

						9 3 4 8 5 7 1 6 
						
					9 3 4 8 		5 7 1 6 

         9 3       4 8               5 7        1 6

       9    3    4     8           5    7     1     6

治:

		3 9           4 8	          5 7            1 6

        			 3 4 8 9		1 5 6 7

         				1 3 4 5 6 7 8 9

##代码:

public class Test1 {
    		public static void main(String[] args) {
			int[] arr = {9,3,4,8,5,7,1,6};
			mergeSort(arr,0,arr.length-1,temp);
			System.out.println(Arrays.toString(arr));
	}
	
	//归并排序,分加合的方法
    	public static void mergeSort(int[] arr,int left,int right,int[] temp){
		if(left<right){
                		int mid = (left +right)/2;
               			mergeSort(arr,left,mid,temp);
                		mergeSort(arr,mid+1,right,temp);
                		merge(arr,left,mid,right,temp);
			}
	}
	//归并排序,合的方法
    	public static void merge(int[] arr,int left,int mid,int right,int[] temp) {
            		int l= left;//初始化i,左边序列初始的索引
            		int r = mid + 1;//初始化j,右边序列初始的索引
            		int t = 0;//temp数组的索引
            		//先把左右两边(有序)的数组按照规则填充到temp数组
           		//直到左右两边的有序序列,有一边处理完毕为止
           		 while(l<=mid && r<=right)
            		{
               		 	if(arr[l]<=arr[r])	temp[t++] = arr[l++];
                		else temp[t++] = arr[r++];
            		}

            		while (l<=mid)	temp[t++] = arr[l++];
         		while (r<=right)	temp[t++] = arr[r++];
           		 //将temp数组拷到arr上
          		  t = 0;
            		for (int i = left; i <= right; i++)	arr[i] = temp[t++];
 }

七、基数排序(桶排序)

基数排序的介绍:

(1)基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义, 它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用。

(2)基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m)。

(3)基数排序法是效率高的稳定性排序法,是桶排序的扩展。

(4)其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。

基数排序的实现原理为:

将所有待比较的值统一同样的数位长度,数位较短的值则在前面补0,然后,从最低位开始,逐次进行一 一排序。这样从最低位一直到最高位排序完成后,数列则会编程一个有序的序列。下面以图文来讲解基数排序。

首先将数组的最大值求出来,判断需要进行多少轮排序,

对数组arr{128,178,4,82,14,22}使用基数排序,升序排序:

第一轮(将每个数组的个位数取出,分别放在对应的桶子里面):

在这里插入图片描述
第二轮(将每个数组的十位数取出,分别放在对应的桶子里面):

在这里插入图片描述

第三轮(将每个数组的十位数取出,分别放在对应的桶子里面):

在这里插入图片描述

以下为参考代码:

public class Test1 {
    public static void main(String[] args) {
	int[] arr = {128,178,4,82,14,22};
	bucketSort(arr);
	System.out.println(Arrays.toString(arr));
    }


//基数排序
    public static void bucketSort(int[] arr) {
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (max<arr[i]) max = arr[i];
        }
        int maxLength = (max + "").length();
        //创建一个桶
        int[][] bucket = new int[10][arr.length];
        //定义每个桶里面含有多少个值
        int[] bucketElementCounts = new int[10];
        //把值放入到桶里面去
        for (int m = 0,n = 1; m < maxLength; m++,n*=10) {
            for (int j = 0; j < arr.length; j++) {
                int digitOfElement = arr[j]/n%10;
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement] ++;
            }
            //把每个桶的值取出来
            int index = 0;
            for (int k = 0; k < bucketElementCounts.length; k++) {
                if (bucketElementCounts[k] != 0){
                    for (int l = 0; l < bucketElementCounts[k]; l++) {
                        arr[index++] = bucket[k][l];
                    }
                }
                bucketElementCounts[k] = 0;
            }
        }

猜你喜欢

转载自blog.csdn.net/zzFZJ_/article/details/108059745