浙大数据结构之09-排序1 排序

这篇具有很好参考价值的文章主要介绍了浙大数据结构之09-排序1 排序。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

题目详情:

给定N个(长整型范围内的)整数,要求输出从小到大排序后的结果。

本题旨在测试各种不同的排序算法在各种数据情况下的表现。各组测试数据特点如下:

  • 数据1:只有1个元素;
  • 数据2:11个不相同的整数,测试基本正确性;
  • 数据3:103个随机整数;
  • 数据4:104个随机整数;
  • 数据5:105个随机整数;
  • 数据6:105个顺序整数;
  • 数据7:105个逆序整数;
  • 数据8:105个基本有序的整数;
  • 数据9:105个随机正整数,每个数字不超过1000。

    输入格式:

    输入第一行给出正整数N(≤105),随后一行给出N个(长整型范围内的)整数,其间以空格分隔。

    输出格式:

    在一行中输出从小到大排序后的结果,数字间以1个空格分隔,行末不得有多余空格。

    输入样例:

    11
    4 981 10 -17 0 -20 29 50 8 43 -5
    

    输出样例:

    -20 -17 -5 0 4 8 10 29 43 50 981

主要思路:

把所有提到的排序都尝试复现一遍(才发现自己啥也不会)

(一)选择排序:

(1)简单选择排序:

在未排序序列中选出最小元素和序列首位元素交换,剩下来的序列以此类推

#include <stdio.h>
#include <stdlib.h>
#define MAX_NUM 100005
int Data[MAX_NUM];
void Swap(int* data1, int* data2) {
    int temp = *data1;
    *data1 = *data2;
    *data2 = temp;
    return;
}
void SimpleSelectionSort(int num, int data[]) {
    int minIndex;
    for(int i = 0; i < num - 1; i++) {
        minIndex = i;
        for(int j = i + 1; j < num; j++) {
           if(data[j] < data[minIndex]) {
               minIndex = j;
           } 
        }
        Swap(&data[i], &data[minIndex]);
    }
    return;
}
int main() {
    int num;
    scanf("%d", &num);
    for(int i = 0; i < num; i++) {
        scanf("%d", &Data[i]);
    }
    SimpleSelectionSort(num, Data);
    for(int i = 0; i < num; i++) {
        printf("%d", Data[i]);
        if(i != num - 1) printf(" ");
    }
    return 0;
}

(2)堆排序

 首先将一个无序的序列生成最大堆,从树的倒数第二排最右边开始,下标是节点总数/2-1,然后依次递减1,不断将以当前下标为根节点的子树调整为最大堆

调整过程为下滤

接着不需要将堆顶元素输出,而是将堆顶元素与当前堆的最后一个元素交换位置,交换完位置后再将大小减1的堆重新调整成最大堆,重复上述过程,原来保存最大堆的数组就转换为一个从小到大的序列

/*堆排序建立堆的时候,
相对于之前建立堆是插入,
这个是在一堆无序的数字上建立
*/
#include <stdio.h>
#include <stdlib.h>
#define MAX_NUM 100005
int Data[MAX_NUM];
void Swap(int* a, int* b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
    return;
}
void PercDown(int data[], int root, int num) { //建立最大堆和从最大堆中弹出最大值,核心部分都是下滤
    //相对与之前的最大堆算法,本题特殊之处在于用于存储最大堆的数组不是从下标1开始,0做哨兵,而就是从0开始
    int parent, child;
    
    int tmp = data[root];
    //从root开始,遍历数组,比较当前值和左右子节点,若当前值大于左右子节点,则交换当前值和左右子节点的值,并重新调整最大堆
    for(parent = root; parent * 2 + 1 < num; parent = child) {
        child = parent * 2 + 1;
        if((child!= num - 1) && (data[child] < data[child + 1])) {
            child++;
        }
        if(tmp >= data[child]) {
            break;
        }
        else {
            data[parent] = data[child];
        }
    }
    data[parent] = tmp;
    return;
}
void HeapSort(int data[], int num) {
    //建立最大堆
    for(int i = num / 2 - 1; i >= 0; i--) {
        PercDown(data, i, num);
    }
    //删除最大堆顶
    for(int i = num - 1; i > 0; i--) {
        Swap(&data[0], &data[i]);
        PercDown(data, 0, i);
    }
}
int main() {
    int num;
    scanf("%d", &num);
    for(int i = 0; i < num; i++) {
        scanf("%d", &Data[i]);
    }
    HeapSort(Data, num);
    for(int i = 0; i < num; i++) {
        printf("%d", Data[i]);
        if(i != num - 1) {
            printf(" ");
        }
    }
    return 0;
}

