插入排序,二分插入排序,希尔排序思想与比较
直接插入排序的基本方法:每步将一个待排序的元素,按其排序码的大小,插入到前面已经排好序的一组元素的适当位置上去,直到元素全部插入为止。
插入排序(insert sorting)思想:当插入第i个元素时,前面的v[0],v[1],v[2]......v[i-1],已经排好序了.这时用v[i]的插入码与v[i-1],v[i-2],......排序码进行比较,找到插入的位置即插入v[i],原来位置上的元素从后向前依次后移。
时间复杂度: 平均比较次数O(n2),平均移动次数 O(n2).
直接插入排序是一种稳定的排序。元素大部分有序时效率会更高,这时比较次数和移动次数都会减少。
参考代码:
void Sort<T>::insertSort(DataList<T> &datalist, int n)
{
if ( -1 == n)
{
for (int i = 1; i < datalist.m_nCurrentSize; i++)
{
insertSort(datalist, i);
}
return;
}
Element<T> temp = datalist.m_pvector[n];
int j;
for ( j = n; j > 0; j--)
{
if (temp > datalist.m_pvector[j-1])
{
break;
}else
{
datalist.m_pvector[j] = datalist.m_pvector[j-1];
}
}
datalist.m_pvector[j] = temp;
}
二分(折半)插入(Binary insert sort)排序基本思想:设在数据表中有一个元素序列v[0],v[1],v[2]......v[n].其中v[0],v[1],v[2]......v[i-1]是已经排好序的元素。在插入v[i]。利用折半搜索寻找v[i]的插入位置。
二分插入排序是一种稳定的排序。当n较大时,总排序码比较次数比直接插入排序的最差情况好得多,但比最好情况要差,所元素初始序列已经按排序码接近有序时,直接插入排序比二分插入排序比较次数少。二分插入排序元素移动次数与直接插入排序相同,依赖于元素初始序列。
参考代码:
template <class T>
void Sort<T>::binaryInsert(DataList<T> &datalist, int n)
{
if (-1 == n)
{
for (int i = 1; i < datalist.m_nCurrentSize; i++)
{
binaryInsert(datalist, i);
}
return;
}
Element<T> temp = datalist.m_pvector[n];
int left = 0, right = n - 1;
while(left <= right)
{
int middle = (left + right) / 2;
if (temp > datalist.m_pvector[middle])
{
left = middle + 1;
}else
{
right = middle - 1;
}
}
for (int j = n - 1; j >= left; j--)
{
datalist.m_pvector[j+1] = datalist.m_pvector[j];
}
datalist.m_pvector[left] = temp;
}
希尔排序(Shell sort)基本思想: 改进的直接插入算法。设待排序的元素序列有n个元素,首先取一整数gap(<n)作为间隔,将全部元素分为gap个子序列,所有距离为gap的元素放在同一序列中,在每个子序列中分别进行直接插入排序。然后缩小gap,例如gap=gap/2,重复上述的子序列划分与排序工作。开始由于gap取直大,每个子序列元素少,排序速度快,待排序后期,gap值逐渐变小,子序列元素变多,但由于前面的工作基础,大多数元素已经有序,所以排序速度快。
希尔排序是一种不稳定的排序。
参考代码:
void swap(int *a, int *b)
{
int x;
x = *a;
*a = *b;
*b = x;
}
void insertion_sort(int data[], int n, int increment)
{
int i, j;
for (i = increment; i < n; i += increment)
{
for (j = i; j >= increment && data[j] < data[j-increment]; j -= increment)
{
swap(&data[j], &data[j-increment]);
}
}
}
void shellsort(int data[], int n)
{
int i, j;
for (i = n / 2; i > 0; i /= 2)
{
for (j = 0; j < i; j++)
{
insertion_sort(data, n, i);
}
}
}
另一种参考代码:
template <class T>
void Sort<T>::shellSort(DataList<T> &datalist, int gap /* = -1 */)
{
if (-1 == gap)
{
int gap = datalist.m_nCurrentSize / 2;
while(gap)
{
shellSort(datalist, gap);
gap = gap / 2;
}
return;
}
for (int i = gap; i < datalist.m_nCurrentSize; i++)
{
insert(datalist, i, gap);
}
}
template <class T>
void Sort<T>::insert(DataList<T> &dataList, int n, int gap)
{
for (int i = n; i >= gap; i -= gap)
{
if (dataList.m_pvector[i] < dataList.m_pvector[i-gap])
{
Element<T> temp = dataList.m_pvector[i];
dataList.m_pvector[i] = dataList.m_pvector[i-1];
dataList.m_pvector[i-1] = temp;
}
}
}
总结:以上三种排序方法的核心都是直接插入排序,直接插入排序对于大部分有序的序列排序时速度快。二分插入排序在直接插入的基本上改变了查找元素插入位置的方法,对于完全无序的序列来说,速度会变快,但是对开大部分有序的序列反而会更慢,希尔排序则利用直接插入排序对于大部分有序的序列速度快的特点,先让大部分序列有序,以此来提高排序效率。