【数据结构】插入排序、选择排序、冒泡排序、希尔排序、堆排序

简介: 【数据结构】插入排序、选择排序、冒泡排序、希尔排序、堆排序

前言:生活中我们总是会碰到各种各样的排序,今天我们就对部分常用的排序进行总结和学习,今天的内容还是相对比较简单的一部分,各位一起加油哦!

d6dc0126edd141a985d72de501ef756b.jpg


插入排序

插入排序:我们可以通俗的理解成将一个数记录下来按其数值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。(由于动图画的实在太过于繁琐博主就画了一半,请见谅)

代码思路:由此图我们可以知道,我们用一个tmp记录后面一个元素,如果后面的比前面的小,就让前面的元素逐一和他比较并往后走,如果碰到比他小的就停下来插入到此位置。(不理解的话可以理解成我们平常玩的斗地主的牌,拿一张牌插到应该有的位置)。


代码实现

void InsertSort(int* a, int n)//插入排序
{
  for (int i = 0; i < n - 1; i++)//升序
  {
    int end = i;
    int tmp = a[end + 1];//保存后一个
    while (end >= 0)
    {
      if (tmp < a[end])//前一个大于后一个,让大的全部往后走
      {
        a[end + 1] = a[end];
        end--;
      }
      else
      {
        break;  
      }
    }
    a[end + 1] = tmp;//在把小的放在此时的位置
  }
}

测试函数:

void Test_InsertSort()
{
  int a[] = { 1,2,30,0,99,1,7,8,2,11,0,3,13 };
  InsertSort(a, sizeof(a) / sizeof(a[0]));
  PrintArray(a, sizeof(a) / sizeof(a[0]));
}

排序结果:


希尔排序

希尔排序:我们可以通俗的把待排的序列看成若干个子序列,然后对其分别进行直接插入排序,最后在对全部进行一次排序即可。(如下图所示)


我们可以理解成gap>1是预排序,目的是让它接近有序

gap == 1是直接插入排序,目的是让它有序。但是记住最后一定要让gap最后一次排序为1

代码思路:我们可以把每次排序写成一次插入排序,然后最后一让其间距不断的变化直到最后一次全部排序完成。

代码实现:

void ShellSort(int* a, int n)//希尔排序 
{
  int gap = n;
  while (gap)
  {
    gap = gap / 2 ;
    for (int i = 0; i < n - gap; i++)//升序
    {
      int end = i;
      int tmp = a[end + gap];//保存后一个
      while (end >= 0)
      {
        if (tmp < a[end])//前一个大于后一个,让大的全部往后走
        {
          a[end + gap] = a[end];
          end = end -gap;
        }
        else
        {
          break;
        }
      }
      a[end + gap] = tmp;//在把小的放在此时的位置
    }
  }
}


函数测试

void Test_ShellSort()
{
  int a[] = { 1,2,30,0,99,1,7,8,2,11,0,3,13 };
  ShellSort(a, sizeof(a) / sizeof(a[0]));
  PrintArray(a, sizeof(a) / sizeof(a[0]));
}

运行结果:


冒泡排序

冒泡排序:也是我们所碰到的最简单的一种排序,这种排序的思想是十分简单的,我们可以理解成每一趟找到序列中最大的一个或最小的元素,排序到序列的最左边(右边),然后排序序列的有效次数趟即可排序完成(如下图)。

代码实现:

void Swap(int* p1, int* p2)
{
  int tmp = *p1;
  *p1 = *p2;
  *p2 = tmp;
}

void BubbleSort(int* a, int n)//冒泡排序
{
  int i = 0;
  for (int j = 0; j < n - 1; j++)
  {
    for (i = 0; i < n - j -1; i++)//每一趟找到一个最大的
    {
      if (a[i] < a[i + 1])
      {
        Swap(&a[i], &a[i + 1]);
      }
    }
  }
}


函数测试:

void Test_BubbleSort()
{
  int a[] = { 1,2,30,0,99,1,7,8,2,11,0,3,13 };
  BubbleSort(a, sizeof(a) / sizeof(a[0]));
  PrintArray(a, sizeof(a) / sizeof(a[0]));
}

运行结果


选择排序

选择排序:我们现在一组有序序列中找到最大(最小)和第一个位置交换,然后再找次大的和第二个交换以此类推,最后我们即可得到一个有序序列。(如下图所示)


代码实现

void SelectSort(int*a ,int n)//选择排序
{
  //现在数组中找到最小的,然后和第一个位置交换
  //再找到次小的,和第二个位置交换
  int min = 0;
  for (int i = 0; i < n - 1 ; i++)
  {
    min = i;//最小的数的下标
    for (int j = i; j < n; j++)
    {
      if (a[min] > a[j])//找到最小位置的下标
      {
        min = j;
      }
    }
    if(min != i)
      Swap(&a[i], &a[min]);
  }
}


测试函数

void Test_SelectSort()
{
  //int a[] = { 1,2,30,0,99,1,7,8,2,11,0,3,13 };
  int a[] = { 1,0,0,0,99,1,7,8,2,9,0,3,0 };

  SelectSort(a, sizeof(a) / sizeof(a[0]));
  PrintArray(a, sizeof(a) / sizeof(a[0]));

}

