排序算法简单涉及(三种)

1、算法复杂度

2、稳定性

3、适用场景

4、对应的Java代码

分析来源:以下面几个排序为例

一、冒泡排序

特点:简单的排序算法;它会遍历若干次要排序的数列,每次遍历时,它都会从前往后依次比较相邻两个数的大小;判定--如果前者比后者大,则交换它们的位置(升序)。这样一次遍历之后,最大的元素在数列末尾!循环--采用相同的方法再次遍历时,第二大元素被排列在最大元素之前。目标--重复此操作,直到整个数列都有序为止!

理解:冒泡(由于气压原因,相邻的两个气泡,后者比前者大);遍历若干次(得到最终结果需要重复执行多次);

Java代码--以函数的形式(优化)--优化

package sort;

import java.util.Random;

public class Bubbling {

	public static void main(String[] args) {

		bubling();
	}

	private static void bubling() {
		Random rd = new Random();// Random普通的类(不是工具类)

		// 测试:函数的形式

		int[] array = new int[10];
		// 随机产生整数

		for (int i = 0; i < array.length; i++) {

			array[i] = rd.nextInt(100);// [0,100)
			// 遍历结果
			System.out.print(array[i] + " ");
		}
		
		System.out.println();

		for (int i = array.length - 1; i > 0; i--) { //每次都是确定最后一个


			// 定义标示位--每次遍历(一趟)都可能发生变化
			boolean flag = true;

			// 开始进行一次排序
			for (int j = 0; j < i; j++) {

				int temp = 0;// 临时变量
				// 体现相邻
				if (array[j] > array[j + 1]) {
					// 互换位置--三种方式(最常用的)
					temp = array[j];
					array[j] = array[j + 1];
					array[j + 1] = temp;
					// 交换位置说明次序变化了(只要有一次就OK)
					flag = false;
				}
			}

			// 若无交换,说明数据已经有序,直接跳出冒泡排序算法
			if (flag == true) {
				break;
			}
		}
		for (int i = 0; i < array.length; i++) {

			System.out.print(array[i] + " ");
		}
	}
}

时间复杂度:O(n^2);理解--被排序的数列中有n个数。遍历一趟的算法复杂度为O(n),需要遍历(n-1)次!因此,冒泡排序的时间复杂度为O(n^2)

算法的稳定性:稳定

算法稳定性的解释:一个数列中有两个相等的数a[i]=a[j],在排序前,a[i]在a[j]前面,经过排序后a[i]仍然在a[j]前,排序算法稳定

思考:如果是降序呢?--简单修改

二、选择排序

基本思想起始--在未排序的数列中找到最大(小)值,然后将其存放到数列的起始位置;过程--接着再从剩余未排序的元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。目标--以此类推,直到所有的元素均排序完毕

注意:并未提到相邻的元素

代码

package sort;

import java.util.Random;

public class Select {

	public static void main(String[] args) {
		
		select();
		
	}

	
	private static void select() {
		//产生数据
		Random rd = new Random();
		int [] array=new int[20];
		for(int i=0;i<array.length;i++){
			array[i]=rd.nextInt(20);
			System.out.print(array[i]+" ");
		}
		
		System.out.println();
		
		//升序排列(注意一些细节)--只剩最后一个则无需排序了,所以是array.length-1
		for(int i=0;i<array.length-1;i++){
			for(int j=i+1;j<array.length;j++){
				//比较,互换位置
				int temp=0;
				if(array[i]>array[j]){
					temp=array[i];
					array[i]=array[j];
					array[j]=temp;
				}
			}
		}
		//查看结果
		for(int i=0;i<array.length;i++){
			System.out.print(array[i]+" ");
		}
	}
}

时间复杂度:O(n^2);理解--被排序的数列中有n个数,遍历一趟的算法复杂度为O(n),需要遍历(n-1)次!因此,冒泡排序的时间

复杂度为O(n^2)。

算法的稳定性:稳定

思考:降序呢?

三、快速排序(两到三种的方法)

 基本思想:使用分冶法策略;选择一个基数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据比另外一部分的所有数据都要小。然后,再按此方法对这两部分的数据进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

分冶法的思想:点击打开链接

一次排序的图解:


一次排序的过程:

