我们先来学习 直接插入排序, 直接排序算是所有排序中最简单的了,代码也非常好实现,尽管直接插入排序很简单,但是我们依旧不可以上来就直接写代码,一定要分析之后才开始写,这样可以提高自己写代码的准确率,整体流程下来,对知识的理解也会加深.
默认第一个元素为有序的,然后从无序序列中的最左边取元素,从有序序列的右到左依次比较,直到找到合适的位置,然后插入. (简言之: 从无序序列取第一个元素从左到右比较插入到有序序列合适的位置)
用生活中的例子来描述直接插入排序理解起来会更加容易,所以我们这里就用一个生活中常发生的事来类比学习吧,如果我们把直接插入排序用打扑克牌来模拟,会是怎样的效果呢?
现在就来试试吧~
使用玩扑克牌的例子来模拟直接插入排序是一个很好的主意,这个例子非常贴近直接插入排序的实际操作过程。让我们详细地通过这个例子来理解直接插入排序:
这个过程很好地模拟了直接插入排序的逻辑。在每一步中,你都保证了手中的牌是有序的,通过找到合适的位置为新牌插入。这就是直接插入排序的精髓:一步步构建有序序列,直到所有元素都被正确地插入。
我们把上面的操作用图示来演示一下,进一步加深理解 , 假设现在的洗好的扑克牌为一组无序序列 : {6,4,9,1,10,2,8}
好,可以开始打牌了~
package src.boke;
public class InsertSort {
public static void main(String[] args){
//无序序列
int[] arr = {6,4,9,1,10,2,8};
//调用直接排序方法
insertSort(arr);
//打印有序序列
printArray(arr);
}
/**
* 直接插入排序方法实现
* @param arr 待排序序列/无序序列
*/
public static void insertSort(int[] arr){
//对传进来的无序序列进行直接插入排序操作
for (int i = 1; i < arr.length; i++) {
//接收int[i] ,即摸到的牌
int key = arr[i];
int j = i-1;
for (; j >=0 ; j--) {
if(arr[j]>key){
arr[j+1] = arr[j];
}else{
break;
}
}
arr[j+1] = key;
}
}
/**
* 打印素组的方法
*/
public static void printArray(int[] arr){
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] +" ");
}
}
}
#include <stdio.h>
void insertSort(int arr[], int n) {
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
// 将大于 key 的元素向右移动一个位置
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
void printArray(int arr[], int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {6, 4, 9, 1, 10, 2, 8};
int n = sizeof(arr) / sizeof(arr[0]);
insertSort(arr, n);
printArray(arr, n);
return 0;
}
function insertSort(arr) {
for (let i = 1; i < arr.length; i++) {
let key = arr[i];
let j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
function printArray(arr) {
console.log(arr.join(" "));
}
// 测试
let arr = [6, 4, 9, 1, 10, 2, 8];
insertSort(arr);
printArray(arr);
排序稳定性是排序算法的一个重要特性,它涉及相等元素的相对顺序在排序前后是否保持不变。
具体来说:
稳定性的重要性主要体现在当元素有多个字段进行排序时。在某些情况下,维持数据的初始顺序是重要的。例如,在对一组人按照出生日期排序后,可能需要对结果按姓名排序,如果使用稳定排序算法,那么同一天出生的人将按照他们原始的顺序(即按姓名的顺序)排列。
通过上述测试我们可以知道,当我们在比较key和arr[j] 的时候,如果取了 等号 ,那么此时就是不稳定的,如果没有取 等号 就是稳定的,所以直接插入排序是稳定的吗?
让我们详细解释一下为什么这样会发生:
arr[j] > key
进行比较时,如果 arr[j]
等于 key
,那么循环会停止,key
将被插入到 arr[j]
的后面。因此,原始数组中顺序相邻的、值相等的元素在排序后仍将保持相同的顺序,这保证了排序的稳定性。arr[j] >= key
进行比较,当 arr[j]
等于 key
时,排序过程仍会继续,尝试找到更前面的位置插入 key
。这可能导致 key
被插入到其他相等元素的前面,从而改变了这些元素的相对顺序,这破坏了排序的稳定性。结论 : 一个本身就稳定的排序你可以将其实现为不稳定的,但是一个本身就不稳定的排序你无法将其变成稳定的. 所以 : 直接插入排序是稳定的排序
直接插入排序的时间复杂度分析涉及到最好情况、平均情况和最坏情况。
直接插入排序的空间复杂度主要考虑的是算法在执行过程中需要额外使用的内存空间。
在直接插入排序中,所有的排序操作都是在原始数组上进行的,不需要额外的数组来存储数据。排序过程中唯一需要的额外空间是一个用于存储待插入元素的临时变量(比如 key
)。除此之外,还需要少量的额外空间用于循环计数和索引存储。
由于这些额外空间的需求量不随待排序的数据量的增加而增加(也就是说,无论要排序多少数据,所需的额外空间量都是固定的),因此直接插入排序的空间复杂度是O(1),也就是说它是一个原地排序算法。这也意味着直接插入排序非常节省内存,适合于在内存受限的环境中使用。