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

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

1.插入排序

思路:插入排序将一个数插入一个有序的数组里面,将这个数和数组元素挨着比较,直到他插入到合适的位置。

动画演示:

步骤:1.定义一个变量tmp保存要插入的数据

2.在循环中用tmp和有序数组中的元素比较(比方说要和a[end]比较,如果tmp<a[end]的话,就将a[end]右移动到a[end+1],如果tmp>a[end]的话就直接结束循环,因为已经找到了自己的位置,就是a[end+1].

3.当循环结束则表明已经找到了tmp的位置,下标为end+1,将tmp赋值给a[end+1]即可。

代码实现

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;
  }
}

直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定

2.希尔排序

希尔排序( 缩小增量排序 )

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。相当于分组的插入排序。

步骤:

1.将要排列的数据分为几组

2.每一组内进行插入排序。

3.缩小组数,当组数为1时,相当于直接插入排序。

void ShellSort(int* a, int n)
{
  int gap = 3;
      for (int i = 0; i < n - gap; i += gap)
      {
        int end = i;
        int tmp = a[end + gap];
        while (end >= 0)
        {
          if (tmp < a[end])
          {
            a[end + gap] = a[end];
            end -= gap;
          }
          else
          {
            break;
          }
        }
        a[end + gap] = tmp;
      }
    
  
}

上面代码为单组的排序,只有一组。

void ShellSort(int* a, int n)
{
  int gap = 3;
  for (int j = 0; j < gap; j++)
  {
    for (int i = j; i < n - gap; i += gap)
    {
      int end = i;
      int tmp = a[end + gap];
      while (end >= 0)
      {
        if (tmp < a[end])
        {
          a[end + gap] = a[end];
          end -= gap;
        }
        else
        {
          break;
        }
      }
      a[end + gap] = tmp;
    }
  }
    
  
}

加了循环,将每组都排好序,但是整体没有有序。当gap!=1时,属于预排序。每次循环减小gap的值。说明分组在变,然后在每一组里面继续排序,当gap等于1时,相当于插入排序。

void ShellSort(int* a, int n)
{
  int gap = 3;
  while (gap >= 1)
  {
    gap /= 2;
    for (int j = 0; j < gap; j++)
    {
      for (int i = j; i < n - gap; i += gap)
      {
        int end = i;
        int tmp = a[end + gap];
        while (end >= 0)
        {
          if (tmp < a[end])
          {
            a[end + gap] = a[end];
            end -= gap;
          }
          else
          {
            break;
          }
        }
        a[end + gap] = tmp;
      }
    }
  }
    
}

上面这种是一组排,会多套一层循环。如果使用下面的代码是一组没排完,就进行排下一组,一组中先把前两个元素排好,在排第二组的前两个元素,当排完每一组的前两个元素,又回到第一组,排第一组的前三个元素,排好前三个元素,接着排第二组的前三个元素,依次类推。

时间复杂度平均:O(N^1.3)

空间复杂度:O(1)

3.选择排序

基本思想:

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的

数据元素排完 。

步骤:1.定义两个变量maxi,mini分别记录要排序数据的最大值和最小值的下标。初始将·maxi,mini等于起始下标。

2.循环遍历要排序的数据,更新下标,如果有数据大于a[maxi],maxi更新为当前的i;如果有数据小于a[mini],mini更新为当前的i;

3.交换此时最小值和第一个数据的位置,最大值和最后一个数据的位置,已经保证了两个数据到他该有的位置。起始位置下标++,结束位置下标–;

4.然后就要排除已经排好的两个元素,现在maxi与mini起始下标就为第二个元素下标了。

5.循环条件起始位置下标小于结束位置下标。

动画演示:

代码实现:

