数据结构之排序【直接插入排序和希尔排序的实现及分析】

简介: 数据结构之排序【直接插入排序和希尔排序的实现及分析】

引言:

今天天气还是依然的冷,码字越来越不容易了,本来上次写了一个比较好的引言,但是因为电脑第二天没电,并且我没有保存,现在找不到了,所以今天我们的引言就这样吧!今天给大家介绍一下有关数据结构中的排序的内容,因为来不及一口气把所有的排序学完并且学明白,我们就把这些排序给分开进行讲解。所以今天我们学的是直接插入排序和希尔排序相关的知识分析和代码的实现。


1.排序的概念及其应用

1.1 排序的概念

排序:所谓的排序就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],并且r[i]在r[j]之前,而在排序后的序列中,r[i]任然在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的

内部排序:数据元素全部在内存中的排序。

外部排序:数据元素太多不能同时放在内存中,根据排序的过程的要求不能在内存之间移动数据的排序


1.2 排序的应用(此时就可以去淘宝上截图一下,就是价格的排序之类的)(可以很好的充当一个筛选的作用)


1.3 常见的排序:插入排序(直接插入排序、希尔排序) 选择排序(选择排序、堆排序) 交换排序(冒泡排序、快速排序)归并排序


2.直接插入排序的实现及分析

2.1 基本思想:

直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按期关键码值的大小逐个插入到一个已经排好的有序序列中,直到所有的记录插入完为止,然后就得到一个新的有序序列。

2.2 生活中的实例:我们玩扑克牌的时候,就是用了插入排序的思想,如图:


24.png


2.3 具体的实现原理:当我们想要插入第i(i>=1)个元素时,前面的arr[0],arr[1],arr[2],……,arr[i-1]已经排好序,此时用arr[i]的排序码值与arr[i-1],arr[i-1],……,的排序码值进行比较,找到合适的位置将arr[i]插入,原来位置上的元素按照顺序向后移动


2.4 具体代码的实现(最详细的注释)

void InsertSort(int* arr, int n)
{
  //此时这种排序方法是有要求的,比如一定是要要求这个数组中的元素已经是有序的了(所以此时我们排序是在默认这个数组有序的情况下进行的)
  //此时将这个问题拆分一下(变成小问题来解决)
  //拆分原理:(关键)此时假设[0,end]中的元素已经是有序的了,然后我们把我们想要插入的元素放在end+1的位置然后按照顺序插入进去,然后使[0,end+1]整个数组也有序(并且此时假设都是以升序来进行的排序)
  int i = 0;
  for (i = 0; i < n - 1; i++)//此时这个位置我们控制成了n-1,但我们要知道次数是到n-2就会结束的(并且此时因为下标的原理,此时这样写就是刚好,不然就会导致end+1的位置造成越界)
  {
    //这个for循环就是为了控制(end ,控制了end也就是控制了有顺序的元素的个数)插入的元素的个数(此时有了这个for循环就可以使我想插入几个元素就比较几次),显而易见,下面的那个while循环只是为了控制顺序插入一个数据(而此时有了这个for循环就可以使我顺序插入n个数据了)
    //int end;//此时就是假设,我也不知道此时的end是什么东西,唯一知道的就是[0,end]此时是一个有序的数组
    //此时想要控制插入的元素的个数,那么此时就可以知道end的值了,所以要对end进行初始化,所以就不可以按照上面那样写了,下成下面这个样子就行
    int end = i;
    int tmp = arr[end + 1];//此时为了进行排序,会进行位置的移动,所以为了防止end+1位置被覆盖,所以此时我可以把我的end+1的位置给先存起来(以便于待会可以更好的拿出来使用)
    while (end >= 0)//此时进行码值的比较的时候(会有一个特殊的情况:就是我想要插入的元素的码值比数组中的元素的码值都要小),所以此时我就要比较很多次,假如比end=0元素的值都要小,此时end再减减,就会导致end=-1,所以此时就会导致越界访问,所以此时我们这个循环就一定要把这个比较的次数(也就是end--)给控制好,让其不为<0,所以此时这个循环的条件就是end>=0,才进行元素的调换
    {
      if (arr[end] > tmp)//此时的这个tmp就是表示我要插入的值(并且此时这个我要插入的值就是在end+1,这个数组的位置处),所以我此时就可以进行比较码值的大小,这样就可以把我想要排序的元素按照顺序从end+1的位置插入到他合适的位置处
      {
        arr[end + 1] = arr[end];//这个位置就是进行排序嘛,就是码值的比较,然后我要排序的元素此时就可以从数组中的最后一个位置慢慢的移动到我的合适的位置
        end--;//这个end--的意思就是慢慢的向前走的意思,直到找到合适的位置或者走到数组的最前面(也就是end=0的位置)(这样我就可以跟数组中的所有元素进行比较了)
      }
      else
      {
        //此时这个else的意思是显而易见的:就是我想要插入的元素比数组中的元素的码值大了,此时就可以直接进行插入了
        //但是此时这个直接插入在这个位置就有一个小细节(还是当我想要插入的元素的码值比我数组中的都小的时候出现的情况),因为当我想插入的数据是最小的时候,程序来到这个位置就会导致我数组中的元素每个都往后移动了一位,然后此时就导致end--,导致此时的end变成了-1,因为我刚刚的end还是0吗,符合循环的条件(但是最后一次减减才导致的-1),所以此时如果程序来到这个位置,此时的end就是还是-1,所以在这个位置插入元素就又会导致越界的问题了,所以此时这个else并不适合插入数据
        break;//所以这个else只适合用break跳出去
      }
    }
    //并且此时程序来到这个位置就是说明循环不满足条件或者是else中的break起了作用,所以此时这个位置就可以放心的把我的数据给插入到这个数组之中去了
    arr[end + 1] = tmp;//因为end+1就可以防止end=-1的情况,并且也符合正常的元素的比较后的插入(就是在这个元素的后一个位置插入,也就是数组中的end+1的位置)
  }
}

