HFUT-算法导论2020-作业1-十大排序

冒泡排序

冒泡排序算法

冒泡排序重复地遍历待排序的数列,每次比较两个相邻元素,如果它们的顺序错误就把它们交换。重复地进行遍历直到没有再需要交换时表示数列已经排序完成。

  • 算法步骤:
      1. 比较相邻的元素:若第一个比第二个大,则交换;
      1. 遍历开始第一对到结尾最后一对,执行步骤 1
      1. 重复步骤 1~2,直到排序完成
  • 可改进的冒泡排序:第一趟排序之后最后一个元素是最大的,因此下一趟遍历只需执行到倒数第二对。

在这里插入图片描述

代码实现

sort.h

#ifndef sort__h
#define sort__h

#include <cstdio>
#include <algorithm>

using namespace std;

void print_array(int *arr, int n);
// 打印数组

void sort_array(int *arr, int n);
// 数组排序

#endif /* sort__h */

sort_.cpp

#include "sort_.h"

void print_array(int *arr, int n)
// 打印数组
{
    if(n==0){
        printf("ERROR: Array length is ZERO\n");
        return;
    }
    printf("%d", arr[0]);
    for (int i=1; i<n; i++) {
        printf(" %d", arr[i]);
    }
    printf("\n");
}

void sort_array(int *arr, int n)
//  编程实现《冒泡排序算法》:将乱序序列arr转化为升序序列
//  函数参数:乱序整数数组arr 数组长度
//  要求输出:调用print_array(int *arr, int n)输出前三次冒泡操作后的序列,以及最终的升序序列
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    int temp;
    // int count = 0;// 排序次数
    for(int i=0;i<n;i++){
        for(int j=0;j<n-i-1;j++){
            if(arr[j] > arr[j+1]){
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
        if(i<3){
            print_array(arr, n);
        }
    }
    print_array(arr, n);
    /********** End **********/
}



选择排序

选择排序算法

选择排序是一种简单直观的排序算法,首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

  • 算法步骤:

      1. 初始状态:无序序列为 R[0,n−1],长度 n,有序区为空;
      1. 第 i=1,…,n−1 趟排序从当前无序区 R[i−1,n−1] 中选出最小的元素 R[k],并将它与无序区的第 1 个记录 R[i−1] 交换,则 R[0,i−1] 变为元素个数增加 1 的新有序区,R[i,n−1] 变为元素个数减少 1 的新无序区;
      1. n−1 趟选择交换后结束。

在这里插入图片描述

代码实现

void sort_array(int *arr, int n)
//  编程实现《选择排序算法》:将乱序序列arr转化为升序序列
//  函数参数:乱序整数数组(无重复元素) 数组长度
//  要求输出:调用print_array(int *arr, int n)输出前三次选择操作后的序列,以及最终的升序序列
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    int min = 999;
    int temp;
    int index;
    for(int i=0;i<n;i++){
        for (int j=i;j<n;j++){
            if(arr[j]<min){
                min = arr[j];
                index = j;
            }
        }
        temp = arr[i];
        arr[i] = min;
        arr[index] = temp;
        min = 999;
        if(i<3){
            print_array(arr, n);
        }

    }
     print_array(arr, n);
    
    /********** End **********/
}

插入排序

插入排序算法

插入排序的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入

  • 算法步骤:
      1. 从第一个元素开始,该元素认为已经被排序;
      1. 取下一个元素,在已经排序的元素序列中从后向前扫描;
      1. 如果已排序元素大于新元素,将已排序元素移到下一位置;
      1. 重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置;
      1. 将新元素插入到该位置后;
      1. 重复步骤2~5

代码实现

void sort_array(int *arr, int n)
//  编程实现《插入排序算法》:将乱序序列arr转化为升序序列
//  函数参数:乱序整数数组(无重复元素) 数组长度
//  要求输出:调用print_array(int *arr, int n)输出前三次插入操作后的序列,以及最终的升序序列
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    
    for(int i=0;i<n;i++){
        for(int j = 0;j<i;j++){
            if(arr[i]<arr[j]){
                int temp = arr[i];
                for(int k=i;k>j;k--){
                    arr[k]=arr[k-1];
                }
                arr[j] =temp;
            }
        }
        if(i<4&&i!=0){
            print_array(arr, n);
        }
    }
    print_array(arr, n);
    /********** End **********/
}

希尔排序

希尔排序算法

