您的位置:首页 > 理论基础 > 数据结构算法

数据结构6-排序算法(直接插入排序、希尔排序、快速排序、归并排序和堆排序)

2017-10-07 13:46 691 查看
        所有排序中,需要排序的数组array的第0位不存放有效的数字,而是作为哨兵存在。

1.直接插入排序

        直接插入排序的基本操作是将一个记录插入到已排好序的表中,从而得到一个新的、记录增1的有序表。一个无序的数字array(同时可以把它当做一个长度为1的有序表),从1以后的每个位置都希望找到自己合适的位置插入,第i位(i从2开始)数字要到自己位置插入,那么就需要从前i-1位开始依次比较(比前一位大,比后一位小),同时后移记录。直到有序表的长度为数组array的长度。

/// <summary>
/// 插入排序
/// </summary>
public void InsertSort(int[] array)
{
int i = 0, j = 0;

for (i = 2; i < array.Length; i++)
{
if (array[i] < array[i - 1])
{
array[0] = array[i];
array[i] = array[i - 1];
for (j = i - 2; array[0] < array[j]; j--)
{
array[j + 1] = array[j];
}
array[j + 1] = array[0];
}
}
}
        直接插入排序适合给本来就较为有序的数组做排序,因为数组越有序,插入的次数就越少。时间复杂度为O(n^2)。

2.希尔排序

        希尔排序是直接插入排序的一个改良版。它的思想是:先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列“基本有序”时,再对全体记录进行一次直接插入排序。

/// <summary>
/// 希尔排序
/// </summary>
/// <param name="array"></param>
public void ShellSort(int[] array)
{
int[] dlta = new[] {5, 3, 1};

for (int i = 0; i < dlta.Length; i++)
{
ShellInsert(array, dlta[i]);
}
}

private void ShellInsert(int[] array, int dk)
{
int i = 0, j = 0;

for (i = dk + 1; i < array.Length; i++)
{
if (array[i] < array[i - dk])
{
array[0] = array[i];
for (j = i - dk; j > 0 && array[0] < array[j]; j -= dk)
{
array[j + dk] = array[j];
}
array[j + dk] = array[0];
}
}
}


3.快速排序

        快速排序是冒泡排序的一种改进版。它的基本思想是,通过一趟排序将带排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,已达到整个序列有序。

/// <summary>
/// 快速排序
/// </summary>
/// <param name="array"></param>
public void QuickSort(int[] array)
{
QSort(array, 1, array.Length - 1);
}

private void QSort(int[] array, int low, int high)
{
if (low < high)
{
int pivokey = Partition(array, low, high);
QSort(array, low, pivokey - 1);
QSort(array, pivokey + 1, high);
}
}

private int Partition(int[] array, int low, int high)
{
array[0] = array[low];
int pivokey = array[low];
while (low < high)
{
while (low < high && array[high] >= pivokey) high--;
array[low] = array[high];
while (low < high && array[low] <= pivokey) low++;
array[high] = array[low];
}
array[low] = pivokey;
return low;
}
        快速排序适合比较“乱”的序列,若序列已经有序,则快速排序会非常的慢。时间复杂度O(nlogn)。

4.归并排序

        归并排序的思想是,把初始的序列表的每一个关键字都视为一个序列表,然后将相邻两序列表合并成一个序列表并排序,多次合并后最终达到只剩一个表。

/// <summary>
/// 归并排序
/// </summary>
/// <param name="array"></param>
public void MergeSort(int[] array)
{
//归并排序
for (int i = 1; i < array.Length; i *= 2)
{
MergePass(array, i);
}
}

private void Merge(int[] array, int low, int mid, int high)
{
int i = low;
int j = mid + 1;
int k = 0;
int[] array2 = new int[high - low + 1];

while (i <= mid && j <= high)
{
if (array[i] <= array[j])
{
array2[k] = array[i];
i++;
k++;
}
else
{
array2[k] = array[j];
j++;
k++;
}
}

while (i <= mid)
{
array2[k] = array[i];
k++;
i++;
}

while (j <= high)
{
array2[k] = array[j];
k++;
j++;
}

for (k = 0, i = low; i < high; k++, i++)
{
array[i] = array2[k];
}
}

private void MergePass(int[] array, int gap)
{
int i =0;
for (i = 0; i + 2*gap - 1 < array.Length; i += 2*gap)
{
Merge(array, i, i + gap - 1, i + 2*gap - 1);
}
if (i + gap - 1 < array.Length)
{
Merge(array, i, i + gap - 1, array.Length - 1);
}
}

        归并排序是一种非常稳定的排序,在多数情况下排序时间都可以稳定到一个小的范围内。时间复杂度O(nlogn)。

5.堆排序

        当序列满足Ki >= K(2i) && Ki >= K(2i+1)时,这个序列就是大顶堆,简单的说就是制造出一个二叉树,其根节点大于两子节点。堆排序就需要制造出这些大顶堆,则堆顶的就是最大的数,我们把堆顶移到序列末尾,再重新制造大顶堆,重复之前的步骤。

/// <summary>
/// 堆排序
/// </summary>
/// <param name="array"></param>
public void HeapSort(int[] array)
{
for (int i = array.Length/2; i >= 1; i--)                //非叶子节点的最大角标为Lenth/2
{
HeapAdjust(array, i, array.Length - 1);
}
for (int i = array.Length - 1; i > 1; i--)
{
int temp = array[1];
array[1] = array[i];
array[i] = temp;

HeapAdjust(array, 1, i - 1);
}
}

private void HeapAdjust(int[] array, int s, int m)
{
int tmp = array[s];
for (int j = 2*s; j <= m; j *= 2)
{
if (j < m && array[j] < array[j + 1]) j++;
if (tmp < array[j])
{
array[s] = array[j];
s = j;
}

else
break;
}
array[s] = tmp;
}
        堆排序不适合对记录较少的文件做排序,但很适合对较大的文件排序。时间复杂度O(nlogn)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