插入排序
简介:
插入排序如同我们日常的时候玩的扑克牌,我们习惯从从小到大排列。假设扑克牌的从小到大的顺序是 1…K 一共13张,此时你手里的牌[1,3,7,9] ,现在又抽了一张4,你得按从小到大的顺序插入其中,那么如何插入呢?我们就必须两张牌之间进行逐一比较,插入到符合的位置。这就是插入排序。
算法思想:
我们没一次都将下标为end+1的位置的数向前插入,插入到[0,end]中。而且每一次插入都是将end + 1插入到[0,end ]有序区间中的。
每一次单趟排序的过程:
数组 v【1,5,3,7,9】按从小大的顺序进行排序
- end = 0时, end + 1 = 1。将end +1位置的数插入到[0,0]中,发现 5比1大,就不用插入 数组[1,5](0到end+1此时为升序)
- end = 1时, end + 1 = 2。将end + 1位置(3)插入到 [0,1]中,发现3比5小,向前插入 最后得到数组[1,3,5] (0到end+1此时为升序)
- end = 2时, end + 1 = 3。已经是升序,不进行向前插入 [1,3,5,7]
- end = 3时,end + 1 = 4。 已经是升序,不进行向前插入 [1,3,5,7,9]
值得注意的是:因为插入的过程是从前往后进行插入的,end + 1是当前要向前插入的值的下标,所以我们的 end+1也是向后遍历的。
所以end + 1要从1遍历到数组最后一个元素对应的下标(n-1)。 所以end的范围就是 [0,n-2] 闭区间。
代码实现:
void InsertSort(int* arr, int n)
{
for (int i = 0; i < n - 1; i++)
{
int end = i;
int tmp = arr[end + 1]; // 要插入的元素,先将它保存起来
while (end >= 0) // 向前找适合插入的位置
{
if (tmp < arr[end]) // 如果tmp < arr[end],就是这个位置可以进行插入
{
arr[end + 1] = arr[end]; //,则需要将arr[end]向后移动,覆盖后面的数,此时arr[end+1]已经保存在tmp当中
}
else
{
break;
}
end--;
arr[end + 1] = tmp; //将存放arr[end+1]的临时变量tmp赋给arr[end]
}
}
}
void PrintArray(int*arr, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 1, 3, 4, 9, 3, 5, 7, 0 };
InsertSort(arr, sizeof(arr) / sizeof(int));
PrintArray(arr, sizeof(arr) / sizeof(int));
system("pause");
}
运行结果:
时间复杂度分析 O(N^2)
因为一个for循环遍历,每一次都要找到要插入的元素。每个插入的元素还要进行一次循环,向前找插入的位置。所以两个循环的长度都是数组长度的级别。所以是O(N^2)。
但是最好的情况下:顺序的情况,就不用去为每一个元素找插入的位置。只用一个for循环从头遍历到尾结束
最坏的情况下:加入我们要排顺序,结果数组逆序。则每一次找插入的位置的时候,都要把之前排好序的数组在遍历一遍。所以是O(N^2)。我们通常将最坏情况下的时间复杂度称为算法的时间复杂度。
空间复杂度分析 O(1)
因为没有用额外的空间,在常数的空间下。