数据结构与算法之美学习笔记(11章) 排序,冒泡,插入,选择,归并,快排

第十一章 

最常用的:冒泡排序、插入排序、选择排序、归并排序、快速排序、计数排序、基数排序、桶排序。

 

1.冒泡排序

当某次冒泡操作已经没有数据交换时,说明已经达到完全有序,不用再继续执行后续的冒泡操作

代码

/**
 * @author: xuxu
 * @date 2020/2/27 9:29
 * @Description: 冒泡排序
 */
public class BubbleSort {

    public static void bubbleSort(int[] arr){
        //控制第几次排序
        for(int i=0;i<arr.length;i++){
            //因为冒泡排序 如果有一次没有发生排序证明已经完成排序,设置跳出的标志位,默认没发送排序
            boolean isSort=false;
            //控制每次排序数据间的比较和换位
            //最后一个位置不需要发生比较所以-1,每进行一次后最后稳定的部分新增1不需要比较所以-i
            for (int j=0;j<arr.length-i-1;j++){
                if(arr[j]>arr[j+1]){
                    int temp = arr[j+1];
                    arr[j+1]=arr[j];
                    arr[j]=temp;
                    isSort=true;
                }
            }
            if(!isSort){
                System.out.print("第"+(i+1)+"次排序后数组无变化退出");
                break;
            }
            //遍历
            System.out.print("第"+(i+1)+"次排序后数组为:");
            for (int k=0;k<arr.length;k++){
                System.out.print(arr[k]+" ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        int[] arr = {6,3,2,1,4};
        bubbleSort(arr);
    }
}

 

2.插入排序

首先,我们将数组中的数据分为两个区间,已排序区间未排序区间。初始已排序区间只有一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程,直到未排序区间中元素为空,算法结束。

插入排序也包含两种操作,一种是元素的比较,一种是元素移动。当我们需要将一个数据 a 插入到已排序区间时,需要拿 a 与已排序区间的元素依次比较大小,找到合适的插入位置。找到插入点之后,我们还需要将插入点之后的元素顺序往后移动一位,这样才能腾出位置给元素 a 插入。

/**
 * @author: xuxu
 * @date 2020/2/27 9:56
 * @Description:插入排序
 */
public class InsertSort {

    public static void  insertSort(int[] arr){
        //这里循环的是未排序部分
        for(int i=1;i<arr.length;i++){
            //本次循环要比较插入的数字
            int insertNum = arr[i];
            //这里是循环已排序部分,从后往前比较
            int j=i-1;
            for(;j>=0;j--){
                //如果前一个数字大于要插入的数字则向后移动让出位置
                if(arr[j]>insertNum){
                    arr[j+1] = arr[j];
                }else{
                    //一旦他找到对的位置,则不需要向前继续找,因为前面是排好序的
                    break;
                }
            }
            //将要插入的数放入前面找到的插入的位置 这里j+1是因为前面j--了
            arr[j+1]=insertNum;
            //遍历
            System.out.print("第"+(i)+"次排序后数组为:");
            for (int k=0;k<arr.length;k++){
                System.out.print(arr[k]+" ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        int[] arr = {6,3,2,1,4};
        insertSort(arr);
    }
}

 

3.选择排序

选择排序算法的实现思路有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾。

/**
 * @author: xuxu
 * @date 2020/2/27 11:02
 * @Description: 选择排序
 */
public class SelectSort {

    public static void selectSort(int[] arr){
        for (int i=0;i<arr.length;i++){
            int min=i;
            for(int j=i;j<arr.length-1;j++){
                if(arr[min]>arr[min+1]){
                    //先找出未排序区最小的一个下标
                    min = min+1;
                }
            }
            //将min放入已排序区的最后面,同时原位置的元素放在min处
            int temp = arr[i];
            arr[i]=arr[min];
            arr[min] = temp;

            //遍历
            System.out.print("第"+(i+1)+"次排序后数组为:");
            for (int k=0;k<arr.length;k++){
                System.out.print(arr[k]+" ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        int[] arr = {6,3,2,1,4};
        selectSort(arr);
    }
}

从推荐使用的角度看 插入>冒泡>选择

 

冒泡排序、插入排序、选择排序这三种排序算法,它们的时间复杂度都是 O(n2),比较高,适合小规模数据的排序。

归并排序快速排序两种时间复杂度为 O(nlogn) 。这两种排序算法适合大规模的数据排序

归并排序和快速排序都用到了分治思想

 

4.归并排序

分治算法一般都是用递归来实现的。分治是一种解决问题的处理思想,递归是一种编程技巧,这两者并不冲突。

递归公式

递推公式:
merge_sort(p…r) = merge(merge_sort(p…q), merge_sort(q+1…r))
 
终止条件:
p >= r 不用再继续分解

merge_sort(p…r) 表示,给下标从 p 到 r 之间的数组排序。我们将这个排序问题转化为了两个子问题,merge_sort(p…q) 和 merge_sort(q+1…r),其中下标 q 等于 p 和 r 的中间位置,也就是 (p+r)/2。当下标从 p 到 q 和从 q+1 到 r 这两个子数组都排好序之后,我们再将两个有序的子数组合并在一起,这样下标从 p 到 r 之间的数据就也排好序了

合并过程

我们申请一个临时数组 tmp,大小与 A[p…r] 相同。我们用两个游标 i 和 j,分别指向 A[p…q] 和 A[q+1…r] 的第一个元素。比较这两个元素 A[i] 和 A[j],如果 A[i]<=A[j],我们就把 A[i] 放入到临时数组 tmp,并且 i 后移一位,否则将 A[j] 放入到数组 tmp,j 后移一位。

继续上述比较过程,直到其中一个子数组中的所有数据都放入临时数组中,再把另一个数组中的数据依次加入到临时数组的末尾,这个时候,临时数组中存储的就是两个子数组合并之后的结果了。最后再把临时数组 tmp 中的数据拷贝到原数组 A[p…r] 中。

/**
 * @author: xuxu
 * @date 2020/2/27 14:32
 * @Description: 归并排序
 * f(a,p) =f(a,q)+f(q+1,p)
 */
public class MergeSort {

    public static void mergeSort(int[] arr,int begin,int end ){
        if(begin>=end){
            return ;
        }
        //中间位置
        int mid = (begin+end)/2;
        //递归分解合并
        mergeSort(arr, begin, mid);
        mergeSort(arr, mid+1 ,end);
        merge(arr,begin,mid,end);
    }

    /**
     * 合并操作
     */
    public static void merge(int[] arr, int begin, int mid, int end){
        int[] mergeArr=new int[arr.length];
        //数组1的指针
        int p1 = begin;
        //数组2的指针
        int p2 = mid+1;
        //合并的数组起始位置
        int index=begin;
        while(p1<=mid && p2<=end){
            if(arr[p1]>=arr[p2]){
                mergeArr[index++]=arr[p2++];
            }else{
                mergeArr[index++]=arr[p1++];
            }
        }
        while(p1<=mid){
            mergeArr[index++] = arr[p1++];
        }
        while(p2<=end){
            mergeArr[index++] = arr[p2++];
        }

        for(int i=begin;i<=end;i++){
            arr[i] = mergeArr[i];
        }
    }

    public static void main(String[] args) {
        int[] arr = {6,5,3,4,2,1,7};
        mergeSort(arr, 0, arr.length-1);
        System.out.print("排序后数组元素为:");
        for (int i=0;i<arr.length;i++){
            System.out.print(arr[i]);
        }
    }
}

 

5.快速排序

基本思想:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分

实现

public class QuickSort {
	
	public static int getMid(int[] nums,int low,int high){
		int temp=nums[low];
		while(low<high){
			while(low<high && nums[high]>=temp){
				high--;
			}
			nums[low]=nums[high];//如果右边小于中间数 移动到左边
			while(low<high && nums[low]<=temp){
				low++;			
			}
			nums[high]=nums[low];	//如果左边有大于中间数 的数移动到右边
		}
		nums[low]=temp;	//赋值中间数
		return low;
	}
	
	public static void quick(int[] nums , int low,int high){
		if(low<high){
		int mid = getMid(nums,low,high);
		//递归调用左右两边继续排序
		quick(nums,low,mid-1);
		quick(nums,mid+1,high);
		}
	}
	
	public static void main(String[] args) {
		int[] nums = {22,11,33,66,77,55,44,99,88};
		quick(nums,0,nums.length-1);
		for (int i = 0; i < nums.length; i++) {
			System.out.print(nums[i]+" ");
		}
	}
}

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值