希尔排序由 Shell 在 1959 年发明,又叫缩小增量排序,是第一个突破 O = ( n 2 ) O = (n^2) O=(n2) 的排序算法,属于简单插入排序的改进版,会优先比较距离较远的元素。

  • 算法步骤:

      1. 选择一个增量序列 T 1 , T 2 , . . . , T k T_1,T_2,...,T_k T1,T2,...,Tk,其中 T i > T j , T k = 1 , i > j T_i>T_j,T_k=1,i>j Ti>Tj,Tk=1,i>j;
      1. 每趟排序,根据对应的增量 T i T_i Ti ,将待排序列分割成若干子序列,分别对各子序列进行直接插入排序;
      1. 按增量序列个数 k,对序列进行 k 趟排序。
  • 希尔排序实例:
    下图的增量序列为:521,第一趟排序将增量为 5 的子序列进行插入排序,第二趟排序将增量为 2 的子序列进行插入排序,第三趟将增量为 1 的子序列进行插入排序,最终完成排序。
    在这里插入图片描述

  • 希尔排序的核心在于增量序列的设定:
    既可以提前设定好增量序列,也可以动态的定义增量序列。例如序列长度为 n,则动态增量为:147,…,3x+1<n/3

编程要求

本关的编程任务是补全右侧代码片段 sort_array 中 Begin 至 End 中间的代码,具体要求如下:在 sort_array 中,使用增量序列 [5, 2, 1] 实现希尔排序算法,完成指定输出。

代码实现

void sort_array(int *arr, int n)
//  编程实现《希尔排序算法》:将乱序序列arr转化为升序序列
//  函数参数:乱序整数数组 数组长度
//  要求输出:调用print_array(int *arr, int n)输出三遍增量排序操作后的序列,以及最终的升序序列
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    int temp;
    int count = 0;
    int xier[3] = {5,2,1};
    for(int i=0;i<3;i++){
        for(int j=xier[i];j<n;j++){
            int index = j;
            int temp = arr[index];
            if(arr[index]<arr[index - xier[i]]){
                while(index - xier[i] >= 0 && temp <arr[index - xier[i]]){
                    arr[index] = arr[index - xier[i]];
                    index -= xier[i];
                }
                arr[index] = temp;
            }
        }
        count++;
            if(count<=3){
                print_array(arr,n);
            }
    }
    print_array(arr,n);
    /********** End **********/
}

归并排序

归并排序算法

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,是采用分治法 Divide and Conquer 的一个非常典型的应用。 Divide:将问题分成一些小的问题然后递归求解; Conquer:将分的阶段得到的各答案合并在一起。

  • 算法步骤:
      1. 把长度为 n 的输入序列分成两个长度为 n/2 的子序列;
      1. 对这两个子序列分别采用归并排序;
      1. 将两个排序好的子序列合并成一个最终的排序序列。

归并排序

代码实现

int* merge_array(int *arr1, int n1, int* arr2, int n2)
//  编程实现两个有序数组arr1和arr2合并
//  函数参数:有序数组arr1 数组arr1长度 有序数组arr2 数组arr2长度
//  函数返回值:返回从小到大排序后的合并数组
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    int* res;
    res = new int[n1 + n2];
    int a1 = 0, a2 = 0;
    int index = 0;

    for (int i = 0; i < n1+n2; i++) {
        if (i < n1) {
            res[i] = arr1[i];
        }
        else {
            res[i] = arr2[i - n1];
        }
    }

    sort(res, res + n1 + n2);

    return res;
    /********** End **********/
}
int* merge_sort(int *arr, int n)
//  基于merge_array函数编程实现归并排序:自上而下的递归方法
//  函数参数:有序数组arr 数组arr长度
//  函数返回值:返回从小到大排序后的数组
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
 int mid = n / 2;
    if (mid > 0) {
        int* arr1 = new int[mid];
        int n1 = mid;
        int n2 = n - mid;
        int* arr2 = new int[n2];
        for (int i = 0; i < n; i++) {
            if (i < mid) {
                arr1[i] = arr[i];
            }
            else {
                arr2[i - mid] = arr[i];
            }
        }

        merge_sort(arr1, n1);   
        merge_sort(arr2, n - mid);     
        merge_array(arr1, n1, arr2, n2);
    }
    else
    {
        return  merge_array(arr, n, NULL, 0);
    }
    
    /********** End **********/
}

快速排序

快速排序算法

快速排序是最常用的一种排序算法,它的特点是速度快、效率高。

  • 快速排序的基本思想:选择一个关键值作为基准值。比基准值小的都在左边序列(一般是无序的),比基准值大的都在右边(一般是无序的)。一般选择序列的第一个元素作为基准值。

  • 算法步骤:

      1. 从数列中挑出一个元素,称为基准 pivot
      1. 分区 partition 操作:比基准值小的元素放在左边,比基准值大的元素放在右边;
      1. 递归 recursive:把小于基准值元素的子数列和大于基准值元素的子数列分别递归排序。
        快速排序

代码实现