1)设置两个变量(指针)i、j,排序开始的时候:i=0,j=N-1; 
2)以第一个数组元素作为基准点(一般),注意:基准元素会影响 算法的效率
3)从j开始向前搜索,即由后开始向前搜索(j - -),找到第一个小于A[i](此时基准点)的值A[j],将A[i]与A[j]交换; 
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于A[j](此时基准点)的值A[i],将A[j]与A[i]交换; 
5)重复第3步 
6)重复第3、4、5步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[j]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束),到此找到基准点的下标,作为分治下标 
7)重复1-6步骤递归排序前半部分 
8 ) 重复1-6步骤递归排序后半部分 

算法的实现

package sort;

import java.util.Random;

public class Quick {

	public static void main(String[] args) {
		//产生数据
		Random rd = new Random();
		//int[] array=new int[10];//72 89 77 69 33 16 42 36 46 97---最终已经结束了,但是还在迭代
		int[] array=new int[]{60,70 ,17 ,59, 22, 70, 91, 15, 39, 24};
		System.out.println("未排序的数组是:");
		for(int i=0;i<array.length;i++){
			//array[i]=rd.nextInt(100);
			System.out.print(array[i]+" ");
		}
		System.out.println();
		quick(array, 0, array.length-1);
		/*
		 * 快速排序
		 * 选择基准元素
		 * 选择两个指针
		 * 交换位置后已经OK了
		 */
	}
	
	//通俗易懂的写法
	
	private static void quick (int[] array,int start,int end){
		
		//定义结束标志(如果只有一个数据的情况)
		if(start>=end||array.length<=0){//原来start==end发生错误
			return;
		}
		
		//初始化
		int i=start;//前指针(动态变化的)
		int j=end;  //后指针(动态变化的)
		int key=array[i];//选择的基准元素(由于递归,也是动态变化的)
		boolean flag=true;//设置标志位;false--前往后(i++)搜索;true---(j--)后往前

			while(i != j){               //如果i≠j,表示还没有比较完,即即关键字左右两侧还不是最小与最大  
	            if(flag){                     
	                if(key>array[j]){    //从后向前遍历,找到小于key的值,  
	                    swap(array,i,j); //"找到"小于key的值后将array[i]与此值交换 ,准备前向比较
	                    flag = false;    
	                }else{               //如果没有找到的话j--,继续"从后向前"遍历  
	                    j--;  
	                }  
	            }else{                                
	                if(key<array[i]){    //从"前向后"遍历,与基准比较,找到大于key的值  
	                    swap(array,i,j); //将此值与array[j]进行交换  
	                    flag = true;  
	                }else{               //如果没有找到话就将i++,从前向后遍历  
	                    i++;  
	                }  
	            }  
	        }  	
				
				sprint(array);               //打印"每次"排序后的数组--为了验证  
				
				//判断的原因:防止已经结束了,任然在判断(一次判断后i==j的)
				//60 70 17 59 22 70 91 15 39 24
				//结果重复的原因:下面的两个判断中,一个变了,一个未变,所以打印的时候还是在变(有瑕疵)
				
				System.out.println(i+" "+j+" "+start+" "+end);
				
					 quick(array,start,j-1); //递归调用,将基准元素的前半段数组再用此方法进行排序,直到所有都排完为止
								
			    System.out.println(i+" "+j+" "+start+" "+end);
					 quick(array,j+1,end);   //递归调用,将基准元素的后半段数组再用此方法进行排序,直到所有都排完为止  
	//也就是说quick(array,j+1,end)是上一层循环中还没有执行过的语句,跳出当前循环后 要继续返回上层循环 继续执行咯这种情况是递归这种方法常出现的情况吗		  
		//这部分代码?还有这时执行这个方法时,参数取值的来源是什么?		  
	}
	
	/*交换函数,用于交换数组中的两个值,easy*/  
	
    public static void swap(int[] array,int i,int j){            
        int temp;  
        temp = array[i];  
        array[i] = array[j];  
        array[j] = temp;  
    }  
    
    /*sprint()函数用于打印每次排序后的结果,非必须,但可以显示每次排序情况*/
    
    public static void sprint(int[] arrays){ 
    	
		System.out.println("排序后的数组是:");  
        for(int i = 0; i <arrays.length;i++){  
            System.out.print(arrays[i] + " ");  
        }  
        System.out.println();   
    }  
}  

说明:执行过程中,排序可能未变的原因?;故意两个输出的原因?

每次当进行左侧的排序时(左侧也有左侧1和左侧2),当且仅当左侧完毕的时候,才跳出下层循环,进入上层循环(在左右递归两个输出的原因?)

细节:切分的问题;递归没有返回值(已经影响到了数组)

Java递归:点击打开链接--第一种方法的问题(没有进行判断),点击打开链接点击打开链接点击打开链接


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值