(二)插入排序 

(1)简单插入排序

将序列分为已排和未排,每次从未排中取一个与已排比较直到合适位置后插入,重复上述过程,直到没有未排序列

#include <stdio.h>
#include <stdlib.h>
#define MAX_NUM 100005
int Data[MAX_NUM];
void SimpleInsertSort(int data[], int num) {
    for(int i = 1; i < num; i++) {
        int tmp = data[i];  //取出未排序列中第一个元素
        int j = i;
        for(; j > 0 && data[j - 1] > tmp; j--) {    //依次与已排序列从末尾往前比
            data[j] = data[j - 1];  
        }
        data[j] = tmp;  //将未排序列第一个元素插入到已排序列合适位置
    }
    return;
}
int main() {
    int num;
    scanf("%d", &num);
    for(int i = 0; i < num; i++) {
        scanf("%d", &Data[i]);
    }
    SimpleInsertSort(Data, num);
    for(int i = 0; i < num; i++) {
        printf("%d", Data[i]);
        if(i != num - 1) {
            printf(" ");
        }
    }
    return 0;
}

(2)希尔排序

通过将待排序的一组元素按一定间隔分为若干个序列,分别进行插入排序;最终间隔是1

往往用Sedgewick作为增量序列

void ShellSort(int data[], int num) {
    //定义一个增量数组
    int i;
    int sedgewick[] = {929, 505, 209, 109, 41, 19, 5, 1, 0};

    //在sedgewick序列中找到比num小的增量,因为初始的增量sedgewick[i]不能超过待排序列长度
    for(i = 0; sedgewick[i] >= num; i++)    //在sedgewick序列中找到比num小的增量,因为初始的增量sedgewick[i]不能超过待排序列长度
        ;

    for(int interval = sedgewick[i]; interval > 0; interval = sedgewick[++i]) {
        //插入排序
       //比较难以理解,首先从一个intervel以后开始选择,因为下标0~intervel-1是分别intervel的第一个(第一个又是已排序列)
        for(int j = interval; j < num; j++) {  
            int tmp = data[j];  //取出未排序列中第一个元素
            int k = j;
            //依次将其已排序元素进行比较
            for(; k >= interval && data[k - interval] > tmp; k -= interval) {   //依次与已排序列从末尾往前比
                data[k] = data[k - interval];
            }
            data[k] = tmp;  //将未排序列的第一个元素插入到已排序列中合适位置
        }
    }
}

(三)交换排序 

(1)冒泡排序

外层循环从待排序列最后一位往前依次确定,内层循环从前往后扫描两两比较取最大

减少循环的一个方法是每层循环设置一个flag,如果从头至尾扫描循环都没有交换,说明当前序列已经有序,可以break了

#include <stdio.h>
#define MAX_NUM 100005
#define bool int
#define TRUE 1
#define FALSE 0
int Data[MAX_NUM];
void Swap(int* a, int* b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
    return;
}
void BubbleSort(int data[], int num) {
    for(int i = num - 1; i >= 0; i--) {    //依次从下标num - 1开始往前定位
        bool flag = FALSE;  //标记该次循环中是否发生交换,若无,说明该序列已经有序
        for(int j = 0; j < i; j++) {
            if(data[j] > data[j + 1]) {
                Swap(&data[j], &data[j + 1]);
                flag = TRUE;
            }
        }
        if(flag == FALSE) {
            break;
        }
    }
    return;
}
int main() {
    int num;
    scanf("%d", &num);
    for(int i = 0; i < num; i++) {
        scanf("%d", &Data[i]);
    }
    BubbleSort(Data, num);
    for(int i = 0; i < num; i++) {
        printf("%d", Data[i]);
        if(i != num - 1) {
            printf(" ");
        }
    }
    return 0;
}

(2)快速排序

快速排序的思路是通过分治法,首先取一个pivot基准值,将大于pivot的都移到右边,小于它的都移到左边,将一个序列分成两个序列,然后将这两个序列分别重复上述操作