int partition_array(int *arr ,int l,int r)
// 编程实现arr[l, r]分区:选定一个基准,左边比基准小,右边比基准大
// 返回基准所处位置
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    int pivot = arr[l];
    int leftIndex = l;
    int rightIndex = r;
    while(leftIndex != rightIndex){
        while(rightIndex > leftIndex && arr[rightIndex]>= pivot){
            rightIndex--;
        }
        while(rightIndex > leftIndex && arr[leftIndex] <= pivot){
            leftIndex++;
        }
        if(leftIndex < rightIndex){
            swap(arr[leftIndex],arr[rightIndex]);
        }
    }
    arr[l]= arr[leftIndex];
    arr[leftIndex] = pivot;
    return leftIndex;
    /********** End **********/
}

int* quick_sort(int *arr, int l, int r)
//  基于partition_array函数编程实现快速排序:自上而下的递归方法
//  函数参数:有序数组arr 初始l=0,r=n-1
//  函数返回值:返回从小到大排序后的数组
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    if(l<r){
        int pivot = partition_array(arr ,l,r);
        quick_sort(arr,l , pivot);
        quick_sort(arr, pivot+1, r);
        return arr;
    }
    /********** End **********/
}

堆排序

堆排序算法

堆排序 Heapsort 是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

算法步骤:

    1. 将初始待排序关键字序列 (R1,R2….Rn) 构建成大顶堆,此堆为初始的无序区;
    1. 将堆顶元素 R[1] 与最后一个元素 R[n] 交换,此时得到新的无序区 (R1,R2,……Rn-1) 和新的有序区 (Rn),且满足 R[1,2…n-1]<=R[n];
    1. 由于交换后新的堆顶 R[1] 可能违反堆的性质,因此需要对当前无序区 (R1,R2,……Rn-1) 调整为新堆,然后再次将 R[1] 与无序区最后一个元素交换,得到新的无序区 (R1,R2….Rn-2) 和新的有序区 (Rn-1,Rn)。不断重复此过程直到有序区的元素个数为 n-1,则整个排序过程完成。

在这里插入图片描述

编程要求

本关的编程任务是补全右侧代码片段 adjustHeap 和 heap_sort 中 Begin 至 End 中间的代码,具体要求如下:

  • adjustHeap 中,实现堆的调整。
  • heap_sort 中,构建大顶堆,并调用 adjustHeap 实现堆的调整。

笔记

完全二叉树

一下内容转载自:该文章
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。

在这里插入图片描述
对堆中的结点按层进行编号,映射到数组中得到:
在这里插入图片描述
公式来描述

大顶堆arr [i] >= arr [2i+1] && arr [i] >= arr [2i+2]

小顶堆arr [i] <= arr [2i+1] && arr [i] <= arr [2i+2]

  • 堆排序的基本思想是:
    将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余 n-1 个元素重新构造成一个堆,这样会得到 n 个元素的次小值。如此反复执行,便能得到一个有序序列了

代码实现

void adjustHeap(int *arr, int param1, int j)
// 编程实现堆的调整
{
    
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    // arr -> 数列
    // param1 -> 父节点index
    // j arr的长度

    int temp = arr[param1]; // 父节点
    int child = 2*param1 + 1; // left child

    while(child < j){
        if(child + 1 < j && arr[child] < arr[child+1 ]){
            child++;
        }

        if(temp >= arr[child]){
            break;
        }

        arr[param1] = arr[child];

        param1 = child;
        child = 2*child +1;
    }
    arr[param1] = temp;

    
    /********** End **********/
}

int* heap_sort(int *arr, int n)
//  基于adjustHeap函数编程实现堆排序
//  函数参数:无序数组arr 数组长度n
//  函数返回值:返回从小到大排序后的数组
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    // 建立初始堆
    for(int i=n/2 - 1;i>=0;i--){
        adjustHeap(arr,i, n);
    }

    for(int i = n-1;i>0;i--){
        swap(arr[0],arr[i]);
        adjustHeap(arr,0, i);
    }
    return arr;
    /********** End **********/
}

计数排序

计数排序算法

计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

  • 算法步骤:
      1. 找出待排序的数组中最大和最小的元素;
      1. 统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项
      1. 对所有的计数累加(从C 中的第一个元素开始,每一项和前一项相加);
      1. 反向填充目标数组:将每个元素i 放在新数组的第 C[i] 项,每放一个元素就将 C[i] 减去 1

在这里插入图片描述

代码实现