运行结果:


但是上面这种方法我们一共要找n-1次,我们思考一下每一次查找的过程中,如果我们每次一起查找最大的和最小的,那我们不就提高了一倍的效率。所以我们只需要再引入一个max变量来帮助我们再记录一下这个值即可。

代码实现

void SelectSort_best(int* a, int n)//选择排序
{
  int begin = 0;
  int end = n - 1;
  while (begin < end)
  {
    int max = begin;
    int min = begin;
    int i = 0;
    for (i = begin + 1; i <= end; i++)//找最小的和最大的
    {
      if (a[i] < a[min])
      {
        min = i;
      }
      if (a[i] > a[max])
      {
        max = i;
      }
    }
    Swap(&a[begin], &a[min]);
    if (begin == max)//防止最大的和最小的是相同的
    {
      max = min;
    }
    Swap(&a[end], &a[max]);
    begin++;
    end--;
  }
}

运行结果依然和前面的相同,这里就不做更多的阐述了。


堆排序

堆排序:前面我们讲了的模拟实现,我们知道堆的父亲结点一定比它的孩子结点大(小),因此我们可以充分的利用这一点来进行排序。

首先我们们将数组中的元素,想象成一个堆,将其中的父亲结点全部向下调整。(如下图)

根据我们模拟建立的大堆或者小堆,将其堆顶元素和堆尾进行交换,因此我们可以得到一个最大(最小的元素)再堆底,然后再通过一个end记录尾部,交换一次减减一次,以此循环到end为0的时候即堆中所有元素也排序完成。(如下图所示)

代码实现:

void AdjustDown(int* a, int size, int parent)//向下调整
{
  assert(a);
  int child = parent * 2 + 1;//找到第一个孩子
  while (child < size)
  {
    //看俩个孩子谁更小,交小的那个与父亲去比较
    if (a[child] < a[child + 1] && (child + 1) < size)
    {
      child += 1;
    }
    if (a[parent] < a[child])
    {
      Swap(&a[parent], &a[child]);
      parent = child;//让父亲和儿子往下走
      child = child * 2 + 1;
    }
    else
    {
      break;
    }
  }
}

void HeapSort(int* a, int n)//堆排序
{
  int i = 0;
  for (i = (n - 1 - 1) / 2; i >= 0; i--)//建堆,只有父亲结点需要调整
  {
    AdjustDown(a, n, i);
  }
  int end = n - 1;
  while (end > 0)
  {
    Swap(&a[0], &a[end]);
    AdjustDown(a, end, 0);
    end--;
  }
}

函数测试:


void Test_HeapSort()
{
  int a[] = { 20,10,8,2,1,2,7 };
  HeapSort(a, sizeof(a) / sizeof(a[0]));
  PrintArray(a, sizeof(a) / sizeof(a[0]));
}


运行结果:


好啦,今天的内容就到这里啦,下期内容预告【快速排序的模拟实现】-- 四种方式


结语:今天的内容就到这里吧,谢谢各位的观看,如果有讲的不好的地方也请各位多多指出,作者每一条评论都会读的,谢谢各位。


相关文章
|
3月前
|
算法 搜索推荐 Shell
数据结构与算法学习十二:希尔排序、快速排序(递归、好理解)、归并排序(递归、难理解)
这篇文章介绍了希尔排序、快速排序和归并排序三种排序算法的基本概念、实现思路、代码实现及其测试结果。
52 1
|
3月前
|
算法 搜索推荐
数据结构与算法学习十八:堆排序
这篇文章介绍了堆排序是一种通过构建堆数据结构来实现的高效排序算法,具有平均和最坏时间复杂度为O(nlogn)的特点。
92 0
数据结构与算法学习十八:堆排序
|
3月前
|
算法 搜索推荐
数据结构与算法学习十一:冒泡排序、选择排序、插入排序
本文介绍了冒泡排序、选择排序和插入排序三种基础排序算法的原理、实现代码和测试结果。
25 0
数据结构与算法学习十一:冒泡排序、选择排序、插入排序
|
5月前
|
存储 算法 搜索推荐
【初阶数据结构篇】冒泡排序和快速排序(中篇)
与直接插入排序法相比,比较次数一致,但冒泡排序的交换需要执行三次,而直接插入排序因为使用了tmp临时变量存储要插入的数据,只用执行一次,所以直接插入排序法效率明显更高。
39 1
|
5月前
|
搜索推荐 算法
【初阶数据结构篇】插入、希尔、选择、堆排序介绍(上篇)
堆排序(Heapsort)是指利⽤堆积树(堆)这种数据结构所设计的⼀种排序算法,它是选择排序的⼀ 种。它是通过堆来进⾏选择数据。需要注意的是排升序要建⼤堆,排降序建⼩堆。
25 0
|
5月前
|
算法
【初阶数据结构篇】堆的应用(堆排序与Top-K问题)
即求数据结合中前K个最⼤的元素或者最⼩的元素,⼀般情况下数据量都⽐较⼤。
56 0
|
2月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
244 9
|
2月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
40 1
|
2月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
73 5
|
2月前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。