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递归:点击打开链接--第一种方法的问题(没有进行判断),点击打开链接,点击打开链接,点击打开链接