void sort_array(int *arr, int n)
//  编程实现《计数排序算法》
//  函数参数:乱序整数数组 数组长度
//  要求输出:调用print_array(int *arr, int n)输出:
//  每一行一个元素及其个数(升序),如 1 1
//  以及最终的升序序列
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    int max = *max_element(arr,arr+n);
    int min = *min_element(arr,arr+n);
    int arrLen = max-min+1;
    int *jishu = new int[arrLen];

    fill(jishu,jishu+arrLen,0);

    for(int i=0;i<n;i++){
        jishu[arr[i]-min] += 1;
    }
    int index = 0;

    for(int i=0;i<arrLen;i++){
        if(jishu[i]!= 0){
            printf("%d %d\n", i+min,jishu[i]);
        }
        

        //cout<<i+min+1<<" "<<jishu[i]<<endl;
    }

    for(int i=0;i<arrLen;i++){
        while(jishu[i] != 0){
            arr[index] = i+min;
            jishu[i]--;
            index++;
        }
    }
    print_array(arr, n);
    /********** End **********/
}

桶排序

桶排序算法

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。
桶排序 (Bucket sort) 的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。

  • 算法步骤:
      1. 设置一个定量的数组当作空桶;
      1. 遍历输入数据,并且把数据一个一个放到对应的桶里去;
      1. 对每个不是空的桶进行排序;
      1. 从不是空的桶里把排好序的数据拼接起来。

在这里插入图片描述
ps 桶的个数 数组中 (max - min)/arr.len

代码实现

int* sort_array(int *arr, int n)
//  编程实现《桶排序算法》
//  函数参数:乱序整数数组 数组长度
//  函数返回值:返回从小到大排序后的数组
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    int max = *max_element(arr,arr+n);
    int min = *min_element(arr,arr+n);

    int bucketNum = (max-min)/n + 1;
    int *bucket = new int[bucketNum*n];
    int *bucketIndex = new int[bucketNum];

    //fill(bucket,bucket+bucketNum*n,0);
    fill(bucketIndex,bucketIndex+bucketNum,-1);

    for(int i=0;i<n;i++){
        int index = arr[i]/(n+1);
        bucketIndex[index]++;
        bucket[index*n+bucketIndex[index]] = arr[i];
    }

    for(int i=0;i<bucketNum;i++){
        if(bucketIndex[i]!= -1){
            sort(bucket+i*n,bucket +i*n +bucketIndex[i]+1);
            //print_array(bucket, n*bucketNum);
        }
    }
    int index = 0;
    for(int i=0;i<bucketNum;i++){
        int a = 0;
        while(bucketIndex[i]>=0){
            arr[index] = bucket[i*n + a];
            a++;
            index++;
            bucketIndex[i]--;
        }
    }
    //print_array(arr, n);
    return arr;
    
    /********** End **********/
}

基数排序

基数排序算法

基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。

  • 算法步骤:
      1. 取得数组中的最大数,并取得位数;
      1. arr 为原始数组,从最低位开始取每个位组成 radix 数组;
      1. radix 进行计数排序(利用计数排序适用于小范围数的特点);
        在这里插入图片描述

代码实现

int* sort_array(int *arr, int n)
//  编程实现《基数排序算法》
//  函数参数:乱序整数数组 数组长度
//  函数返回值:返回从小到大排序后的数组
{
    // 请在这里补充代码,完成本关任务
    /********** Begin *********/
    int max = *max_element(arr,arr+n);
    int *radix = new int[n*10];
    fill(radix,radix+n*10,0);
    //print_array(radix, 10);
    int *tmparr = new int[n];
    for(int i=0;i<n;i++){
        tmparr[i] = arr[i];
    }

    int jishu[10];
    fill(jishu,jishu+10,-1);

    int wei = 0;
    while(max>0){
        max /= 10;
        wei ++;
    }

    //int 
    
    for(int i=1;i<=wei;i++){

        int dev = 1;
        for(int j=1;j<i;j++){
            dev *= 10;
        }
        for(int j=0;j<n;j++){

            int index = arr[j]/(dev)%(10);
            //tmparr[j] = tmparr[j]/(10);
            jishu[index]++;
            radix[index*n + jishu[index]] = arr[j];
        }
        int ind = 0;
        for(int k=0;k<10;k++){
            
            if(jishu[k]>=0){
                sort(radix + k*n,radix + k*n + jishu[k]+1);
                for(int j=0;j<jishu[k]+1;j++){
                    arr[ind] = radix[k*n+j];
                    ind ++;
                }
                jishu[k] = -1;
            }
        }
        fill(radix,radix+n*10,0);
    }

    return arr;
    
    /********** End **********/
}

ps 个十百千位求法:

    qian = i/1000%10;
    bai = i/100%10;		
	shi = i/10%10;
	ge = i%10;

ps 水平有限…甚至有的代码我都觉得有问题,也不知道为什么就通过了…欢迎指正

在这里插入图片描述

  • 25
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值