数据结构——排序总结

数据结构——排序总结_第1张图片

目录

一、排序的概念

二、排序的分类

三、交换排序(省略冒泡)

1、概念

2、算法思想

3、实现原理

4、快速排序代码

四、归并排序

1、概念

2、代码实现

五、堆排序

1、堆排序基本思想

2、代码实现

六、插入排序

1、概念

2、代码实现

七、基数排序

1、概述

2、代码实现


一、排序的概念

排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。(递增或递减)

二、排序的分类

一图以蔽之:

数据结构——排序总结_第2张图片

三、交换排序(省略冒泡)

1、概念

快速排序:

又称划分交换排序(partition-exchange sort),简称快排,一种排序算法,最早由东尼·霍尔提出。在平均状况下,排序n个项目要 O(n log2 n)(大O符号)次比较。在最坏状况下则需要 O(n^2)次比较,但这种状况并不常见。事实上,快速排序 (n log n)通常明显比其他算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地达成。         

——(维基百科)

快速排序:

分治排序的一种排序算法,它将一个数组分成两个子数组,将两个部分独立的排序。快速排序和归并排序是互补的,归并排序将数组分成两个子数组分别排序,并将有序的子数组归并以将整个数组排序,而快速排序则是就当两个子数组都有序时,整个数组就自然有序了。归并中,递归调用发生在处理整个数组之前,而在快速中,递归调用发生在处理整个数组之后

数据结构——排序总结_第3张图片

2、算法思想

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

3、实现原理

(1)设置两个变量 low、high,排序开始时:low=0,high=size-1。

(2)整个数组找基准正确位置,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面

        (a)默认数组的第一个数为基准数据,赋值给key,即key=array[low]。

        (b)因为默认数组的第一个数为基准,所以从后面开始向前搜索(high–),找到第一个小于key的array[high],就将 array[high] 赋给 array[low],即 array[low] = array[high]。(循环条件是 array[high] >= key;结束时 array[high] < key)

        (c)此时从前面开始向后搜索(low++),找到第一个大于key的array[low],就将 array[low] 赋给 array[high],即 array[high] = array[low]。(循环条件是 array[low] <= key;结束时 array[low] > key)

        (d)循环 2-3 步骤,直到 low=high,该位置就是基准位置。

        (e)把基准数据赋给当前位置。

(3)第一趟找到的基准位置,作为下一趟的分界点。

(4)递归调用(recursive)分界点前和分界点后的子数组排序,重复2.2、2.3、2.4的步骤。

(5)最终就会得到排序好的数组。

4、快速排序代码

#include
using namespace std;

void display(int* array, int size) {
    for (int i = 0; i < size; i++) {
        cout<= key) {
            j--;
        }
        /// 当找到比 array[i] 小的时,就把后面的值 array[j] 赋给 坑
        if (i < j) {
            array[i] = array[j];
        }
        /// 当队首元素小于等于基准数据时,就一直向后挪动 i 指针
        while (i < j && array[i] <= key) {
            i++;
        }
        /// 当找到比 array[j] 大的时,就把前面的值 array[i] 赋给 新坑
        if (i < j) {
            array[j] = array[i];
        }
    }
    /// 跳出循环时 i 和 j 相等,此时的 i 或 j 就是 key 的正确索引位置
    /// 把基准数据赋给正确位置(基准数据的坑)
    array[i] = key;
    return i;
}

void QuickSort(int array[], int low, int high) {
    /// 开始默认基准为 low
    if (low < high) {
        /// 分段位置下标
        int standard = getStandard(array, low, high);
        /// 递归调用排序
        /// 左边排序
        QuickSort(array, low, standard - 1);
        /// 右边排序
        QuickSort(array, standard + 1, high);
    }
}

/// 合并到一起的快速排序,与上面的是一样的
// void QuickSort(int array[], int low, int high) {
//     if (low < high) {
//         int i   = low;
//         int j   = high;
//         int key = array[i];
//         while (i < j) {
//             while (i < j && array[j] >= key) {
//                 j--;
//             }
//             if (i < j) {
//                 array[i] = array[j];
//             }
//             while (i < j && array[i] <= key) {
//                 i++;
//             }
//             if (i < j) {
//                 array[j] = array[i];
//             }
//         }
//         array[i] = key;
//         QuickSort(array, low, i - 1);
//         QuickSort(array, i + 1, high);
//     }
// }

