数据结构–关于插入排序
理解插入排序的过程
1.对于任意一个数组序列,可以是有序的,也可以是无序的,我们先取这个数组的第一个元素,将他视为一个有序数组(只有一个元素的数组当然可以认为是有序的啦),然后将第二个元素与他比较,如果比他大,则依旧保持原来位置,否则,交换位置,这样我们就可以得到前两个元素是有序的数组。
2.我们将第三个元素与第二个元素比较,如果比他大,则依旧保持原来位置,否则,交换位置,这里是和上面一样,但是我们还要比较第一个元素与第二个元素的大小,这是针对如果第三个和第二个交换的话,当然了,如果第三个元素和第二个元素没有发生交换,说明第三个元素小于第二个元素,同样也就大于比第二个元素小的第一个元素了(这里加下划线的我们一会在代码部分会有说明,或者说代码和他有关系)
3.以此类推,第四个元素,第五个元素等等直到最后一个元素
关于插入排序的代码表示
刚刚我们说了插入排序的过程,我个人认为亲手写出代码可以增强对概念的理解
public class Demo {
public static void insertFunction(int[] array){
int m = 0;
//每添加一个元素,或者说每一个新元素都要与前面的元素进行比较,下标为i的每次递增意味着一次数组的扩张
for(int i=0;i<array.length;i++){
for(int j=i;j>0;j--){//这里是从后往前比较的
if(array[j]<array[j-1]){//比较下标为J的元素与前一个元素的大小,判断是否要换位
m=array[j-1];
array[j-1]=array[j];
array[j]=m;
}else
break;//这里的break就和我们刚刚划线部分有关啦
}
}
}
public static void main(String[] args) {
int[] arr={1,23,45,9,25,888,901,3,89,19};
Demo.insertFunction(arr);
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]);
System.out.print(" ");
}
}
}
我们在划线部分说了,如果没有交换位置,那么加上新元素构成的新数组也是一个有序数组。我在这里说明这个break的原因是我本人比较笨,在学这个的时候,一开始没有理解为什么是用break结束循环体而不是用continue来终结这次循环,开启下次循环,原因就是我们将新元素与原来的有序数组中的元素比较是从后往前的,所以当新元素比某个元素大时,一定也是比这个元素之前的元素都大。例如:
假设我们有一个数组array={1,2,3,4,5,6,17,19,16}
我们前面8个元素都是有序的,最后一个元素16比19小,那么按照代码,则换位,变成{1,2,3,4,5,6,17,16,19}
然后继续循环,j–,那么再次比较,发现16小于17,再次换位,变成{1,2,3,4,5,6,16,17,19},我们继续循环,j–,
此时16比6大,那么我们可以就可以结束循环体了,因为比6大的16一定会比6前面的数字大。
关于时间复杂度
既然是谈数据结构,那么一定离不开复杂度,插入排序的时间和空间复杂度是多少?
首先是时间复杂度,我们知道插入排序干两件事,一个是比较,另一个是插入(移动),比较这件事我们理解,最好情况当然是数组已经是有序的了,我们每次只需要比较一次,即一个元素与前一个元素的大小,发现他比前一个元素大,只需要这一次就可以,如果数据规模是2一共就比较1次,是3就一共比较2次,数据规模是n就一共比较n-1次,那么插入或者说移动呢,没有数据移动,也就是0,所以最好情况下是O(n)。
那么最坏情况就是顺序完全颠倒,比较:每次新元素都要与前面所有元素比较一次。第二个元素要比较1次,第三个元素要比较2次,第四个元素要比较三次,如此这般,最后相加,可以知道这是等差数列求和,1+2+3+4+…+(n-1),结果一共是n(n-1)/2,移动:每一次比较之后都有数据的交换移动,所以次数和比较的次数一样,都是n(n-1)/2,他们相加得到n(n-1),所以最坏情况复杂度是O(n2)。由于大O表示法说的是最糟糕的形式,所以认为插入排序的时间复杂度是O(n2)。
关于空间复杂度
现在我们考虑空间复杂度。本人在考虑空间复杂度时参考了一些博客,但是我个人感觉关于空间复杂度这部分,在插入排序中要么没讲,要么直接给出答案O(1),要么就是讲了但是很笼统,或者说不是以初学者可以看懂的方式讲解的,当然也许是我太笨。
首先我们要明白空间复杂度是个啥,有人笼统的说一个程序的空间复杂度是指运行完一个程序所需内存的大小,这点我并不太认同,因为要是较真的话,数据本身的存储也是要分配内存的啊,那么一个规模为n的数组,最起码的空间复杂度也是O(n),怎么会是O(1)呢?
百度百科的定义如下:空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。
好,我们明白了第一件事:空间复杂度是算法在运行过程中占用的存储空间大小的度量。
第二件要明白的事情是程序执行时所需存储空间包括以下两部分。
(1)固定部分。这部分空间的大小与输入/输出的数据的个数多少、数值无关。主要包括指令空间(即代码空间)、数据空间(常量、简单变量)等所占的空间。这部分属于静态空间。
(2)可变空间,这部分空间的主要包括动态分配的空间,以及递归栈所需的空间等。这部分的空间大小与算法有关。
那么我们可以认为,诸如数组为所分配的空间属于静态空间或者说固定空间,而我们研究的算法的空间复杂度的空间实际上是可变空间,所以一句话概括,我们可以认为空间复杂度是运行一个程序所需要临时占用的可变空间。
现在我们研究插入排序的空间复杂度规模。
这里就三个变量i,j和m,就是说算法为这三个变量分配了空间,其中我们在代码
for(int i=0;i<array.length;i++){
for(int j=i;j>0;j--){//这里是从后往前比较的
if(array[j]<array[j-1]){//比较下标为J的元素与前一个元素的大小,判断是否要换位
m=array[j-1];
array[j-1]=array[j];
array[j]=m;
}else
break;//这里的break就和我们刚刚划线部分有关啦
}
}
这里面虽然看起来发生许多次j–,i++,m的赋值,这些数值或者数据变化但是实际上空间还是没有变化的。
所以实际上与算法有关的就这三个空间,所以空间复杂度是常数3,大O表示法表示为O(1)。
以上就是我这次学习插入排序的感悟,也许有错误和不足之处,欢迎各位斧正和交流。
参考资料:
插入排序及其复杂度分析 - JollyWing - 博客园 (cnblogs.com)
选择排序,插入,快排,冒泡排序的时间空间复杂度详解_SamGeren的博客-CSDN博客