具体实现:

首先为了减少时间复杂度,要考究选择的pivot,方法是将最左边,最右边和中间(left + right)/2按最左边<=中间<=最右边,因为这样排序后最左边必定比中间小,最右边必定比中间大,将中间的值移动到最右边倒数第二个后,比较范围只用从最左边下标+1到最右边下标-2

另一个优化是因为递归针对较大数据还好,但对于较小数据就很慢,所以临设一个cutoff变量,当前序列大小比这个大就用快排,如果小就直接用简单插入排序

但发现这个cutoff最小必须设为2,再小就报错了,目前还没想出来是为什么

#include <stdio.h>
#define TRUE 1
#define FALSE 0
#define MAX_NUM 100005
typedef int bool;
int Data[MAX_NUM];
int Cutoff = 3;
void Swap(int* a, int* b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
    return;
}
/*实现简单插入排序*/
void SimpleInsertSort(int data[], int left, int right) {
    for(int i = left + 1; i <= right; i++) {
        int tmp = data[i];
        int j = i;
        for(; j > left && data[j - 1] > tmp; j--) {
            data[j] = data[j - 1];
        }
        data[j] = tmp;
    }
    return;
}
/*下面函数作用是确定快排中主元,将最左边、最右边、中间元素调整为
最左边 <= 中间 <= 最右边,此时最左边一定小于中间,最右边一定大于等于中间,这两个所以就不用考虑了
将中间的主元放到右边倒数第二个位置,下面就只用比较左边第二个到右边倒数第三个位置的范围就行*/
int CompareThreeNums(int data[], int left, int right) {
    int mid = (left + right) / 2;
    int max = data[left];
    if(data[left] > data[mid]) {
        Swap(&data[left], &data[mid]);
    }
    if(data[left] > data[right]) {
        Swap(&data[left], &data[right]);
    }
    if(data[mid] > data[right]) {
        Swap(&data[mid], &data[right]);
    }
    Swap(&data[mid], &data[right - 1]);
    return data[right - 1];
}
void QuickSortCore(int data[], int left, int right) {
    if(Cutoff <= right - left) {
        int pivot = CompareThreeNums(data, left, right);
        int low = left;
        int high = right - 1;
        while(TRUE) {
                while(data[++low] < pivot) ;
                while(data[--high] > pivot) ;
                if(low < high) {
                    Swap(&data[low], &data[high]);
                }
                else {
                    break;
                }
        }
        Swap(&data[low], &data[right - 1]); //将基准换到正确位置
        QuickSortCore(data, left, low - 1); //对基准左边序列递归
        QuickSortCore(data, low + 1, right);    //对基准右边序列递归
    }
    else {
        SimpleInsertSort(data, left, right); //对序列进行简单插入排序
    }
    return;
}
void QuickSort(int data[], int num) {
    QuickSortCore(data, 0, num - 1);
    return;
}
int main() {
    int num;
    scanf("%d", &num);
    for(int i = 0; i < num; i++) {
        scanf("%d", &Data[i]);
    }
    QuickSort(Data, num);
    for(int i = 0; i < num; i++) {
        printf("%d", Data[i]);
        if(i != num - 1) {
            printf(" ");
        }
    }
    return 0;
}

(四)归并排序 

归并排序的思路类似于快排,不过归并是通过将待排序列不断分割到每个序列只剩两个数字时排序,然后再两两合并实现排序文章来源地址https://www.toymoban.com/news/detail-682057.html

①递归版本