int main() {
    int array[] = {49, 38, 65, 97, 76, 13, 27, 49, 10};
    int size = sizeof(array) / sizeof(int);

    /// 打印数据
    cout<

四、归并排序

1、概念

归并排序是一种效率较高的排序方法,它分为拆分和合并两大部分,先拆后合。

所谓拆分,就是将一堆无序的数拆成单个,方便合并。

所谓合并,就是将拆分好的数按照排序规则再拼凑起来。

详细解释请看下图:

数据结构——排序总结_第4张图片

看完这幅图你应该明白了,归并排序原来就是将一堆数字分开,再合成有序的数列。其实,这就是分治的思想,将大问题化小问题,将每个最小的问题处理好,合并起来大问题也就处理好了。

2、代码实现

#include 
using namespace std;

///每一趟合并
void merge(int* data,int* tmp,int left,int mid,int right){
    int i=left,j=mid+1;
    int k=left;
    while(i<=mid&&j<=right){
        if(data[i]>n;
    int* data=new int[n];
    for(int i=0;i>data[i];
    for(int i=0;i

五、堆排序

1、堆排序基本思想

(1)将待排序序列构造成一个大顶堆。

(2)此时,整个序列的最大值就是堆顶的根节点。

(3)将其与末尾元素进行交换, 此时末尾就为最大值。

(4)然后将剩余 n-1 个元素重新构造成一个堆, 这样会得到 n 个元素的次小值。 如此反复执行, 便能得到一个有序序列了。

最后可以看到在构建大顶堆的过程中, 元素的个数逐渐减少,最后就得到一个有序序列了。

注:一般升序采用大顶堆, 降序采用小顶堆。

2、代码实现

/**以大顶堆举例*/
#include
using namespace std;

void Max_heap(int arr[], int first, int last){
    int par = first;
    int son = par * 2 + 1;

    while(son <= last){
        if(son < last && arr[son] < arr[son + 1]){
            son++;
        }

        if(arr[par] > arr[son]){///如果满足,那么就直接返回
            return;
        }else{
            int temp = arr[par];
            arr[par] = arr[son];
            arr[son] = temp;///交换顺序

            par = son;
            son = par * 2 + 1;///再次进入循环
        }
    }
}

void heap_sort(int arr[], int len){
    for(int i = len / 2 - 1;i >= 0;i--){
        ///从最后一个非叶子节点开始往回走
        Max_heap(arr, i, len-1);
    }///先排好形成大顶堆

    ///下面代码:先将第一个元素和已经排好元素前一位作交换,再进行堆排序
    for(int i = len - 1;i > 0;i--){
        int temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;

        Max_heap(arr, 0, i - 1);
    }
}

int main(){
    int n;///一共有多少数据
    cin>>n;
    int* arr=new int[n+1];///要排序的数据
    for(int i = 0;i < n;i++){
        cin>>arr[i];
    }

    heap_sort(arr, n);

    for(int i = 0;i < n;i++){
        cout<

六、插入排序

1、概念

(1)直接插入排序
把待排序的数据插入到已经排好序的数据中,直到所有的数据插入完成,即就是直接插入排序。(如流行于陕西一带的纸牌游戏,挖坑)一般待排序的区间是第二个数到最后一个数。注意动态移动插入的过程,结合图与代码去理解。

例如下面的例子就是待排序区间从 7 到 8,把第一个数当作已经排序的数据:

数据结构——排序总结_第5张图片

 (2)希尔排序

希尔排序实在前面直接插入排序的基础上进行改进的一种排序。直接插入排序的变量 i 其实就是一个间隔,而希尔排序的间隔不是 1,它的间隔逐渐缩小直到为 1 的一种排序(当间隔gap到达1时停止),因此又叫缩小增量法。它是对直接插入排序算法的优化,当间隔不为 1 的时候,都是预排序。下图是举例,第一次的间隔是数据长度的三分之一再加一。即 gap = size / 3 + 1

数据结构——排序总结_第6张图片

2、代码实现

#include 

using namespace std;

///初始化数组
void SetArray(int *data, int size) {
    //srand(time(0));
    cout << "随机初始化" << size << "个数" << endl;
    for (int i = 0; i < size; i++) {
        data[i] = rand() % 100 + 1;
    }
}

///打印函数
void Print(int *data, int size) {
    for (int i = 0; i < size; i++) {
        cout << data[i] << " ";
    }
    cout << endl;
}

///交换函数
void Swap(int &a, int &b) {
    int tmp = a;
    a = b;
    b = tmp;
}

///冒泡排序
void Bubble_Sort(int *data, int size) {
    for (int i = size - 1; i >= 0; i--) {
        bool flag = false;        //、是否交换标志
        for (int j = 0; j < i; j++) {
            if (data[j + 1] < data[j]) {
                Swap(data[j], data[j + 1]);
                flag = true;//发生了交换
            }
        }
        if (flag == false) {//全程无交换
            break;
        }
    }
}

///直接插入排序
void Insertion_Sort(int *data, int size) {
    for (int i = 1; i < size; i++) {
        int tmp = data[i];
        int j;
        for (j = i; j > 0 && data[j - 1] > tmp; j--) {
            ///前一个数大于后一个树,进行交换
            data[j] = data[j - 1];
        }
        ///找到合适位置,进行插入
        data[j] = tmp;
    }
}


///折半插入排序
void Binary_Insertion_Sort(int *data, int size) {
    for (int i = 1; i < size; i++) {
        int low = 0, high = i - 1;//在i之前找到最佳位置
        int tmp = data[i], mid, j;
        while (low <= high) {//寻找最佳位置
            mid = (low + high) / 2;
            if (data[mid] < tmp) {
                //大于中间值,到右半部分寻找
                low = mid + 1;
            } else {//小于中间值,到左半部分寻找
                high = mid - 1;
            }
        }
        for (j = i; j > low; j--) {
            data[j] = data[j - 1];
        }
        data[j] = tmp;
    }
}

///希尔排序(通俗易懂版)
void ShellInsert(int *arr, int len, int gap) {
    int k, i, j, temp;
    ///k循环是处理每一组
    for (k = 0; k < gap; k++) {
        ///i循环是调用了其中一组进行排序
        for (i = k; i < len; i += gap) {
            ///temp是某组第i个数值(都是比较序列的最后一个)
            temp = arr[i];
            ///k永远的每一组的第一个
            for (j = i; j > k; j -= gap) {
                ///条件符合temp与arr[j]交换
                if (temp > arr[j - gap])
                    arr[j] = arr[j - gap];
                else
                    break;
            }
            arr[j] = temp;
        }
    }
}

///调用希尔排序,确定gap(gap大小是循环除以2)
void ShellSort(int *arr, int len) {
    int gap = len / 2;
    while (true) {
        ShellInsert(arr, len, gap);
        Print(arr, len);
        if (gap == 1)
            break;
        gap /= 2;
    }
}

///改进版希尔排序
void Shell_sort(int *arr, int len) {
    int i, j, gap, key;
    ///初始间隔:n/2,每一趟之后除以2
    for (gap = len / 2; gap > 0; gap /= 2) {
        ///每一趟采用插入排序,为什么是i++而不是i+=gap呢,因为是每一组都处理一下
        for (i = gap; i < len; i++) {
            key = arr[i];
            for (j = i; j >= gap && key < arr[j - gap]; j -= gap)
                arr[j] = arr[j - gap];
            ///找到合适位置,进行插入
            arr[j] = key;
        }
        Print(arr, len);
    }
}

int main() {
    cout << "请输入数组长度:" << endl;
    int size, *data;
    cin >> size;
    data = new int[size];

    SetArray(data, size);
    cout << "冒泡排序前:" << endl;
    Print(data, size);
    cout << "冒泡排序后:" << endl;
    Bubble_Sort(data, size);
    Print(data, size);

    SetArray(data, size);
    cout << "直接插入排序前:" << endl;
    Print(data, size);
    cout << "直接插入排序后:" << endl;
    Insertion_Sort(data, size);
    Print(data, size);

    SetArray(data, size);
    cout << "折半插入排序前:" << endl;
    Print(data, size);
    cout << "折半插入排序后:" << endl;
    Binary_Insertion_Sort(data, size);
    Print(data, size);

    SetArray(data, size);
    cout << "希尔排序前:" << endl;
    Print(data, size);
    cout << "希尔排序后:" << endl;
    ShellSort(data, size);
    //Shell_sort(data,size);
    Print(data, size);

    return 0;
}

七、基数排序

1、概述

基数排序的知识点我就不贴出来,相信都能搜到对应概念解释,下面就直接上代码,代码解释其实也很清晰了。

2、代码实现

#include
using namespace std;

///补充:(arr[i] / exp) % 10  这个表达式很重要哦,作用是截取到某个数的某一位

/// 获取数组arr中最大值,arr——待排序的数组,len——数组arr的长度。
int arrmax(int *arr, int len) {
    int i, imax;
    imax = arr[0];
    for (i = 1; i < len; i++)
        if (arr[i] > imax)
            imax = arr[i];
    return imax;
}

/// 对数组arr按指数位进行排序。
/// arr——待排序的数组,len——数组arr的长度。
/// exp——排序指数,exp=1:按个位排序;exp=10:按十位排序;......
void _radixsort(int *arr, int len, int exp) {
    int i;
    int result[len];       /// 存放从桶中排序好收集到的数据的临时数组。
    int buckets[10] = {0};   /// 初始化10个桶。

    ///遍历arr,将数据出现的次数存储在buckets中。
    for (i = 0; i < len; i++)
        buckets[(arr[i] / exp) % 10]++;

    ///调整buckets各元素的值,调整后的值就是arr中元素在result中的位置。
    ///核心是下面两行代码,我暂时不知道怎么解释。下面两行代码处理完后buckets的数字
    ///代表的是该桶前面有多少个已经排好序的数
    for (i = 1; i < 10; i++)
        buckets[i] = buckets[i] + buckets[i - 1];

    /// 将arr中的元素填充到result中。注意:一定得是倒序从后面一个一个读取哦!
    ///再说一遍,这里的倒序取非常关键!
    for (i = len - 1; i >= 0; i--) {
        int iexp = (arr[i] / exp) % 10;
        result[buckets[iexp] - 1] = arr[i];
        buckets[iexp]--;
    }

    /// 将排序好的数组result复制到数组arr中。
    ///memcpy(arr, result, len * sizeof(int));
    for (int i = 0; i < len; i++)
        arr[i] = result[i];
}

/// 基数排序主函数,arr——待排序的数组,len——数组arr的长度。
void radixsort(int *arr, int len) {
    int imax = arrmax(arr, len);    /// 获取数组arr中的最大值。

    int iexp;    /// 排序指数,iexp=1:按个位排序;iexp=10:按十位排序;......

    /// 从个位开始,对数组arr按指数位进行排序。
    for (iexp = 1; imax / iexp > 0; iexp = iexp * 10) {
        _radixsort(arr, len, iexp);
        int i;
        ///cout << iexp << " ";     ///输出指数
        for (i = 0; i < len; i++)
            cout << arr[i] << " ";
        cout << endl;
    }
}

int main() {
    ///int arr[] = {144, 203, 738, 905, 347, 215, 836, 26, 527, 602, 946, 504, 219, 750, 848};
    ///int len = sizeof(arr) / sizeof(int);
    int len;
    cin >> len;
    int *data = new int[len];
    for (int i = 0; i < len; i++)
        cin >> data[i];
    for (int i = 0; i < len; i++)
        cout << data[i] << " ";
    cout<

我是花花,祝自己也祝您变强了~ 

你可能感兴趣的:(数据结构与算法,数据结构,排序算法)