图解基本排序算法

简单选择排序

冒泡排序是将最大的元素往后面排,简单选择排序是将小的元素往前面排

算法思想:
1.将第一个元素和其余元素进行对比,如果第一个元素和其他元素相比,第一个元素大,则交换,一轮下来,最小的元素会在第一个
2.将第二个元素和其余的元素对比,如果对比结果大,则交换
3.重复

算法图解:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以此类推……
Java代码:

void simpleSort(int[] a){
	int temp;
	for (int i=0;i<a.length;++i)
	{
		temp=a[i];
		for(int j=i;j<a.length;++j)
		{
			if (a[j]<temp)
			{
				temp=ia[j];
				swap(a[i],a[j]);
			}
		}
	}
}

冒泡排序

冒泡排序就是把一个数组中最大的数字往数组的末端排序:

算法思想:
1.比较相邻的两个元素,如果第一个元素比第二个元素大,就交换
2.对每一对相邻的元素都执行1操作,从开始第一对到最后一对(所以经过一轮之后,最大的数字会被安排到最末尾的位置放着)
3.第一轮排序之后,因为最后一个数字是最大的,不论如何位置都不会改变的,那么我们就不管最后一个,排除第一个位置之后,我们执行1,2操作

算法图解:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
冒泡排序算法是稳定的排序算法:当arr[i]==arr[i+1]时,我们可以选择不移动,所以为稳定的
时间复杂度位O(n^2):双重循环遍历
空间复杂度O(1):只在原本的数组上排序交换

Java代码:

public void bubbleSort(int[] array) {
    int n = array.length;
    for (int i = 0; i < n - 1; i++) {
        //在这里j不需要遍历到n-1了,因为n-1-i~n-1之间的元素已经排好序了,不需要再比较
        for (int j = 0; j < n - 1 - i; j++) {
            //将最大元素移动到数组末尾
            if (array[j] > array[j + 1]) {
            //交换
                swap(array, j, j + 1);
            }
        }
    }
}

void swap(int[] array, int i, int j) {
    array[i] = array[i] + array[j];
    array[j] = array[i] - array[j];
    array[i] = array[i] - array[j];
}

选择排序

选择排序和冒泡排序时一样的思想,都是把最大的数字排到最后面,不同的是,冒泡排序是对比相邻元素交换,而选择排序是直接将最大的数字和最后一个数字交换。

算法图解:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如此类推;

java代码:

public void selectSort(int [[ nums){
    int size = nums.length;
    for (int i=0;i<size-1;i++){
        int a = i;
        for (int j=i+1;j<size;j++){
            if (nums[a] > nums[j]){
                a = j;
            }
        }
        int temp = nums[a];
        nums[a] = nums[i];
        nums[i] = temp;   
    }
}

选择排序是不稳定的排序算法;
时间复杂度:O(n^2)
空间复杂度:O(1)

插入排序

顾名思义就是将元素插入到他属于的位置上。
插入排序非常类似于整扑克牌。在开始摸牌时,左手是空的,牌面朝下放在桌上。接着,一次从桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上。为了找到这张牌的正确位置,要将它与手中已有的牌从右到左地进行比较。无论什么时候,左手中的牌都是排好序的。

算法思想:
1.刚开始结果集只有一个元素
2.获取要插入的元素,和结果集相对比,如果比第一个元素大,则往后继续继续比较,如果比第一个元素小,则放在元素前面。
3.重复2操作直到全部元素排列完毕

算法图解:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Java代码:

public static void insertSort(int[] A) {
    int key = 0;
    int j = 0;
    for (int i = 1; i < A.length; i++) {
        //要插入的数
        key = A[i];
        for (j = i - 1; j >= 0 && key < A[j]; j--) {
            A[j + 1] = A[j];
        }
        A[j + 1] = key;
    }
}

插入排序是稳定的排序算法:
时间复杂度:O(n^2)
空间复杂度:O(1)
归并排序的思想:就是将两个有序的数组归并成一个更大的有序数组

那么将两个有序的数组合并成一个大的数组,你会怎么操作呢?

一般我们都是往一个数组里面插入另外一个数组,但是这样会带来插入排序
的一个问题,如果往已经排序好的结果集非常大,那么我们要插入最小元素所要移动的代价就非常大了,那么我们怎么来解决这个问题呢?

原地归并的抽象方法:
该方法先将所有的元素复制到aux[]中,然后再归并到a[]中。方法在指定第二个for循环的时候,用了4个条件判断,分别是左半边用尽(取右半边的元素)、右半边用尽(取左半边的元素)、右半边的元素比左半边的元素小(取右半边的元素)、右半边的元素比左半边的元素大(取左半边的元素)。

public static void merge(int[] a,int low,int mid,int hight) {
		int i=low,j=mid+1;
		int[] aux=new int[a.length];
		//int[] aux=a.clone();
		for(int k=low;k<=hight;k++) {
			aux[k]=a[k];
		}
		for(int k=low;k<=hight;k++) {
			if(i>mid) {
				a[k]=aux[j++];
			}else if(j>hight) {
				a[k]=aux[i++];
			}else if(aux[j]<aux[i]) {
				a[k]=aux[j++];
			}else {
				a[k]=aux[i++];
			}
		}
	}

那么我们有了这样的一个将两个数组合并成一个数组之后,我们就可以将一个大数组划分成n个小数组,然后小数组之间两两使用这个方法,就可以完成归并排序了,我们将这种归并排序称为自顶向下的归并排序

归并排序

自顶向下的归并排序

自顶向下的归并排序使用分治的思想,将大问题不断划分成小问题,小问题解决了,大问题也就解决了。

public static void sortTopToButton(int[] a,int low,int hight) {
		if(low>hight) {
			return;
		}
		int mid=low+(low+hight)/2;
		//将数组分为左右两边,分别进行归并排序
		sortTopToButton(a, low, mid);
		sortTopToButton(a, mid+1, hight);
		merge(a, low, mid, hight);
	}

在这里插入图片描述

当然了,我们也可以换个角度想问题,如果我们将数组一开始就两两划分,然后两两组合成我们需要得到的数组,这个种方法我们称之为自底向上的归并排序

自底向上的归并排序

public static void sortButtonToTop(int[] a) {
		int N=a.length;
		for(int i=1;i<N;i+=i) {
			for(int low=0;low<N-i;low+=i+i) {
				merge(a, low, low+i-1, Math.min(low+i+i-1, N-1));
			}
		}
	}

在这里插入图片描述

package sort;
/**
*@time 2020年4月26日:上午10:38:58
*@author Weirdo
*@version 1.0
**/
public class Merge {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}
	
	public static void merge(int[] a,int low,int mid,int hight) {
		int i=low,j=mid+1;
		int[] aux=new int[a.length];
		//int[] aux=a.clone();
		for(int k=low;k<=hight;k++) {
			aux[k]=a[k];
		}
		for(int k=low;k<=hight;k++) {
			if(i>mid) {
				a[k]=aux[j++];
			}else if(j>hight) {
				a[k]=aux[i++];
			}else if(aux[j]<aux[i]) {
				a[k]=aux[j++];
			}else {
				a[k]=aux[i++];
			}
		}
	}
	
	//自顶向下的归并排序
	public static void sortTopToButton(int[] a,int low,int hight) {
		if(low>hight) {
			return;
		}
		int mid=low+(low+hight)/2;
		//将数组分为左右两边,分别进行归并排序
		sortTopToButton(a, low, mid);
		sortTopToButton(a, mid+1, hight);
		merge(a, low, mid, hight);
	}
	
	//自底向上的归并排序
	public static void sortButtonToTop(int[] a) {
		int N=a.length;
		for(int i=1;i<N;i+=i) {
			for(int low=0;low<N-i;low+=i+i) {
				merge(a, low, low+i-1, Math.min(low+i+i-1, N-1));
			}
		}
	}

}

总结自:《算法》(第四版)2.2归并排序,大家可以看一下,收获良多

希尔排序

希尔排序是基于插入排序快速排序算法。对于大规模乱序数组插入排序很慢,因为他只会交换相邻的元素,因此元素只能一点一点的从数组的一端移动到另一端。如果最小的元素恰好在最末端,那么要挪到正确的位置需要移动n-1次。

希尔排序为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序
——《算法》(第四版,2.1.6希尔排序)

根据上面这段,我的理解就是,希尔排序就是通过一种手段,将原本的数组状态1转换成为另一种状态2,最后使用插入排序完成排序,而状态2的时候使用插入排序比状态1的时候使用插入排序更快。

希尔排序的思想就是使得数组中间隔为h的元素都是有序的(称为h有序数组)。换句话说就是将一个数组划分成h个小数组,而小数组内部是有序的。

算法思想:
我们将需要排序的乱序数组划分成h组
不断缩小h,直到h=1
h=1,即我们学的插入排序,h不断缩小的过程就是我们将数组从状态1转化成为状态2的操作

算法图解:
在这里插入图片描述
小数组之间使用排序:
在这里插入图片描述
在这里插入图片描述
然后将h缩小
在这里插入图片描述
小数组之间再进行排序
在这里插入图片描述
在这里插入图片描述
然后h=1
使用插入排序

希尔排序完成!!!

那么我们最开始使用的h怎么去确定呢?

在《算法 第四版》对于希尔排序的算法代码为:

package sort;
/**
*@time 2020年4月25日:下午7:40:23
*@author Weirdo
*@version 1.0
**/
public class Shell {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		int[] arr= {5,1,2,4,3};
		sort(arr);
		for(int i:arr) {
			System.out.print(i+" ");
		}

	}
	
	public static void sort(int[] a){
		int N = a.length;
		int h=1;
		//这里就是对h最大能取多大进行判定
		while(h<N/3){
			h=3*h+1; // 1,4,13,40
		}
		while(h>=1){
			for(int i=h;i<N;i++){
			//less(a[j],a[j-h]) --> a[j]<a[j-h]?true:false
				for(int j=i; j>=h && less(a[j],a[j-h]);j-=h){
					//交换
					exchang(a,j,j-h);
				}
			}
			h=h/3;
		}
	}

	private static boolean less(Integer i, Integer j) {
		// TODO Auto-generated method stub
		return i.compareTo(j)<0;
	}

	private static void exchang(int[] a, int j, int i) {
		// TODO Auto-generated method stub
		int temp=a[j];
		a[j]=a[i];
		a[i]=temp;
		
	}

	

}