void SelectSort(int* a, int n)
{
  int begin = 0;
  int end = n - 1;
  while (begin < end)
  {
    int maxi = begin;
    int mini = begin;
    for (int i = begin + 1; i <=end; i++)
    {
      if (a[i] > a[maxi])
      {
        maxi = i;
      }
      if (a[i] < a[mini])
      {
        mini = i;
      }
         }
    swap(&a[begin], &a[mini]);
    swap(&a[end], &a[maxi]);
    begin++;
    end--;
  }
  
}

如果你要排序的数据里面最大的数据不是第一个的话,上面的代码你会觉得是正确的,如果第一个数据是最大的,排序就不对了,到底是为什么呢?我们可以调试调试

修改代码为:

void SelectSort(int* a, int n)
{
  int begin = 0;
  int end = n - 1;
  while (begin < end)
  {
    int maxi = begin;
    int mini = begin;
    for (int i = begin + 1; i <=end; i++)
    {
      if (a[i] > a[maxi])
      {
        maxi = i;
      }
      if (a[i] < a[mini])
      {
        mini = i;
      }
         }
    swap(&a[begin], &a[mini]);
    if (maxi == begin)
    {
      maxi = mini;
    }
    swap(&a[end], &a[maxi]);
    begin++;
    end--;
  }
  
}

直接选择排序的特性总结:

时间复杂度:O(N^2)

空间复杂度:O(1)

4.堆排序

堆排序是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

void AdjustDwon(int* a, int n, int root)//向下调整建立大堆。
{
  int child = root * 2 + 1;
  while (child < n)
  {
    if (child+1<n &&a[child] < a[child + 1])
    {
      child = child + 1;
    }
    if (a[child] > a[root])
    {
      swap(&a[child], &a[root]);
      root = child;
      child = root * 2 + 1;
    }
    else
    {
      break;
    }
  }
}

上图为建立的大堆。

堆排序演示

5.冒泡排序

思路:

左边大于右边交换一趟排下来最大的在右边

代码实现:

void BubbleSort(int* a, int n)
{
  for (int i = 0; i < n-1; i++)
  {
    for (int j = 0; j < n - i-1; j++)
    {
      if (a[j + 1] < a[j])
      {
        swap(&a[j], &a[j + 1]);
      }
        }
  }
}

时间复杂度:O(N^2)

空间复杂度:O(1)

目录
相关文章
|
5月前
|
算法 搜索推荐
数据结构与算法学习十八:堆排序
这篇文章介绍了堆排序是一种通过构建堆数据结构来实现的高效排序算法,具有平均和最坏时间复杂度为O(nlogn)的特点。
127 0
数据结构与算法学习十八:堆排序
|
5月前
|
算法 搜索推荐
数据结构与算法学习十一:冒泡排序、选择排序、插入排序
本文介绍了冒泡排序、选择排序和插入排序三种基础排序算法的原理、实现代码和测试结果。
63 0
数据结构与算法学习十一:冒泡排序、选择排序、插入排序
|
7月前
|
存储 算法 搜索推荐
【初阶数据结构篇】冒泡排序和快速排序(中篇)
与直接插入排序法相比,比较次数一致,但冒泡排序的交换需要执行三次,而直接插入排序因为使用了tmp临时变量存储要插入的数据,只用执行一次,所以直接插入排序法效率明显更高。
47 1
|
7月前
|
搜索推荐 算法
【初阶数据结构篇】插入、希尔、选择、堆排序介绍(上篇)
堆排序(Heapsort)是指利⽤堆积树(堆)这种数据结构所设计的⼀种排序算法,它是选择排序的⼀ 种。它是通过堆来进⾏选择数据。需要注意的是排升序要建⼤堆,排降序建⼩堆。
40 0
|
7月前
|
算法
【初阶数据结构篇】堆的应用(堆排序与Top-K问题)
即求数据结合中前K个最⼤的元素或者最⼩的元素,⼀般情况下数据量都⽐较⼤。
82 0
|
4月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
455 9
|
4月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
70 1
|
2月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
176 77
|
27天前
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
30 11
|
1月前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。