插入排序(思路分析)
插入排序算法介绍:
插入式排序属于内部排序法,是对欲排序的元素以插入的方式找到该元素的适当位置,以达到选择排序的目的
插入排序算法思想:
插入排序(Insertion Sorting)的基本思想是: 把n个待排序的元素看成一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含了n-1个元素,排序过程中每次从无序表中取出第一个元素,依次和有序表中的所有元素进行比较,将它插入到有序表中的适当位置,使之成为新的有序表
插入排序算法思路图:
插入排序思路分析(以升序排序为例):
我们拿到一个原始数组,然后将这个数组中的第一个元素看成是一个有序表 ( 我们这里的有序表和无序表都是我们自己为了解决为题抽象出来的,实际是不存在的,我们的无序表前面就是有序表 ) 因为只要一个元素那么肯定是有序的,然后将除了第一个元素以外的元素就认为是组成了一个无序表,然后就开始第一轮排序,我们先拿出无序表中的第一个元素,与它前面的有序表中的最后一个元素进行比较,如果有序表中最后一个元素比无序表中的第一个元素小,那么就说明无序表中的第一个元素的位置是对的,不需要改变,因为它前面的有序表中的最后一个元素(也就是最大的元素)比自己小,那么自己比有序表中最大的元素还大,所以我们的无序表中的第一个元素就要插入在有序表的最后一个元素的位置(其实这个位置没有发生改变),而如果无序表中的第一个元素比有序表中的最后一个元素的值小,那么自然就需要让这个有序表中最后一个元素进行后移
- 但是在后移之前我们要提前将我们的无序表中的第一个元素保存到一个临时变量中,然后我们一旦遇到比自己大的值,那么就要让大的值后移,这个时候注意:我们将有序表中的最后一个元素后移之后我们我们就要使用待排序元素和我们的有序表中的最后一个元素的前一个位置的元素比较,所以这个时候我们就要将我们的有序表中的最后一个位置记录下来,这个位置表示的就是待插入位置前一个位置,如果判断出来这个位置的值比较大,那么就让这个位置的值后移,然后让记录的索引前移,这个时候由于有序表中只有一个元素,所以这个时候我们的有序表中的唯一一个元素的索引前移之后就到了-1上,这个时候就是我们已经和有序表中的所有的元素进行了比较了,这个时候说明我们的待排序元素的值比我们的有序表中的所有的值都要小,这个时候我们就要将我们的待插入元素的值插入到有序表的最前面
- 注意: 我们插入的位置一定是临时变量记录的位置 +1,因为我们的待排序的值比临时变量记录的位置的值大,那么肯定是排在临时变量记录的位置的下一个位置上
- 如果我们的临时变量中记录的值大,那么就让临时变量记录的值后移,不用担心后移之后会让本次待排序位置的值失效,因为待排序位置的值一开始就被记录到临时变量insertVal中
- 因为这里临时变量记录的索引前移之后可能会出现移动到 -1上,naem这个时候一旦移动到-1上我们就要退出循环,因为我们的循环中的判断条件中是要临时变量记录的索引位置的值和待排序元素值比较的,如果临时变量记录的索引位置 < 0 ,naem就会出现数组角标越界,所以我们要判断: 如果临时变量记录的索引< 0,那么就会出现数组下标越界,所以我们就要判断如果临时变量记录的索引位置 < 0,就要退出循环,这个时候就没有比较再比较了,我们在有序表中第一个元素(也就是有序表中的最小元素值最小的元素)都比我们的待排序元素大,这个时候我们的待排序元素就要到有序表中的第一个位置去,也就是当前的索引位置 +1的位置(这个时候索引由于和第一个位置元素比较完之后已经跑到了 -1上去)
- 注意: 我们插入的位置一定是临时变量记录的位置 +1,因为我们的待排序的值比临时变量记录的位置的值大,那么肯定是排在临时变量记录的位置的下一个位置上
在循环中我们不应该出现变量的声明和赋值一起出现的情况:
我们应该将变量的声明写在循环的外面以提高效率
- 如果我们将变量的声明也写在循环的里面那么我们每次循环的时候都会重新创建这个变量
选择排序算法和插入排序算法和冒泡排序的效率的比较:
我们在插入排序中一旦从后往前在有序表中找到比待插入元素大的值,那么该有序表中的对应大的值就要后移,这里我们每当执行一次后移操作就会进行一次赋值操作,并且如果遇到完全逆序的情况这个时候每一次的待排序元素都要和前面的有序表中的所有的元素进行交换位置
- 但是注意:我们这里其实是一种优化过的冒泡排序,我们这里是进行了一个后移,但是这个时候执行的效率还是很低,因为每次比较都是要进行一次赋值操作,就有些类似于冒泡排序,我们的冒泡排序在完全逆序的情况下就是比较一下就可能交换一次,并且在冒泡排序的时候我们直接是执行的交换而不是后移,所以我们要进行两次赋值操作,所以我们的插入排序算法的执行效率比我们的冒泡排序的效率高
而选择排序中每次都是找最小值,都要将数组遍历一次,但是在选择排序中交换时整体发生在内层for循环之外的, 在内层for循环之中只是进行了修改,而对于插入排序则是在内层for循环中进行了数组元素的移动,我们认为数组赋值的速度比变量赋值的速度要慢,所以我们认为在完全逆序(或者说数组混乱度比较大)的情况下我们的插入排序的速度是比选择排序的速度要慢的
- 但是我们的插入排序如果找到带插入位置之后就会提前终止这一趟排序,所以在我们的数组趋于有序的情况下我们的插入排序算法的执行效率甚至要比一些nlogn阶的排序算法的执行效率都要高
结论: 插入排序算法执行效率 > 选择排序算法执行效率 > 冒泡排序算法执行效率
- 但是这个只是一般情况之下的
- 如果在数组中的元素为 6,1,2,3,4,5的情况之下这个时候我们的插入排序其实也就是要将我们的整个待排序数组都遍历一遍,这个时候由于数组元素赋值要比临时变量赋值满,所以我们这种情况之下选择排序的效率相对来讲要高一些
补充:
-
我们之前讲过的冒泡排序和我们的选择排序都是从第一个位置开始去操作,但是这个时候我们的插入排序是从我们的无序表中的第一个元素开始操作的 --> 而我们的无序表中的第一个元素其实就是我们待排序数组中的第二个元素 —> 所以我们的冒泡排序和选择排序算法的外层for循环都是从索引为0的位置开始到< length-1的位置,刚好length -1轮, 而我们的插入排序算法的外层for循环是从索引位置为1的位置开始到 <length的位置的,这个时候也是刚刚好 length -1轮
-
我们的直接插入排序中的待插入元素肯定是要和待插入位置的前一个位置的元素进行比较的 (如果是升序排序的话)
算法核心
其实直接插入排序就是将待排序序列看做是一个有序表和一个无序表, 每次就是使用无序表中的第一个元素来和有序表中从后到前进行比较, 知道比较到自己该存在的位置即可
- 算法难点: 每次比较之前都先将无序表中的第一个元素(也就是要比较的元素)存储到一个临时变量中, 因为开始比较的时候是从有序表中最后一个元素开始比较的, 这个时候我们比较的时候如果有序表中的最后一个元素比我们的无序表中的第一个元素大,那么这个时候我们直接让我们的有序表中的最后一个元素后移, 直接覆盖我们的无序表中的第一个元素,也就是我们当前要比较的元素, 这个也就是为什么我们要先将我们的无序表中的第一个元素, 也就是要比较的元素存储下来了, 其实就是这个位置的元素可能会"失真"
根据算法核心所得:
什么是有需要临时存储一个值?
- 如果这个值是我们的数组或者是其他存储数据的容器中的一个值, 此时我们需要这个值的时候, 我们要么就要存储这个值在数组中的索引, 或者是在容器中的位置, 或者就是我们要直接获取到这个值然后存储到我们的临时变量中
- 如果这个值是我们数组或者是其他存储数据的容器中的一个值, 甚至是变量中存储的一个值, 这个值如果我们后面还要使用到, 也就是对于我们而言还是有作用的, 如果这个时候这个值要被覆盖, 比如这个索引位置的值要换成其他值, 这个时候我们就要先将这个值暂时存储到一个临时变量中, 也就是对于我们要使用的即将要"失真"的值也是需要临时变量来存储# 插入排序(思路分析)
插入排序算法介绍:
插入式排序属于内部排序法,是对欲排序的元素以插入的方式找到该元素的适当位置,以达到选择排序的目的
插入排序算法思想:
插入排序(Insertion Sorting)的基本思想是: 把n个待排序的元素看成一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含了n-1个元素,排序过程中每次从无序表中取出第一个元素,依次和有序表中的所有元素进行比较,将它插入到有序表中的适当位置,使之成为新的有序表
插入排序算法思路图:
插入排序思路分析(以升序排序为例):
我们拿到一个原始数组,然后将这个数组中的第一个元素看成是一个有序表 ( 我们这里的有序表和无序表都是我们自己为了解决为题抽象出来的,实际是不存在的,我们的无序表前面就是有序表 ) 因为只要一个元素那么肯定是有序的,然后将除了第一个元素以外的元素就认为是组成了一个无序表,然后就开始第一轮排序,我们先拿出无序表中的第一个元素,与它前面的有序表中的最后一个元素进行比较,如果有序表中最后一个元素比无序表中的第一个元素小,那么就说明无序表中的第一个元素的位置是对的,不需要改变,因为它前面的有序表中的最后一个元素(也就是最大的元素)比自己小,那么自己比有序表中最大的元素还大,所以我们的无序表中的第一个元素就要插入在有序表的最后一个元素的位置(其实这个位置没有发生改变),而如果无序表中的第一个元素比有序表中的最后一个元素的值小,那么自然就需要让这个有序表中最后一个元素进行后移
- 但是在后移之前我们要提前将我们的无序表中的第一个元素保存到一个临时变量中,然后我们一旦遇到比自己大的值,那么就要让大的值后移,这个时候注意:我们将有序表中的最后一个元素后移之后我们我们就要使用待排序元素和我们的有序表中的最后一个元素的前一个位置的元素比较,所以这个时候我们就要将我们的有序表中的最后一个位置记录下来,这个位置表示的就是待插入位置前一个位置,如果判断出来这个位置的值比较大,那么就让这个位置的值后移,然后让记录的索引前移,这个时候由于有序表中只有一个元素,所以这个时候我们的有序表中的唯一一个元素的索引前移之后就到了-1上,这个时候就是我们已经和有序表中的所有的元素进行了比较了,这个时候说明我们的待排序元素的值比我们的有序表中的所有的值都要小,这个时候我们就要将我们的待插入元素的值插入到有序表的最前面
- 注意: 我们插入的位置一定是临时变量记录的位置 +1,因为我们的待排序的值比临时变量记录的位置的值大,那么肯定是排在临时变量记录的位置的下一个位置上
- 如果我们的临时变量中记录的值大,那么就让临时变量记录的值后移,不用担心后移之后会让本次待排序位置的值失效,因为待排序位置的值一开始就被记录到临时变量insertVal中
- 因为这里临时变量记录的索引前移之后可能会出现移动到 -1上,naem这个时候一旦移动到-1上我们就要退出循环,因为我们的循环中的判断条件中是要临时变量记录的索引位置的值和待排序元素值比较的,如果临时变量记录的索引位置 < 0 ,naem就会出现数组角标越界,所以我们要判断: 如果临时变量记录的索引< 0,那么就会出现数组下标越界,所以我们就要判断如果临时变量记录的索引位置 < 0,就要退出循环,这个时候就没有比较再比较了,我们在有序表中第一个元素(也就是有序表中的最小元素值最小的元素)都比我们的待排序元素大,这个时候我们的待排序元素就要到有序表中的第一个位置去,也就是当前的索引位置 +1的位置(这个时候索引由于和第一个位置元素比较完之后已经跑到了 -1上去)
- 注意: 我们插入的位置一定是临时变量记录的位置 +1,因为我们的待排序的值比临时变量记录的位置的值大,那么肯定是排在临时变量记录的位置的下一个位置上
在循环中我们不应该出现变量的声明和赋值一起出现的情况:
我们应该将变量的声明写在循环的外面以提高效率
- 如果我们将变量的声明也写在循环的里面那么我们每次循环的时候都会重新创建这个变量
选择排序算法和插入排序算法和冒泡排序的效率的比较:
我们在插入排序中一旦从后往前在有序表中找到比待插入元素大的值,那么该有序表中的对应大的值就要后移,这里我们每当执行一次后移操作就会进行一次赋值操作,并且如果遇到完全逆序的情况这个时候每一次的待排序元素都要和前面的有序表中的所有的元素进行交换位置
- 但是注意:我们这里其实是一种优化过的冒泡排序,我们这里是进行了一个后移,但是这个时候执行的效率还是很低,因为每次比较都是要进行一次赋值操作,就有些类似于冒泡排序,我们的冒泡排序在完全逆序的情况下就是比较一下就可能交换一次,并且在冒泡排序的时候我们直接是执行的交换而不是后移,所以我们要进行两次赋值操作,所以我们的插入排序算法的执行效率比我们的冒泡排序的效率高
而选择排序中每次都是找最小值,都要将数组遍历一次,但是在选择排序中交换时整体发生在内层for循环之外的, 在内层for循环之中只是进行了修改,而对于插入排序则是在内层for循环中进行了数组元素的移动,我们认为数组赋值的速度比变量赋值的速度要慢,所以我们认为在完全逆序(或者说数组混乱度比较大)的情况下我们的插入排序的速度是比选择排序的速度要慢的
- 但是我们的插入排序如果找到带插入位置之后就会提前终止这一趟排序,所以在我们的数组趋于有序的情况下我们的插入排序算法的执行效率甚至要比一些nlogn阶的排序算法的执行效率都要高
结论: 插入排序算法执行效率 > 选择排序算法执行效率 > 冒泡排序算法执行效率
- 但是这个只是一般情况之下的
- 如果在数组中的元素为 6,1,2,3,4,5的情况之下这个时候我们的插入排序其实也就是要将我们的整个待排序数组都遍历一遍,这个时候由于数组元素赋值要比临时变量赋值满,所以我们这种情况之下选择排序的效率相对来讲要高一些
补充:
-
我们之前讲过的冒泡排序和我们的选择排序都是从第一个位置开始去操作,但是这个时候我们的插入排序是从我们的无序表中的第一个元素开始操作的 --> 而我们的无序表中的第一个元素其实就是我们待排序数组中的第二个元素 —> 所以我们的冒泡排序和选择排序算法的外层for循环都是从索引为0的位置开始到< length-1的位置,刚好length -1轮, 而我们的插入排序算法的外层for循环是从索引位置为1的位置开始到 <length的位置的,这个时候也是刚刚好 length -1轮
-
我们的直接插入排序中的待插入元素肯定是要和待插入位置的前一个位置的元素进行比较的 (如果是升序排序的话)
算法核心
其实直接插入排序就是将待排序序列看做是一个有序表和一个无序表, 每次就是使用无序表中的第一个元素来和有序表中从后到前进行比较, 知道比较到自己该存在的位置即可
- 算法难点: 每次比较之前都先将无序表中的第一个元素(也就是要比较的元素)存储到一个临时变量中, 因为开始比较的时候是从有序表中最后一个元素开始比较的, 这个时候我们比较的时候如果有序表中的最后一个元素比我们的无序表中的第一个元素大,那么这个时候我们直接让我们的有序表中的最后一个元素后移, 直接覆盖我们的无序表中的第一个元素,也就是我们当前要比较的元素, 这个也就是为什么我们要先将我们的无序表中的第一个元素, 也就是要比较的元素存储下来了, 其实就是这个位置的元素可能会"失真"
根据算法核心所得:
什么是有需要临时存储一个值?
- 如果这个值是我们的数组或者是其他存储数据的容器中的一个值, 此时我们需要这个值的时候, 我们要么就要存储这个值在数组中的索引, 或者是在容器中的位置, 或者就是我们要直接获取到这个值然后存储到我们的临时变量中
- 如果这个值是我们数组或者是其他存储数据的容器中的一个值, 甚至是变量中存储的一个值, 这个值如果我们后面还要使用到, 也就是对于我们而言还是有作用的, 如果这个时候这个值要被覆盖, 比如这个索引位置的值要换成其他值, 这个时候我们就要先将这个值暂时存储到一个临时变量中, 也就是对于我们要使用的即将要"失真"的值也是需要临时变量来存储