插入排序和归并排序算法实现Java版

333 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

排序算法是软件开发中非常重要的一环。本期就来讨论关于算法在面试中会遇到的一些问题,其中包含个人一些理解,由于本人学识有限,难免会有纰漏之处,还望读者能多多包涵。

上一期我们讲了冒泡排序、选择排序和堆排序。本期继续讲解其他的排序算法。

插入排序

插入排序思想比较简单,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增 1 的有序表。插入排序的平均时间复杂度也是 O(n2)O(n^2),空间复杂度为常数阶 O(1)O(1),具体时间复杂度和数组的有序性也是有关联的。

插入排序中,当待排序数组是有序时,是最优的情况,只需当前数跟前一个数比较一下就可以了,这时一共需要比较 N-1 次,时间复杂度为 O(N)O(N)。最坏的情况是待排序数组是逆序的,此时需要比较次数最多,最坏的情况是 O(n2)O(n^2)

image.png

插入排序过程用文字描述就是(从小到大排序):首先位置1上的数和位置0上的数进行比较,如果位置1上的数小于位置0上的数,将位置0上的数向后移一位,将1插入到0位置,否则不处理。位置k上的数和前面的k-1个数依次进行比较,如果位置K上的数更小,将之前的数向后移位,最后将位置k上的数插入不满足条件点,反之不处理。

Java实现如下:

public static void insertSort(int[] array) {
		int temp,j;
	    for(int i=1;i<array.length;i++)
	    {
	        if(array[i-1]>array[i])  //如果前面比自己大
	        {
	            temp=array[i];
	            for(j=i-1;j>=0 && array[j]>temp;j--) //那么自己与前面所有的进行比较
	            {
	                array[j+1]=array[j];
	            }
	            array[j+1]=temp;
	        }
	        
	    }
	    //输出
	    for(int k=0;k<array.length;k++) {
	    	System.out.print(array[k]+" ");
	    }

	}
public static void main(String[] args) throws IOException { 
		
		int[] array= {5,0,1,2,8,4};
		insertSort(array);
}

在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

归并排序

归并排序(Merge sort)是建立在归并操作上的一种有效、稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

文字描述过程:首先让数组中的每一个数单独成为长度为1的区间,然后两两一组有序合并,得到长度为2的有序区间,依次进行,直到合成整个区间。

image.png

归并排序适用于数据量大,并且对稳定性有要求的场景。

Java代码实现版本:

        public static void MergeSort(int[] k)  {  
	    int i,next,left_min,left_max,right_min,right_max; 
	    int n=k.length;
	    //动态申请一个与原来数组一样大小的空间用来存储
	    int[] temp=new int[n];
	    //逐级上升,第一次比较2个,第二次比较4个,第三次比较8个。。。  
	    for(i=1; i<n; i*=2)  
	    {  
	        //每次都从0开始,数组的头元素开始  
	        for(left_min=0; left_min<n-i; left_min = right_max)  
	        {  
	            right_min = left_max = left_min + i;  
	            right_max = left_max + i;  
	            //右边的下标最大值只能为n  
	            if(right_max>n)  
	            {  
	                right_max = n;  
	            }  
	            //next是用来标志temp数组下标的,由于每次数据都有返回到K,  
	            //故每次开始得重新置零  
	            next = 0;  
	            //如果左边的数据还没达到分割线且右边的数组没到达分割线,开始循环  
	            while(left_min<left_max&&right_min<right_max)  
	            {  
	                if(k[left_min] < k[right_min])  
	                {  
	                    temp[next++] = k[left_min++];  
	                }  
	                else  
	                {  
	                    temp[next++] = k[right_min++];  
	                }  
	            }  
	            //上面循环结束的条件有两个,如果是左边的游标尚未到达,那么需要把  
	            //数组接回去,可能会有疑问,那如果右边的没到达呢,其实模拟一下就可以  
	            //知道,如果右边没到达,那么说明右边的数据比较大,这时也就不用移动位置了  

	            while(left_min < left_max)  
	            {  
	                //如果left_min小于left_max,说明现在左边的数据比较大  
	                //直接把它们接到数组的min之前就行  
	                k[--right_min] = k[--left_max];   
	            }  
	            while(next>0)  
	            {  
	                //把排好序的那部分数组返回该k  
	                k[--right_min] = temp[--next];        
	            }  
	        }  
	    }  
	    
	    //输出
	    for (int j = 0; j < n; j++) {
			System.out.print(k[j]+" ");
		}
	}  
        public static void main(String[] args) throws IOException { 
		
		int[] array= {5,0,1,2,81,-4,21,9};
		MergeSort(array);
	}

本代码没有采用递归方式,而是使用迭代方式。这样可以节省深度为log2Nlog_2N的栈空间。

当有 n 个记录时,需进行 lognlog n 轮归并排序,每一轮归并,其比较次数不超过 n,元素移动次数都是 n,因此,归并排序的时间复杂度为 O(nlogn)O(nlogn)。归并排序时需要和待排序记录个数相等的存储空间,所以空间复杂度为 O(n)O(n)

归并排序算法,是目前为止最重要的算法之一,是分治法的一个典型应用,需要牢记。