插入排序是一种简单直观且稳定的排序算法。如果有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。插入算法把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。
插入排序的基本思想是:每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
正如生活中整理扑克的方法:一张一张的来,将每一张扑克插入到其他已经有序的扑克中的适当位置。
将n个元素的数列分为已有序和无序两个部分,如
插入排序过程示例
下所示:
{{a1},{a2,a3,a4,…,an}}
{{a1⑴,a2⑴},{a3⑴,a4⑴ …,an⑴}}
…
{{a1(n-1),a2(n-1) ,…},{an(n-1)}}
每次处理就是将无序数列的第一个元素与有序数列的元素从后往前逐个进行比较,找出插入位置,将该元素插入到有序数列的合适位置中。
假设在一个无序的数组中,要将该数组中的数按插入排序的方法从小到大排序。假设啊a[]={3,5,2,1,4};插入排序的思想就是比大小,满足条件交换位置,一开始会像冒泡排序一样,但会比冒泡多一步就是交换后(a[i]=a[i+1]后)原位置(a[i])会继续和前面的数比较满足条件交换,直到a[i+1]前面的数组是有序的。
一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
⒈ 从第一个元素开始,该元素可以认为已经被排序
⒉ 取出下一个元素,在已经排序的元素序列中从后向前扫描
⒊ 如果该元素(已排序)大于新元素,将该元素移到下一位置
⒋ 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
⒌ 将新元素插入到下一位置中
template<class Type>
class CInsertionSort
{
public:
CInsertionSort();
~CInsertionSort();
public:
void sorting(Type arr[], int length);
void aptimizeSorting(Type arr[], int length);
void binaryAptinizeSorting(Type arr[], int length);
void swap(Type& a, Type& b);
};
#include "InsertionSort.h"
template<class Type>
CInsertionSort<Type>::CInsertionSort()
{
}
template<class Type>
CInsertionSort<Type>::~CInsertionSort()
{
}
/*普通插入排序*/
template<class Type>
void CInsertionSort<Type>::sorting(Type arr[], int length){
//插入排序第一个元素不用考虑
for (int i = 1; i < length; i++)
{
for (int j = i; j > 0 && arr[j] <= arr[j - 1]; j--)
{
swap(arr[j], arr[j - 1]);
}
}
}
/************************************************************************/
/*采用赋值的方式来降低元素位置替换的消耗 */
/************************************************************************/
template<class Type>
void CInsertionSort<Type>::aptimizeSorting(Type arr[], int length){
for (int i = 1; i < length; i++)
{
int j = i;
Type spiling = arr[i];//记录插入值
// 将插入点以后的所有元素,后移一位
for (j; j > 0 && spiling <= arr[j - 1]; j--)
{
arr[j] = arr[j - 1];
}
if (j != i)//将插入值放到插入点上
{
arr[j] = spiling;
}
}
}
/************************************************************************/
/* 使用二分查找更加快速的找到要插入的点儿,从而降低代码查找消耗 */
/************************************************************************/
template<class Type>
void CInsertionSort<Type>::binaryAptinizeSorting(Type arr[], int length) {
for (int i = 1; i < length; i++) {
int key = arr[i]; //记录一下arr[i]的值
int left = 0;
int right = i - 1;
// 二分查找寻找插入点
while (left <= right) {
int middle = left + ((right - left) >> 1);
if (key < arr[middle]) {
right = middle - 1;
}
else {
left = middle + 1;
}
}
// 将插入点以后的所有元素,后移一位
for (int j = i - 1; j >= left; j--) {
arr[j + 1] = arr[j];
}
// 插入元素到插入点
arr[left] = key;
}
}
template<class Type>
void CInsertionSort<Type>::swap(Type& a, Type& b){
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
⒍ 重复步骤2~5
如果比较操作的代价比交换操作大的话,可以采用二分查找法来减少比较操作的数目。该算法可以认为是插入排序的一个变种,称为二分查找排序。