#include <stdio.h>
#include <stdlib.h>
#define MAX_NUM 100005
int DataToSort[MAX_NUM];
void Merge(int data[], int tmpData[], int leftStart, int rightEnd, int rightStart) {
    int leftEnd = rightStart - 1;
    int tmpStart = leftStart;
    int num = rightEnd - leftStart + 1;
    while(leftStart <= leftEnd && rightStart <= rightEnd) {
        if(data[leftStart] <= data[rightStart]) {
            tmpData[tmpStart++] = data[leftStart++];
        }
        else {
            tmpData[tmpStart++] = data[rightStart++];
        }
    }    
    while(leftStart <= leftEnd) {
        tmpData[tmpStart++] = data[leftStart++];
    }
    while(rightStart <= rightEnd) {
        tmpData[tmpStart++] = data[rightStart++];
    }
    for(int i = 0; i < num; i++, rightEnd--) {
        data[rightEnd] = tmpData[rightEnd];
    }
    return;
}
void MergeSortRecursiveVersionCore(int data[], int tmpData[],int leftStart, int rightEnd) {
    if(leftStart >= rightEnd) {
        return;
    }
    int mid = (leftStart + rightEnd) / 2;
    MergeSortRecursiveVersionCore(data, tmpData, leftStart, mid);
    MergeSortRecursiveVersionCore(data, tmpData, mid + 1, rightEnd);
    Merge(data, tmpData, leftStart, rightEnd, mid + 1);
    return;
}
void MergeSort(int data[], int num) {
    int* tmpData = (int*)malloc(sizeof(int) * num);
    MergeSortRecursiveVersionCore(data, tmpData, 0, num - 1);
    free(tmpData);
    return;
}
int main() {
    int num;
    scanf("%d", &num);
    for(int i = 0; i < num; i++) {
        scanf("%d", &DataToSort[i]);
    }
    MergeSort(DataToSort, num);
    for(int i = 0; i < num; i++) {
        printf("%d", DataToSort[i]);
        if(i != num - 1) {
            printf(" ");
        }
    }
    return 0;
}

②循环版本 

#include <stdio.h>
#include <stdlib.h>
#define MAX_NUM 100005
int DataToSort[MAX_NUM];
void Merge(int data[], int tmpData[], int leftStart, int rightStart, int rightEnd) {
    int leftEnd = rightStart - 1;
    int tmpStart = leftStart;
    int numElements = rightEnd - leftStart + 1;
    while(leftStart <= leftEnd && rightStart <= rightEnd) {
        if(data[leftStart] <= data[rightStart]) {
            tmpData[tmpStart++] = data[leftStart++];
        }
        else {
            tmpData[tmpStart++] = data[rightStart++];
        }
    }
    while(leftStart <= leftEnd) {
        tmpData[tmpStart++] = data[leftStart++];
    }
    while(rightStart <= rightEnd) {
        tmpData[tmpStart++] = data[rightStart++];
    }
    for(int i = 0; i < numElements; i++, rightEnd--) {
        data[rightEnd] = tmpData[rightEnd];
    }
    return;
}
/*length表示当前有序序列*/
void MergeCirculationSortCore(int data[], int tmpData[], int length, int totalNum) {
    /*i表示当前序列中左开头*/
    /*下面代码中传i, i + length, i + 2 * length - 1等都是坐标*/
    int i;
    for(i = 0; i <= totalNum - 2 * length; i += 2 * length) {    
        Merge(data, tmpData, i, i + length, i + 2 * length - 1);
    }
    /*如果剩余的序列已经小于等于两个length但依然比一个length长*/
    if(i + length < totalNum) {
        Merge(data, tmpData, i, i + length, totalNum - 1);
    }
    /*如果剩余的序列已经不足一个length*/
    else {
        for(int j = i; j < totalNum; j++) {
            tmpData[j] = data[j];
        }
    }
    return;
}
void MergeCirculationSort(int data[], int num) {
    int length = 1;
    while(length < num) {
        int* tmpData = (int*)malloc(sizeof(int) * num);
        /*第一次先对按length长度的序列排序*/
        MergeCirculationSortCore(DataToSort, tmpData, length, num);
        length *= 2;
        /*第二次对之前length长度的2倍的序列归*/
        MergeCirculationSortCore(tmpData, data, length, num);
        length *= 2;    
        free(tmpData);    
    }
    return;
}
int main() {
    int num;
    scanf("%d", &num);
    for(int i = 0; i < num; i++) {
        scanf("%d", &DataToSort[i]);
    }
    MergeCirculationSort(DataToSort, num);
    for(int i = 0; i < num; i++) {
        printf("%d", DataToSort[i]);
        if(i != num - 1) {
            printf(" ");
        }
    }   
    return 0;
}

