排序(详解)下

简介: 排序(详解)

快排(非递归)


21ad5c6f49abc5592453340159a81ded_47c9d2a8da8142b0b0deba0a14a88a7d.png

da2fcd1d6aaea4c2c43bf65566bb172f_a37eb2a230744658b1d63cc6300bab01.png


算法实现


void Quicksortnon(int* a, int begin, int end)
{
  SK sk;
  SKinit(&sk);
  SKpush(&sk, begin);
  SKpush(&sk, end);
  while (!SKempty(&sk))
  {
  int right = SKtop(&sk);
  SKpop(&sk);
  int left = SKtop(&sk);
  SKpop(&sk);
  int key = Partsort3(a, left, right);
  if (key + 1 < right)
  {
    //左边先入栈
    SKpush(&sk, key + 1);
    SKpush(&sk, right);
  }
  if (left < key - 1)
  {
    //右边后入栈
    SKpush(&sk, left);
    SKpush(&sk, key - 1);
  }
  }
}


快排的特点


综合性能较好,适应场景较多

时间复杂度:O(N*logN)

空间复杂度:O(logN)

稳定性:不稳定


归并排序(递归)


思路:将已有序的子序列合并,得到完全有序的序列;若子序列未有序,先使子序列有序,再进行合并(符合分治法的思想)


1b04f6ba88fbbad5398fe494ae9c76df_1b7e9fd1397147019478021e76b60330.png


算法实现


void mergesort(int* a, int begin, int end, int* tmp)
{
    //递归结束条件
  if (begin >= end)
  {
  return;
  }
  //将数组平分
  int mid = begin + (end - begin) / 2;
  //进行递归
  mergesort(a, begin, mid, tmp);
  mergesort(a, mid+1, end, tmp);
  //归并  取小的尾插
  int begin1 = begin;
  int end1 = mid;
  int begin2 = mid + 1;
  int end2 = end;
  int i = begin;
  while (begin1 <= end1 && begin2 <= end2)
  {
  if (a[begin1] <= a[begin2])
  {
    tmp[i++] = a[begin1++];
  }
  else
  {
    tmp[i++] = a[begin2++];
  }
  }
  //由于两部分不可能同时结束,所以还需要对剩余的数据进行插入
  while (begin1 <= end1)
  {
  tmp[i++] = a[begin1++];
  }
  while (begin2 <= end2)
  {
  tmp[i++] = a[begin2++];
  }
  //按照相应位置进行拷贝回原数组
  memcpy(a+begin, tmp+begin, sizeof(int) * (end - begin + 1));
}
void Mergesort(int* a, int n)
{
  int* tmp = (int*)malloc(n * sizeof(int));
  if (tmp == NULL)
  {
  perror("malloc fail");
  return;
  }
  int begin = 0;
  int end = n - 1;
  //如果直接在归并函数中递归,每次都会开辟空间,导致程序崩溃
  //所以创建子函数进行归并
  mergesort(a, begin, end, tmp);
  free(tmp);
  tmp = NULL;
}


归并排序(非递归)


b03a87891d7ee5c7b8776696f9109b96_f6f801cdafbd419e8fdcf9df44cc2101.png


算法实现


void Mergesortnon(int* a, int n)
{
  //开辟空间,用于排序
  int* tmp = (int*)malloc(n * sizeof(int));
  if (tmp == NULL)
  {
  perror("malloc fail");
  return;
  }
  int gap = 1;
  while (gap < n)
  {
  int j = 0;
  //每组gap个数据  
  for (j = 0; j < n; j += 2 * gap)
  {
    //取小尾插,进行归并
    int begin1 = j;
    int end1 = j + gap - 1;
    int begin2 = j + gap;
    int end2 = j + 2 * gap - 1;
    int i = j;
    while (begin1 <= end1 && begin2 <= end2)
    {
    if (a[begin1] <= a[begin2])
    {
      tmp[i++] = a[begin1++];
    }
    else
    {
      tmp[i++] = a[begin2++];
    }
    }
    //由于两部分不可能同时结束,所以还需要对剩余的数据进行插入
    while (begin1 <= end1)
    {
    tmp[i++] = a[begin1++];
    }
    while (begin2 <= end2)
    {
    tmp[i++] = a[begin2++];
    }
    //排序之后,按照位置进行拷贝回原数组
    memcpy(a+j, tmp+j, (end2 - j + 1) * sizeof(int));
  }
  gap *= 2;
  }
  free(tmp);
  tmp = NULL;
}


图示分析似乎没有什么问题,但如果再向数组中加一个数,结果会怎么样呢?


ca1d47d76baae4adfeb6e9c5e2d2483d_4438e6d9f5184a6c95fc63bfa3886a16.png


如果再向数组中加入一个数,结果又是怎么样的呢?


393516f7d16d01f4d15b68721f98967c_76979c9765f7482392e9b8338efa805d.png


目前出现的这两种情景,上面都没有考虑到,那又该如何解决呢?

先将每次分组的数据下标打印出来,再进行分析


ded6483e8a14fe33fac9bb7da0aecd4e_8be55850e57945f5ab6fda2be579d817.png


总结下来越界有三种可能


前一组的end1越界

后一组全部越界

后一组end2越界

解决办法


直接跳出循环,剩余的数据不再临时开辟的空间中进行排序

直接跳出循环,剩余的数据不再临时开辟的空间中进行排序

修改end2的数值,end2=n-1 再进行排序

优化后


