1. 选择排序
1.1 选择排序算法原理
以序列首元素位置为基准位置,每次将该基准位置的元素和后面元素逐个进行比较,挑选最大或者最小的那个数放在基准位置上,一趟比较结束后,然后将基准位置设置为该位置的下一位置,重复上述操作,直到基准被安排在序列的最后一个位置时,此时序列已经排列完成。
1.2 选择排序过程分析
例如:48,12,61,3,5,19,32,7,24,31
说明:按照从小到大的顺序进行排序,其中每条彩色线条为两数之间的比较,其条数为比较次数。
①第一趟比较:
当基准所在位置下标0进行完第一次循环比较后,可以看到此时min在下标为3的位置,所以要将下标为0的元素和下标为3的元素进行交换,也就是48和3交换,此时待排数组变成3,12,61,48,5,19,32,7,24,31,第一趟选择排序比较结束。
②第二趟比较:
此时基准在下标为1的位置,初始化min=1,对应的元素为12,12分别与后面的元素作比较,当比较到下标为4时,对应的元素为5,12>5,所以min=4,然后5再跟后面的元素作比较,一直比较到最后一个元素,都没有比5小的元素,所以基准所在位置1的对应元素要和min所在位置4的对应元素交换,也就是12和5交换,然后待排数组变成3,5,61,48,12,19,32,7,24,31,第二趟选择排序比较结束。
③第三趟比较:
此时基准在下标为2的位置,初始化min=2,对应的元素为61,61分别与后面的元素作比较,当比较到下标为3时,对应的元素为48,61>48,所以min=3。然后48再和后面的元素进行比较,当比较到下标为4时,对应的元素为12,48>12,所以min=4。然后12再跟后面的元素进行比较,当比较到下标为7时,对应的元素为7,12>7,所以min=7。然后7再跟后面的元素进行比较,一直比较到最后一个元素,都没有比7小的元素,所以基准所在位置2的对应元素要和min所在位置7的对应元素交换,也就是61和7交换,然后待排数组变成3,5,7,48,12,19,32,61,24,31,第三趟选择排序比较结束。
以此类推,一直比较到基准在下标为8的位置,执行好比较之后待排数组就是排序好的数组了,得到排序数组为3,5,7,12,19,24,31,32,48,61。
1.3 选择排序核心代码及说明
/*选择排序算法*/
/*比如待排数组为1 5 2*/
/*第一趟排序如下*/
/*此时min=1,首先第一次循环就以第一个元素1为标志,1分别与数组中其他元素比较*/
/*如果有元素比1小,那么就把那个元素的下标存到min中*/
/*一直比较到数组最后一个元素,然后把min下标对应的元素与第一个元素交换*/
/*因为待排序列中第一个元素1与其他元素比较,都没有比他小的值,所以不需要交换,1就是排序的第一个元素*/
/*第二趟排序如下*/
/*此时min=2,因为排序序列的第一个元素我们在第一趟中已经找到了最小值去填充这个位置,所以第二趟排序我们需要找第二个元素*/
/*第二个元素为5,要和后面的数组元素进行比较,跟第一趟排序一样,如果后面的元素大于2,那么min就等于那个元素所对应的下标*/
/*一直比较到最后一个元素,然后将min下标对应的元素与第二个元素交换*/
/*待排数组中5>2,所以2要跟5交换位置,此时数组变成1 2 5*/
/*因为待排数组的长度为3,只需要进行3-1=2次排序,所以不需要进行第三趟排序了,此时就是给定待排数组1 5 2的排序好的数组1 2 5了*/
/*对于其他待排长度为len的数组,我们需要进行len-1趟排序*/
/*因为我们有两层循环,一层循环是用来标志排序数组当前要找到的位置的元素的下标,一层循环用来比较大小,所以时间复杂度为O(N²)*/
/*由于使用了中间变量b,所以空间复杂度为O(1)*/
import java.util.Arrays;
public class selectionSort {
//方法1
public static void SelectionSort(int[] arr) {
if(arr==null || arr.length <2) { //假设数组为空或者数组中只有一个数,直接返回
return;
}
// 0 ~ N-1 找到最小值,在哪,放到0位置上
// 1 ~ n-1 找到最小值,在哪,放到1 位置上
// 2 ~ n-1 找到最小值,在哪,放到2 位置上
for(int i=0;i<arr.length-1;i++) { //从下标为0的位置开始
int minIndex=i;
for(int j=i+1;j<arr.length;j++) { //与数组中其他的数依次对比
minIndex =arr[j]<arr[minIndex] ? j:minIndex;
}
swap(arr,i,minIndex);
}
}
public static void swap(int[] arr,int i,int j) {
//交换函数
int b;
b= arr[i];
arr[i]=arr[j];
arr[j]=b;
}
public static void main(String[] args) {
int[] arr= {1,6,8,2};
System.out.println(Arrays.toString(arr));//打印排序前的数组
SelectionSort(arr);
System.out.print(Arrays.toString(arr)); //打印排序后的数组
}
}
2. 冒泡排序
2.1 冒泡排序算法原理
比较相邻的元素,将大的元素交换到右边的位置,重复多次后,最大元素就“冒泡”到列表的最后一个位置。第二遍将第二大元素沉下去,N-1遍后结束。
2.2 冒泡排序过程分析
例如:48,12,61,3,5
说明:按照从小到大的顺序进行排序,其中每条彩色线条为两数之间的比较,其条数为比较次数。
①第一趟比较:
当基准所在位置下标0进行完第一次循环比较后,可以看到此时max在一次次的交换中来到了数组中的最后一个位置,下标为4,也就是N-1(N为待排数组的元素个数)。此时待排数组变成[12 48 3 5 61],第一趟冒泡排序比较结束。
②第二趟比较:
此时基准在下标为0的位置,初始化max=12,12与后面的元素作比较,12<48,所以此时max=48,不交换位置;接着max继续与下一个数比较,48>3,交换48和3;接着48继续与下一个数比较,48>5,交换48和5的位置,此时48已经来到了倒数第二个位置,因为最后一个数已经在第一次比较中确定位置,因此48不需要再跟它比较。此时待排数组变成[12 3 5 48 61],第二趟选择排序比较结束。
③第三趟比较:
此时基准在下标为0的位置,初始化max=12,12与后面的元素作比较,12>3,所以交换12和3接着max继续与下一个数比较,12>5,交换12和5,此时12已经来到了倒数第3个位置,后面的数已经排序完成,因此12不需要再跟它们比较。此时待排数组变成[3 5 12 48 61],第三趟选择排序比较结束。
④第四趟比较:
此时基准在下标为0的位置,初始化max=3,3与后面的元素作比较,5>3,此时max=5,不交换位置。此时5已经来到了倒数第4个位置,后面的数已经排序完成,因此5不需要再跟它们比较。此时待排数组变成[3 5 12 48 61],第四趟选择排序比较结束,此时进行了4次比较,也就是N-1次,因此冒泡排序结束,得到排序数组为[3 5 12 48 61]。
2.3 冒泡排序核心代码
import java.util.Arrays;
public class bubbleSort {
public static void BubbleSort(int[] arr) {
if(arr ==null || arr.length<2) {
return;
}
// 0 ~ N-1
// 0 ~ N-2
// 0 ~ N-3
for(int i = arr.length - 1 ; i > 0 ; i-- ) {
for(int j=0 ; j<i ; j++) {
if(arr[j] > arr[j+1]) {
swap(arr,j,j+1);
}
}
}
}
public static void swap(int[] arr,int i,int j) {
int b;
b=arr[i];
arr[i]=arr[j];
arr[j]=b;
}
public static void main(String[] args) {
int[] arr= {1,8,6,7,2,3};
System.out.println(Arrays.toString(arr));
BubbleSort(arr);
System.out.print(Arrays.toString(arr));
}
}
3. 插入排序
3.1 插入排序算法步骤
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
3.2 插入排序算法步骤
①第一趟比较:
此时待插入元素的下标为i=1,对应的元素为12,令b=12,接着12跟前面的元素进行比较,也就是下标j=i-1=0对应的元素48,因为48>12,所以需要把48向后面挪一个位置,也就是48要移动到j+1=1下标的地方。因为此时j=0,所以循环结束,将b=12插入到j+1=0的位置(因为循环解释的条件为j<=0或b<a[j],然后j--,所以此时j=-1,j+1=0),第一趟插入排序比较结束。此时待排数组为[12,48,61,3,5,19,32,7,24,31]。
②第二趟比较:
此时待插入元素的下标为i=2,对应的元素为61,令b=61,接着61跟前面的元素进行比较,也就是下标j=i-1=1对应的元素48,因为61>48,所以循环结束,第二趟插入排序比较结束。
此时待排数组为[12,48,61,3,5,19,32,7,24,31]。
③第三趟比较:
此时待插入元素的下标为i=3,对应的元素为3,令b=3,接着3跟前面的元素进行比较,也就是下标j=i-1=2对应的元素61,因为61>3,所以需要把61向后面挪一个位置,也就是61要移动到j+1=3下标的地方。然后3继续跟前面的元素进行比较,也就是下标j=j-1=1对应的元素48,因为48>3,所以需要把48向后面挪一个位置,也就是48要移动到j+1=2下标的地方。然后3继续跟前面的元素进行比较,也就是下标j=j-1=0对应的元素12,因为12>3,所以需要把12向后面挪一个位置,也就是12要移动到j+1=1下标的地方。因为此时已经比较到了数组的第一个元素,前面没有元素了,所以循环结束,将b=3插入到j+1=0的位置(因为循环解释的条件为j<=0或b<a[j],然后j--,所以此时j=-1,j+1=0),第三趟插入排序比较结束。
此时待排数组为[3,12,48,61,5,19,32,7,24,31]
3.3 插入排序核心代码及说明
import java.util.Arrays;
public class InsertionSort {
public static void insertSort(int[] arr) {
if(arr==null || arr.length<2) {
return;
}
// 0 ~ i 做到有序
for(int i=0;i<arr.length;i++) {
for(int j = i-1 ; j >= 0 ;j--) {
if(arr[j]>arr[j+1]) {
swap(arr,j,j+1);
}
}
}
}
public static void swap(int[] arr,int i,int j) {
int b;
b= arr[i];
arr[i]=arr[j];
arr[j]=b;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr = {1,5,7,3,4,2,9};
System.out.println(Arrays.toString(arr));
insertSort(arr);
System.out.println(Arrays.toString(arr));
}
}
3.4 二分法改进插入排序
折半插入排序算法的时间复杂度:O(nlogn)
折半插入排序利用二分法的思想,在一个有序的序列中,找到新元素在该序列中的位置,然后插入。如图1所示,共有n个元素,前i个元素已经是有序序列,现在要将第i个元素插入其中。折半插入排序需要做两步工作:找到待插入元素的位置、插入。
首先要定义两个指针(不是语法里面的指针,是下标的意思) low 和 high 用于寻找 a[i] 的插入位置,low 指向a[0],high 指向 a[i-1] ,中点 mid=(low+high)/2,
如图2所示二分法的思想是,比较a[i]与a[mid]的大小,若 a[i]>a[mid] ,说明a[i]的位置应该在 mid-high 之间,将区间 [low,high] 缩短为 [mid+1,high] ,令指针 low=mid+1 ;若 a[i]<=a[mid] ,说明a[i]的位置应该在 low-mid 之间,将区间压缩为[low,mid-1],令指针high=mid-1。每次折半之后,a[i]的位置应该在 [low,high] 之间。
如此循环,low与high渐渐靠近,直到 low>high 跳出循环,a[i]的位置找到,low即为a[i]应该放置的位置。
找到a[i]的位置之后进行插入,先将 a[low]~a[i-1] 这些元素向后平移一个元素的位置,然后将a[i]放到low位置。
代码如下:
void BinSort(int *a,int n)
{
int i,j;
for(i=1;i<n;i++) /*开始 以a[0]作为有序序列,从a[1]开始找到当前元素a[i]应该放置的位置 */
{
int low=0,high = i-1,mid;/*每次寻找a[i]的位置,都要更新这些数据 */
while(low<=high) /*二分思想循环寻找a[i]的位置 */
{
mid = (low+high) / 2;
if(a[i]<=a[mid])
high = mid - 1; /*high指针减小 */
else
low = mid + 1; /*low指针增加 */
} /*循环结束,low就是a[i]应该放置的位置 */
int temp = a[i];
for(j=i;j>low;j--) /*将元素向后平移*/
a[j] = a[j-1];
a[low] = temp; /*将元素temp = a[i] 放置到low位置 */
}
}
其他相关文章参考:
算法复杂度:快速搞懂算法复杂度 - 掘金 (juejin.cn)
二分法求解问题:对半切快速解题——二分法 - 掘金 (juejin.cn)
作者:ee在努力
链接:https://juejin.cn/post/7236634496766722108
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。