2.5 直接插入排序的测试

void PrintArr(int* arr, int sz)
{
  int i;
  for (i = 0; i < sz; i++)
  {
    printf("%d ",arr[i]);
  }
  printf("\n");
}
void TestInsertSort()
{
  int arr[] = { 6,3,7,4,10,9,2,1,5,8 };
  InsertSort(arr, sizeof(arr) / sizeof(arr[0]));
  PrintArr(arr, sizeof(arr) / sizeof(arr[0]));
}
#include<stdio.h>
int main()
{
  TestInsertSort();
  return 0;
}

25.png


3.希尔排序的实现及分析

希尔排序又称为缩小增量法。


3.1 希尔排序的基本思想:先选定一个整数,把待排序文件中所有记录分成一个组,所有距离为1的记录在用一个组内,并对每一组的记录进行排序。然后,取,重复上述分组和排序的工作。当到达距离=1时,所有记录在统一组内排好序。

就是一个直接插入排序的优化(但是好像很高级)

此时这个希尔排序进行排序是将排序过程分为了两步

3.1.1.先进行预排序,让数组接近有序

3.1.2.然后再进行直接插入排序


3.2 此时就会涉及到一个叫gap的变量,我们可以对数组中间隔为gap的元素先进行一个预排序,gap由大变小。

特点:gap越大,大的数可以越快的到后面,小的数可以越快的到前面,gap越小,越接近有序,gap=1时就是我的直接插入排序


26.jpeg

代码实现:(具体原理就是直接插入排序的原理,重点就是如何理解这个gap)

void ShellSort(int* arr, int n)
{
  //int gap = 3;//因为我并不知道我需要排序的数据的个数,所以这个位置我们不可以将gap给固定掉
  int gap = n;
  while (gap > 1)
  {
    gap = gap / 2;
    //gap = gap / 3 + 1;//这两种都可以
    //此时这个位置中gap>1就都是预排序
    //gap==1时就是直接插入排序
  }
  int i = 0;
  //此时下面的代码就是为了把数组中间隔为gap的多组数据同时进行排序
  for (i = 0; i < n - gap; i++)
  {
    int end = i;
    int tmp = arr[end + gap];
    while (end >= 0)
    {
      if (arr[end] > tmp)
      {
        arr[end + gap] = arr[end];
        end = end - gap;
      }
      else
      {
        break;
      }
    }
    arr[end + gap] = tmp;
  }
}


相关文章
|
16天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
43 1
|
2月前
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
34 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
2月前
|
存储 搜索推荐 算法
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
29 1
|
2月前
|
算法
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
|
1月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
159 9
|
1月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
26 1
|
18天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
39 5
|
1月前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
1月前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
1月前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
51 4

热门文章

最新文章