void Mergesortnon(int* a, int n)
{
  //开辟空间,用于排序
  int* tmp = (int*)malloc(n * sizeof(int));
  if (tmp == NULL)
  {
  perror("malloc fail");
  return;
  }
  int gap = 1;
  while (gap < n)
  {
  int j = 0;
  //每组gap个数据  
  for (j = 0; j < n; j += 2 * gap)
  {
    //取小尾插,进行归并
    int begin1 = j;
    int end1 = j + gap - 1;
    int begin2 = j + gap;
    int end2 = j + 2 * gap - 1;
    int i = j;
    //第一组end1越界
    if (end1 >= n)
    {
       printf("[%d,%d]",begin1,end1);
    break;
    }
    //第二组全部越界
    if (begin2 >= n)
    {
       printf("[%d,%d]",begin1,end1);
    break;
    }
    //第二组部分越界
    if (end2 >= n)
    {
       //修改end2,继续归并
    end2 = n - 1;
    }
    printf("[%d,%d][%d,%d]  ", begin1, end1, begin2, end2);
    while (begin1 <= end1 && begin2 <= end2)
    {
    if (a[begin1] <= a[begin2])
    {
      tmp[i++] = a[begin1++];
    }
    else
    {
      tmp[i++] = a[begin2++];
    }
    }
    //由于两部分不可能同时结束,所以还需要对剩余的数据进行插入
    while (begin1 <= end1)
    {
    tmp[i++] = a[begin1++];
    }
    while (begin2 <= end2)
    {
    tmp[i++] = a[begin2++];
    }
    //排序之后,按照位置进行拷贝回原数组
    memcpy(a+j, tmp+j, (end2 - j + 1) * sizeof(int));
  }
  gap *= 2;
  printf("\n");
  }
  free(tmp);
  tmp = NULL;
}


eac74451259ebbd34bbe3189f4bc0283_2f843e5b64e84497afa1ea4ea5a67383.png


归并排序的特点


时间复杂度:O(N*logN)

空间复杂度:O(N)

稳定性:稳定


计数排序


思路:

先统计相同元素出现的次数,然后开辟相对大小的空间(最大值-最小值+1),将相同元素按照对应的下标中赋以其出现的次数


用于计数的数组的下标时其对于的值(相对)的个数,某个值出现几次,便会被计数几次


3b6397a3d2ba3f29b44b2dd92947d7d9_13d9852b691d4851a5adada397530212.png


算法实现


void Countsort(int* a, int n)
{
  int max = a[0];
  int min = a[0];
  int i = 0;
  for (i = 0; i < n; i++)
  {
  if (a[i] > max)
  {
    max = a[i];
  }
  if (a[i] < min)
  {
    min = a[i];
  }
  }
  int range = max - min + 1;
  //统计次数
  int* tmp = (int*)malloc(sizeof(int) * range);
  memset(tmp, 0, sizeof(int) * range);
  for (i = 0; i < n; i++)
  {
  tmp[a[i] - min]++;
  }
  //排序
  int j = 0;
  for (i = 0; i < range; i++)
  {
  while (tmp[i]--)
  {
    a[j] = i + min;
    j++;
  }
  }
  free(tmp);
  tmp = NULL;
}


计数排序的特点


当数据范围比较集中时,效率很高

时间复杂度:O(N,range)

空间复杂度:O(range)

稳定性:稳定


目录
相关文章
|
UED 开发者 容器
Flutter&鸿蒙next 中的 Expanded 和 Flexible 使用技巧详解
在 Flutter 开发中,Expanded 和 Flexible 是两个常用的布局控件,用于管理 UI 布局的空间分配。Expanded 使子组件占据主轴上的所有剩余空间,而 Flexible 允许通过 flex 参数按比例分配空间。掌握两者的区别和使用场景,可以让你在构建复杂 UI 时更加得心应手。
655 1
|
Linux 开发者 iOS开发
跨界英雄Python:一招搞定跨平台兼容性难题🎯
【10月更文挑战第2天】Python 作为一种现代且灵活的编程语言,在处理跨平台兼容性方面表现出色。其标准库如 `os` 和 `pathlib` 以及第三方库使开发者能轻松编写高可移植性的代码。通过文件系统操作、执行外部命令及使用 Tkinter 创建 GUI 等示例,Python 展现了其强大的跨平台能力,让开发者专注于业务逻辑而非平台差异。掌握这些技巧,你将能在不同操作系统间游刃有余。
264 4
【Java基础面试十三】、面向对象的三大特征是什么?
这篇文章介绍了面向对象程序设计的三大基本特征:封装、继承和多态,其中封装隐藏对象实现细节,继承实现软件复用,多态允许子类对象表现出不同的行为特征。
【Java基础面试十三】、面向对象的三大特征是什么?
9-17|远端执行date命令报错
9-17|远端执行date命令报错
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
310 4
ArcGIS:如何进行离散点数据插值分析(IDW)、栅格数据的重分类、栅格计算器的简单使用、缓冲区分析、掩膜?
ArcGIS:如何进行离散点数据插值分析(IDW)、栅格数据的重分类、栅格计算器的简单使用、缓冲区分析、掩膜?
974 0
|
JavaScript 安全
Cannot read property ‘querySelectorAll‘ of undefined问题解决
Cannot read property ‘querySelectorAll‘ of undefined问题解决
389 2
|
定位技术 计算机视觉 Windows
生态学建模:增强回归树(BRT)预测短鳍鳗生存分布和影响因素
生态学建模:增强回归树(BRT)预测短鳍鳗生存分布和影响因素
|
开发工具 图形学 git
寻路库recastnavigation改造
寻路库recastnavigation改造
504 0
Visual Studio 2019 切换界面显示语言为中文
Visual Studio 2019 设置中文/英文界面
1079 0