• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > C语言演示对归并排序算法的优化实现

C语言演示对归并排序算法的优化实现

作者:FanYu Kong 字体:[增加 减小] 来源:互联网 时间:2017-05-28

FanYu Kong 通过本文主要向大家介绍了归并排序算法c语言,归并排序c语言,c语言归并排序代码,c语言实现归并排序,c语言归并排序递归等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

基础

如果有两个数组已经有序,那么可以把这两个数组归并为更大的一个有序数组。归并排序便是建立在这一基础上。要将一个数组排序,可以将它划分为两个子数组分别排序,然后将结果归并,使得整体有序。子数组的排序同样采用这样的方法排序,这个过程是递归的。

下面是示例代码:

#include "timsort.h"
#include <stdlib.h>
#include <string.h>

// 将两个长度分别为l1, l2的已排序数组p1, p2合并为一个
// 已排序的目标数组。
void merge(int target[], int p1[], int l1, int p2[], int l2);

void integer_timsort(int array[], int size){
  if(size <= 1) return;
 
  int partition = size/2;
  integer_timsort(array, partition);
  integer_timsort(array + partition, size - partition);
  merge(array, array, partition, array + partition, size - partition);
}

void merge(int target[], int p1[], int l1, int p2[], int l2){
  int *merge_to = malloc(sizeof(int) * (l1 + l2));

  // 当前扫描两数组的位置
  int i1, i2;
  i1 = i2 = 0;

  // 在合并过程中存放下一个元素的位置
  int *next_merge_element = merge_to;

  // 扫描两数组,将较小的元素写入
  // merge_to. 当两数相等时我们选择
  // 左边的, 因为我们想保证排序的稳定性
  // 当然对于integers这无关紧要,但这种想法是很重要的
  while(i1 < l1 && i2 < l2){
    if(p1[i1] <= p2[i2]){
      *next_merge_element = p1[i1];
      i1++;
    } else {
      *next_merge_element = p2[i2];
      i2++;
    }
    next_merge_element++;
  }

  // 如果有一个数组没有扫描完,我们直接拷贝剩余的部分
  memcpy(next_merge_element, p1 + i1, sizeof(int) * (l1 - i1));
  memcpy(next_merge_element, p2 + i2, sizeof(int) * (l2 - i2));

  // 现在我们已经将他们合并在了我们的额外的存储空间里了
  // 是时候转存到target了
  memcpy(target, merge_to, sizeof(int) * (l1 + l2));

  free(merge_to);
}

</div>
#include "timsort.h"
#include <stdlib.h>
#include <string.h>
 
// 将两个长度分别为l1, l2的已排序数组p1, p2合并为一个
// 已排序的目标数组。
void merge(int target[], int p1[], int l1, int p2[], int l2);
 
void integer_timsort(int array[], int size){
  if(size <= 1) return;
 
  int partition = size/2;
  integer_timsort(array, partition);
  integer_timsort(array + partition, size - partition);
  merge(array, array, partition, array + partition, size - partition);
}
 
void merge(int target[], int p1[], int l1, int p2[], int l2){
  int *merge_to = malloc(sizeof(int) * (l1 + l2));
 
  // 当前扫描两数组的位置
  int i1, i2;
  i1 = i2 = 0;
 
  // 在合并过程中存放下一个元素的位置
  int *next_merge_element = merge_to;
 
  // 扫描两数组,将较小的元素写入
  // merge_to. 当两数相等时我们选择
  // 左边的, 因为我们想保证排序的稳定性
  // 当然对于integers这无关紧要,但这种想法是很重要的
  while(i1 < l1 && i2 < l2){
    if(p1[i1] <= p2[i2]){
      *next_merge_element = p1[i1];
      i1++;
    } else {
      *next_merge_element = p2[i2];
      i2++;
    }
    next_merge_element++;
  }
 
  // 如果有一个数组没有扫描完,我们直接拷贝剩余的部分
  memcpy(next_merge_element, p1 + i1, sizeof(int) * (l1 - i1));
  memcpy(next_merge_element, p2 + i2, sizeof(int) * (l2 - i2));
 
  // 现在我们已经将他们合并在了我们的额外的存储空间里了
  // 是时候转存到target了
  memcpy(target, merge_to, sizeof(int) * (l1 + l2));
 
  free(merge_to);
}
</div>

我不会总是贴出完整的代码~

优化
现在,如果你是一个C程序员,你可能已经在吐槽了:我在每次合并过程中都申请并释放了一次额外存储空间(你可能也会不爽于我没有检查返回值是否为null,请无视之…如果这能让你感觉好一点)

这个问题只要一点点的改动就可以修正:

void merge(int target[], int p1[], int l1, int p2[], int l2, int storage[]);
void integer_timsort_with_storage(int array[], int size, int storage[]);

void integer_timsort(int array[], int size){
  int *storage = malloc(sizeof(int) * size);
  integer_timsort_with_storage(array, size, storage);
  free(storage);
}

void merge(int target[], int p1[], int l1, int p2[], int l2, int storage[]);
void integer_timsort_with_storage(int array[], int size, int storage[]);
 
void integer_timsort(int array[], int size){
  int *storage = malloc(sizeof(int) * size);
  integer_timsort_with_storage(array, size, storage);
  free(storage);
}