和上面图解算法不同的地方在于,图解算法的h是成倍减小的,算法里的h是通过while()循环确定最大值,然后减少3倍,算法的思想都是一样的,通过划分数组,数组内部有序,缩小h

快速排序

使用了分治的思想

算法步骤:
1 从数列中挑出一个元素,称为 “基准”(pivot),
2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

算法图解:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一轮排序完毕,3左边的肯定小于3,3右边的肯定大于3
然后左右分别进行快速排序……

Java代码:

public static void quickSort(int[] a , int low,int high) {
        int start = low;
        int end = high;
        int temp = a[start];//标记值
 
        while (start < end){
            //通行条件:while()中的值为停止循环条件,即标记值更小就停止循环
            while (start < end && temp <= a[end]){
                end--;
            }
            //再次确认标记值是否大于后面的值
            if (temp > a[end]){
                a[start] = a[end];
                a[end] =temp;
            }
 
            while (start < end && temp >= a[start]){//标记值更大就停止循环
                start++;
            }
            if (temp < a[start]){
                a[end] = a[start];
                a[start] = temp;
            }
        }
       //当start >= end 时将跳出循环   
       //此时temp值一定会在数组的中间 即(a =2,1,3,4,6,5,), 左边比3小,右边比3大 
 
        if(start-1 > low ){
            quickSort(a,low,start-1);
        }
 
        if(end+1<high){
            quickSort(a,end+1,high);
        }
    }

初始化:a = [3, 5, 1, 4, 6, 2]
快排一次结果a--------[2, 1, 3, 4, 6, 5]
递归左
快排二次结果a--------[1, 2, 3, 4, 6, 5]
递归右
快排三次结果a--------[1, 2, 3, 4, 6, 5]
递归右
快排四次结果a--------[1, 2, 3, 4, 5, 6]

参考:https://blog.csdn.net/csdn_aiyang/article/details/73108606#%E5%9B%9B%E3%80%81%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F

总结

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值