数据结构笔记--总结各种排序算法及其应用

来源:互联网 发布:java微信 api好友列表 编辑:程序博客网 时间:2024/05/22 03:14

排序分为直接插入类排序、交换类排序、选择类排序和归并排序。
注:为了满足每个函数只有一个数组首地址、一个数组长度的要求,我们会对某些函数进行封装。
直接插入类排序:在一个已排好序的记录子集的基础上,每一步将下一个待排序的记录有序的插入到已排好序的记录子集中,直到将所有的待排记录全部插入为止。
直接插入排序:通俗点说,就是把第i个元素插入到前i-1个有序集合并使得插入之后的集合有序。具体做法就是将第i个元素依次与前i-1个元素进行比较,并且将前面的元素依次后移,直到找到一个合适的位置(如果是增序则应插入第一个小于或等于待插入元素的后面)。依次遍历所有元素。

// 直接插入排序template <typename T>void InsertSort(T *data, int len){    if (len <= 0)        return;    for (int i = 1; i < len; ++i)    {        int j = i - 1;        T temp = data[i];        while (j >= 0 && temp < data[j])        {            data[j + 1] = data[j];            j--;        }        data[j + 1] = temp;    }}

折半查找排序:与直接插入排序类似,但不同的是,在直接插入排序中,寻找待插入元素(第i个元素)的位置时,使用的顺序查找。然而,已知待插入元素前i-1个元素有序(从插入类排序定义可知),为了提高查找速度,此时可以使用二分查找。

// 折半插入排序template <typename T>void BinInsertSort(T *data, int len){    if (len <= 0)        return;    for (int i = 1; i < len; ++i)    {        int temp = data[i];        int low = 0, high = i - 1;        // 与直接插入不同点        while (low <= high)        {            int mid = (low + high) / 2;            if (temp < data[mid])                high = mid - 1;            else                low = mid + 1;        }        // 元素依次后移        for (int j = i - 1; j >= low; --j)        {            data[j + 1] = data[j];        }        data[low] = temp;    }}

希尔排序:希尔排序又称增量缩小排序法,是一种基于插入思想的排序方法。先将待排序记录分割成若干个(具体个数与增量有关)子序列,然后对这若干个序列进行直接插入排序。其中,增量缩小为1时,希尔排序就是复杂度很小的直接插入排序。

// 一次增量为d的直接排序template <typename T>void ShellSortPart(T *data, int len, int d){    for (int i = d; i < len; ++i)    {        int j = i - d;        T temp = data[i];        while (j >= 0 && temp < data[j])        {            data[j + d] = data[j];            j = j - d;        }        data[j + d] = temp;    }}// 希尔排序,封装上述函数template <typename T>void ShellSort(T *data, int len){    if (len <= 0)        return;    int d[] = { 5, 3, 2, 1 };  // 增量序列    int len2 = sizeof(data) / sizeof(data[0]);    for (int i = len2; i > 0; --i)    {        ShellSortPart(data, len, d[i]);    }}

交换类排序:基于交换的排序法是一类通过交换不符合要求(前后顺序不符合排序要求)的元素对。
冒泡排序:从头到尾扫描记录序列(也可从尾到头扫描),比较相邻的两个记录,如果逆序,则交换两个记录。则一次扫描就可以将最大元素(降序排序则为最小元素)交换到最后一位。接下来第二趟,扫描前(i-1)个元素。直到所有元素有序。

// 冒泡排序template <typename T>void BubbleSort(T* data, const int len){    if (len <= 0)        return;    for (int i = 0; i < len - 1; i++)    {        for (int j = 0; j < len - i - 1; j++)        {            if (data[j] > data[j + 1])            {                mySwap(data[j], data[j + 1]);            }        }    }}

冒泡排序初级改进:在冒泡排序过程中,如果有一次比较没有发生交换,则数组已经有序。例如对序列{8,6,9,1,2,3,4},遍历三次数组后,序列变为{1,2,3,4,6,8,9},数组已经有序,则在下一次遍历,数组中没有发生交换,此时可以直接退出排序。

// 冒泡排序初级改进template <typename T>void PrimBubbleSort(T *data, const int len){    if (len <= 0)        return;    bool flag = true;    for (int i = 0; i < len - 1 && flag; i++)    {        flag = false;        for (int j = 0; j < len - i - 1; j++)        {            if (data[j] > data[j + 1])            {                mySwap(data[j], data[j + 1]);                flag = true;            }        }    }}

冒泡排序再改进:在上述过程中,即使序列已经有序,也需要再遍历一次,才能退出程序。按照同样的思路,可以记录交换发生的最后位置。例如,如果现在有五个元素没有排好顺序的话,但是在这五个元素比较时,如果有两个发生了交换,而后面的没有发生交换,即后面的已经有序,下次排序就可以排序到记录的最后位置。