</div>

现在我们有了排序函数的最顶层,做了一些内存分配(setup)工作并将其传入调用中。这是我们将要开始优化工作的模版,当然最后实际可用的版本会更加复杂而不仅仅是优化一块内存空间。

现在我们有了基本的归并排序了,我们需要想想:我们能怎样来优化它?

一般来说我们不能指望对于每一种情况都能达到最优。归并排序的性能已经很接近比较排序的下界了。timsort的关键特性是极好的利用了数据中存在的规律。如果数据中存在普遍的规律,我们应该尽可能的利用他们,如果没有,我们的算法应该保证不会比普通的归并排序差太多。

如果你看过归并排序的实现,你会发现其实所有的工作都是在合并(merge)的过程当中完成的。所以优化的重点也就落在了这里。由此我们得出以下三点可能的优化途径:

1、能否使合并过程运行的更快?
2、能否执行更少的合并过程?
3、是否存在一些与其使用归并排序不如使用其他排序的情况?

以上三个问题的答案都是肯定的,并且这也是对归并排序进行优化最为普遍的途径。举例来说,递归的实现使得根据数组的规模使用不同的排序算法变的非常简单。归并排序是一个很好的通用性排序算法,(具有很好的渐进复杂度)但对小数组而言常数因子就显得愈发重要,当数组的大小小于某个值时(通常是7或者8左右)归并排序的性能频繁的低于插入排序。

这并不是timsort的原理,但是我们之后会用到插入排序,所以我们先开个小差。

最简单的:假设我们有一个具有n个元素的已排序数组,并且在尾端有第n+1个元素的位置。现在我们想要向里面添加一个新的元素并保持数组有序。我们需要为新元素找到一个合适的位置并将比它大的数向后移动。一种显而易见的做法是将新元素放到第n+1个位置上,然后从后向前两两交换直到到达正确的位置(对较大的数组这并不是最好的做法:你可能想要对数据进行二分查找(binary search)然后把剩下的元素不做比较的向后移动。但是对小数组来说这样的做法反而不是很好,due to cache effects)

这就是插入排序工作的方式:当你有了k个已排序的元素,将第k+1个元素插入其中,你就有了k+1个已排序的元素。反复如此直到整个数组有序。

下面是代码:

void insertion_sort(int xs[], int length){
  if(length <= 1) return;
  int i;
  for(i = 1; i < length; i++){
    // i之前的数组已经有序了,现在将xs[i]插入到里面
    int x = xs[i];
    int j = i - 1;

    // 将j向前移动直到数组头或者
    // something <= x, 并且其右边的所有的元素都已经
    // 右移了
    while(j >= 0 && xs[j] > x){
      xs[j+1], xs[j];
       j--;
    }  
    xs[j+1] = x;
  }
}

</div>
void insertion_sort(int xs[], int length){
  if(length <= 1) return;
  int i;
  for(i = 1; i < length; i++){
    // i之前的数组已经有序了,现在将xs[i]插入到里面
    int x = xs[i];
    int j = i - 1;
 
    // 将j向前移动直到数组头或者
    // something <= x, 并且其右边的所有的元素都已经
    // 右移了
    while(j >= 0 && xs[j] > x){
      xs[j+1], xs[j];
       j--;
    }  
    xs[j+1] = x;
  }
}
</div>

现在排序的代码会被修改为下面这样:

void integer_timsort_with_storage(int array[], int size, int storage[]){
  if(size <= INSERTION_SORT_SIZE){
    insertion_sort(array, size);
    return;
  }
}

</div>
void integer_timsort_with_storage(int array[], int size, int storage[]){
  if(size <= INSERTION_SORT_SIZE){
    insertion_sort(array, size);
    return;
  }
}
</div>


你可以在这里查看这个版本

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C语言开发之归并排序详解及实例
  • C语言 实现归并排序算法
  • 常用的C语言排序算法(两种)
  • 举例讲解C语言对归并排序算法的基础使用
  • C语言演示对归并排序算法的优化实现
  • C语言实现排序算法之归并排序详解

相关文章

  • 2017-05-28C++快速幂与大数取模算法示例
  • 2017-05-28浅谈时间戳与日期时间互转C语言
  • 2017-05-28c语言全盘搜索指定文件的实例代码
  • 2017-05-28C程序实现整数的素数和分解问题
  • 2017-05-28C++指向类成员函数的指针详细解析
  • 2017-05-28C语言实现的猴子分桃问题算法解决方案
  • 2017-05-28指向变量的常指针与指向常变量的指针详细解析
  • 2017-05-28C语言中多维数组的内存分配和释放(malloc与free)的方法
  • 2017-05-28统计输入字符各个字母出现频率的解题思路
  • 2017-05-28C++回文数及素数问题计算方法

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • error LNK2019: 无法解析的外部符号 问题的解决办法
    • 浅析C++中前置声明的应用与陷阱
    • C语言中快速排序和插入排序优化的实现
    • 减少C++代码编译时间的简单方法(必看篇)
    • VC多线程编程详解
    • 详解C++设计模式编程中责任链模式的应用
    • CStdioFile的用法详细解析
    • C语言编程中生成随机数的入门教程
    • c++重载的详细总结
    • 用C语言模仿Python函数的实例

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有