[数据结构]———交换排序

简介: [数据结构]———交换排序

1.交换排序



基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。


首先我们先介绍并创建两个函数,后面要用


第一个定义了一个名为Swap的函数

实现了交换两个整数指针所指向的值


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

第二个三数取中

定义了一个名为GetMidi的函数用于确定三个位置begin、midi和end在数组a中的中间值索引,确定并返回中间值的索引


int GetMidi(int* a, int begin, int end)
{
  int midi = (begin + end) / 2;
  // begin midi end 三个数选中位数
  if (a[begin] < a[midi])
  {
  if (a[midi] < a[end])
    return midi;
  else if (a[begin] > a[end])
    return begin;
  else
    return end;
  }
  else // a[begin] > a[midi]
  {
  if (a[midi] > a[end])
    return midi;
  else if (a[begin] < a[end])
    return begin;
  else
    return end;
  }
}


第三个 实现快速排序算法

函数QuickSort采用递归的方式对数组a进行排序。它接收三个参数:数组a、开始索引begin和结束索引end。

调用partSort函数,它用来进行划分操作,将数组中的元素分为两部分,以一个关键元素作为参考,小于等于关键元素的元素放在关键元素的左边,大于关键元素的元素放在关键元素的右边。partSort函数返回关键元素的索引位置keyi。

递归调用 QuickSort 函数对基准值左边的子数组进行排序,起始位置为 begin,结束位置为 keyi - 1。这样可以保证基准值左边的子数组是有序的。

最后,递归调用 QuickSort 函数对基准值右边的子数组进行排序,起始位置为 keyi + 1,结束位置为 end。这样可以保证基准值右边的子数组是有序的

void QuickSort(int* a, int begin, int end)
{
  if (begin >= end)
  return;
  int keyi = partSort(a, begin, end);
  QuickSort(a, begin, keyi - 1);
  QuickSort(a, keyi + 1, end);
}

2.冒泡排序



6f8b67933e744c5793680a9f8cdf2230.gif

代码解析

void BubbleSort(int* a, int n)//冒泡排序
{
  for (int j = 0; j < n; j++)
  {
  bool exchange = false;
  for (int i = 1; i < n - j; i++)
  {
    if (a[i - 1] > a[i])
    {
    Swap(&a[i - 1], &a[i]);
    exchange = true;
    }
  }
  if (exchange == false)
    break;
  }
}


image.png


冒泡排序的特性总结:


1. 冒泡排序是一种非常容易理解的排序

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

3. 空间复杂度:O(1)

4. 稳定性:稳定


3.快速排序



快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,


其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。


// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right)
{
if(right - left <= 1)
return;
// 按照基准值对array数组的 [left, right)区间中的元素进行划分
int div = partion(array, left, right);
// 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)
// 递归排[left, div)
QuickSort(array, left, div);
// 递归排[div+1, right)
QuickSort(array, div+1, right);
}

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,同学们在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

将区间按照基准值划分为左右两半部分的常见方式有:


1. hoare版本

image.png


在快速排序算法中,需要在找到左边比关键值大的元素和右边比关键值小的元素之后才进行元素交换,以确保左边的元素都比关键值小,右边的元素都比关键值大。但是在代码中目前的交换步骤存在问题,因为在while循环结束之后直接进行了一次元素交换,而应该是在找到左右指针位置后再进行交换。


int QuickSort1(int* a, int begin, int end)
{
  int midi = GetMidi(a, begin, end);
  Swap(&a[midi], &a[begin]);
  int left = begin, right = end;
  int keyi = begin;
  while (left < right)
  {
  // 右边找小
  while (left < right && a[right] >= a[keyi])
  {
    --right;
  }
  // 左边找大
  while (left < right && a[left] <= a[keyi])
  {
    ++left;
  }
  Swap(&a[left], &a[right]);
  }
  Swap(&a[left], &a[keyi]);
  return left;
}


image.png


2. 挖坑法

6f8b67933e744c5793680a9f8cdf2230.gif


代码解析

挖坑法:

使用挖坑法的思想,即每次在处理完左右指针位置的元素后,将最初选择的“坑”填入正确的位置。

函数逻辑:

首先在数组中选择基准值(pivot)并与起始位置的元素交换,这里选择的基准值为a[begin]。

使用两个指针begin和end分别指向数组的起始和末尾,开始移动指针以找到需要交换的元素。

当begin小于end时,进行以下操作:

从右侧开始,找到第一个比基准值小的元素,将其填入“坑”中,同时更新“坑”的位置和end指针的位置。

从左侧开始,找到第一个比基准值大的元素,将其填入之前右侧留下的“坑”中,同时更新“坑”的位置和begin指针的位置。

最后,将基准值填入最后的“坑”中,返回该“坑”的位置,用于分割左右子数组。

返回值:

函数返回的是基准值在排序后的位置,用于在递归调用中分割左右子数组。

//2.挖坑法
int QuickSort2(int* a, int begin, int end)
{
  int midi = GetMidi(a, begin, end);
  Swap(&a[midi], &a[begin]);
  int key = a[begin];
  int hole = begin;
  while (begin < end)
  {
  // 右边找小,填到左边的坑
  while (begin < end && a[end] >= key)
  {
    --end;
  }
  a[hole] = a[end];
  hole = end;
  // 左边找大,填到右边的坑
  while (begin < end && a[begin] <= key)
  {
    ++begin;
  }
  a[hole] = a[begin];
  hole = begin;
  }
  a[hole] = key;
  return hole;
}


image.png


3. 前后指针版本

6f8b67933e744c5793680a9f8cdf2230.gif


代码解析

将数组分成两个子数组,左边的子数组中的元素都小于等于中间元素,右边的子数组中的元素都大于等于中间元素,并返回中间元素的索引


int QuickSort3(int* a, int begin, int end)
{
  int midi = GetMidi(a, begin, end);
  Swap(&a[midi], &a[begin]);
  int keyi = begin;
  int prev = begin;
  int cur = prev + 1;
  while (cur <= end)
  {
  if (a[cur] < a[keyi] && ++prev != cur)
    Swap(&a[prev], &a[cur]);
  ++cur;
  }
  Swap(&a[prev], &a[keyi]);
  keyi = prev;
  return keyi;
}

image.png

相关文章
|
1月前
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
23 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
1月前
|
存储 搜索推荐 算法
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
【用Java学习数据结构系列】七大排序要悄咪咪的学(直接插入,希尔,归并,选择,堆排,冒泡,快排)以及计数排序(非比较排序)
23 1
|
1月前
|
搜索推荐 索引
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(二)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理
|
1月前
|
搜索推荐 C++
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(一)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理
|
1月前
|
算法
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
蓝桥杯宝藏排序 | 数据结构 | 快速排序 归并排序
05_用一个栈实现另一个栈的排序
05_用一个栈实现另一个栈的排序
|
1月前
|
人工智能 搜索推荐 算法
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理(三)
【初阶数据结构】深度解析七大常见排序|掌握底层逻辑与原理
|
3月前
|
C语言
数据结构——排序【上】
数据结构——排序【上】
|
3月前
|
搜索推荐 算法 测试技术
【数据结构】排序
【数据结构】排序
|
4月前
|
搜索推荐
【数据结构常见七大排序(三)上】—交换排序篇【冒泡排序】And【快速排序】
【数据结构常见七大排序(三)上】—交换排序篇【冒泡排序】And【快速排序】
【数据结构常见七大排序(三)上】—交换排序篇【冒泡排序】And【快速排序】