到了这里,关于浙大数据结构之09-排序1 排序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构与算法】十大经典排序算法-插入排序

    🌟 个人博客: www.hellocode.top 🏰 Java知识导航: Java-Navigate 🔥 CSDN: HelloCode. 🌞 知乎 :HelloCode 🌴 掘金 :HelloCode ⚡如有问题,欢迎指正,一起学习~~ 插入排序(Insertion Sort)是一种简单直观的排序算法,其基本思想是将一个记录插入到已排好序的有序序列中,直到所有记录

    2024年02月13日
    浏览(76)
  • 【数据结构与算法】十大经典排序算法-快速排序

    🌟 个人博客: www.hellocode.top 🏰 Java知识导航: Java-Navigate 🔥 CSDN: HelloCode. 🌞 知乎 :HelloCode 🌴 掘金 :HelloCode ⚡如有问题,欢迎指正,一起学习~~ 快速排序(Quick Sort)是一种高效的排序算法,是对冒泡排序的优化。它采用分治法(Divide and Conquer)的思想,将待排序序列

    2024年02月13日
    浏览(58)
  • 数据结构和算法笔记4:排序算法-归并排序

    归并排序算法完全遵循分治模式。直观上其操作如下: 分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列。 解决:使用归并排序递归地排序两个子序列。 合并:合并两个已排序的子序列以产生已排序的答案。 我们直接来看例子理解算法的过程,下面是要排序

    2024年01月21日
    浏览(57)
  • 数据结构——排序算法之快速排序

        个人主页: 日刷百题 系列专栏 : 〖C/C++小游戏〗 〖Linux〗 〖数据结构〗   〖C语言〗 🌎 欢迎各位 → 点赞 👍+ 收藏 ⭐️+ 留言 📝  ​ ​ 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。 基本思想: 任取待排序元素序列中 的某元素作为基准值,按照

    2024年01月21日
    浏览(50)
  • 【数据结构与算法】排序算法(选择排序,冒泡排序,插入排序,希尔排序)

    基本概念这了就不浪费时间解释了,这四种都是很简单的排序方式,本专栏后续文章会出归并排序,计数排序,快速排序,堆排序,桶排序等排序算法,今天这篇文章中给出选择排序,冒泡排序,插入排序和希尔排序的实现; 如果发现文章中有错误,还请大家指出来,我会非

    2024年02月15日
    浏览(70)
  • 数据结构与算法-排序算法

    递归将整个函数的调用过程 调用过程 如何在CSDN博客中插入公式和各种符号 类似二叉树的后续遍历 递归行为和递归行为时间复杂度的估算 master 公式 : T ( n ) = a × T ( n b ) + O ( n d ) T(n) = a times T (frac{n}{b}) + O(n^d) T ( n ) = a × T ( b n ​ ) + O ( n d ) T ( n ) T(n) T ( n ) : 母问题的规模

    2024年02月15日
    浏览(46)
  • 算法 数据结构 递归插入排序 java插入排序 递归求解插入排序算法 如何用递归写插入排序 插入排序动图 插入排序优化 数据结构(十)

    1. 插入排序(insertion-sort):                                           是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入     算法稳定性:                  

    2024年02月09日
    浏览(49)
  • 数据结构算法练习 插入排序 冒泡排序

    插入排序 代码如下  package main import \\\"fmt\\\" func main() {     a := []int{4, 5, 6, 1, 3, 2}         b := insert(a)     for i := 0; i len(b); i++ {         fmt.Println(b[i])     } } func insert(a []int) []int {     if len(a) = 1 {                   如果数组长度小于等于1 不用排序直接返回          retur

    2024年02月08日
    浏览(49)
  • 数据结构与算法:插入排序&希尔排序

    假设现在你有一个有序的数组,你要把一个数据插入到数组中,保证插入后依然有序,要怎么做? 对于人来说,这个问题就像是在整理扑克牌,瞄一眼就知道应该插入什么位置。但是对于程序来说,就需要一一对比,直到找到一个位置 左边比它大,右边比它小 ,就算找到了

    2024年01月17日
    浏览(53)
  • 数据结构与算法—插入排序&选择排序

    目录 一、排序的概念 二、插入排序   1、直接插入排序  特性总结: 2、希尔排序 特性总结:  三、选择排序 1、直接选择排序  特性总结: 2、堆排序—排升序(建大堆) 向下调整函数 堆排序函数 特性总结: 代码完整版:   头文件  函数文件  测试文件 排序 :所谓排序,

    2024年01月20日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包