// 冒泡排序再改进template <typename T>void AdvBubbleSort(T *data, const int len){    if (len <= 0)        return;    int lastIndex = 0, tempIndex = len - 1;    for (int i = 0; i < len - 1; i++)    {        lastIndex = tempIndex;        for (int j = 0; j < lastIndex; j++)        {            if (data[j] > data[j + 1])            {                mySwap(data[j], data[j + 1]);                tempIndex = j;            }        }        if (lastIndex == tempIndex)            break;    }}

快速排序:太经典了,不讲算法,直接上程序。

// 快速排序template <typename T>void QuickSortPart(T *data, int left, int right){    if (left > right)        return;    int low = left, high = right;    while (low < high)    {        while (low < high && data[low] < data[high])            high--;        if (low < high)            mySwap(data[low], data[high]);        while (low < high && data[low] < data[high])            low++;        if (low < high)            mySwap(data[low], data[high]);    }    QuickSortPart(data, left, low - 1);    QuickSortPart(data, high + 1, right);}// 快速排序,封装上述函数template <typename T>void QuickSort(T *data, int len){    if (len <= 0)        return;    QuickSortPart(data, 0, len - 1);}

选择类排序:每一趟排序再len - i - 1个记录中选取最小的记录作为有序序列中第i个记录。
简单选择排序:第i趟简单选择排序是指通过n-1次比较,从n-i-1个记录中选出最小的记录(如果是增序的话),并与第i个记录交换。直到所有记录排序完成为止。

// 简单选择排序template <typename T>void SelectSort(T *data, int len){    if (len <= 0)        return;    for (int i = 0; i < len - 1; ++i)    {        int k = i;        int min = data[i];        for (int j = i; j < len; ++j)        {            if (min > data[j])            {                min = data[j];                k = j;            }        }        if (k != i)            mySwap(data[i], data[k]);    }}

堆排序:把待排序的元素看成一颗完全二叉树,然后调整这些节点使其符合大根堆性质,然后交换堆顶元素与最后一个元素。此时最大元素在数组末尾,再调整其余未排序节点为大根堆,重复执行使得所有节点有序。

// 调整堆,从root节点到index节点调整template <typename T>void AdjHeap(T *data, int root, int index){    int j = 2 * root + 1;   //最后一个有孩子节点的节点位置    T temp = data[root];    while (j <= index)    {        if (j < index)//找出左孩子节点与右孩子节点中较大的一个        {            if (data[j] < data[j + 1])            {                j++;            }        }        if (temp < data[j])//找出根节点与左右孩子节点中较大的一个        {            data[(j - 1) / 2] = data[j];            j = j * 2 + 1;        }        else        {            break;        }    }    data[(j - 1) / 2] = temp;}// 堆排序template <typename T>void HeapSort(T *data, int len){    if (len <= 0)        return;    int i = 0, index = len - 1;    //初始化堆    for (i = (index - 1) / 2; i >= 0; i--)    {        AdjHeap(data, i, index);    }    //交换最大值与最后一个元素,并调整堆    for (i = index; i>0; i--)    {        mySwap(data[0], data[i]);        AdjHeap(data, 0, i - 1);    }}

归并类排序:归并就是将两个或两个以上的有序序列合并成一个有序序列。假设初始序列含有n个记录,首先将这n个记录看成n个有序的子序列,即每个子序列含有1个元素,即每个子序列有序,然后让这n个子序列两两归并,再次归并基础上,再进行两两归并…

// 合并有序子序列template <typename T>void Merge(T *data, int first, int mid, int last, T *temp){    int i = first, j = mid + 1;    int m = mid, n = last;    int k = 0;    while (i <= m && j <= n)    {        if (data[i] < data[j])            temp[k++] = data[i++];        else            temp[k++] = data[j++];    }    while (i <= m)        temp[k++] = data[i++];    while (j <= n)        temp[k++] = data[j++];    for (i = 0; i < k; ++i)        data[i + first] = temp[i];}// 部分归并排序template <typename T>void MergeSortPart(T *data, int first, int last, T *temp){    if (first >= last)        return;    int mid = (first + last) / 2;    MergeSortPart(data, first, mid, temp);    MergeSortPart(data, mid + 1, last, temp);    Merge(data, first, mid, last, temp);}// 归并排序,封装上述函数template <typename T>void MergeSort(T *data, int len){    if (len <= 0)        return;    T *temp = new T[len]();    MergeSortPart(data, 0, len - 1, temp);}

努力提高可读性,努力提高健壮性,努力符合编程规范。但是水平有限,能力不够,如果您能发现程序中的错误并愿意提出来,本人不胜感激。

0 0
原创粉丝点击