数据结构第六课 -----排序-2

简介: 数据结构第六课 -----排序

数据结构第六课 -----排序-1

https://developer.aliyun.com/article/1498935


快速排序

hoare版本

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

f5b767a426b56e2489c1e70594d17aa3_8d3a2ed9dd634e0bb0f0c82707818405.png


这个图可能有点简陋

时间复杂度:每一次都会把当前数组的每个元素遍历一遍,然后再把key交换, 需要进行log(n)次递归

时间复杂度是:O(n*log(n))


复杂的话,就如同这个一样,这种情况就是有n层, 时间复杂度就是 1+2+3+…+n, 所以时间复杂度就是O(n^2)

//快速排序
void QuickSrot(int* a, int begin, int end)
{
  //当只有一个元素就不用进行了
  if (begin >= end)
    return;
  int key = begin;
  int left = begin;//这里不能begin加1 否则在遇到有序的时候就会排序出错
  int right = end;
  while (left < right)
  {
    // 找最小
    while (left < right)
    {
      if (a[right] < a[key])
      {
        break;
      }
      right--;
    }

    // 找最大
    while (left < right)
    {
      if (a[left] > a[key])
      {
        break;
      }
      left++;
    }
    
    excheng(&a[right], &a[left]);
  }
  excheng(&a[right], &a[key]);
  //左
  QuickSrot(a, begin, right - 1);
  // 右
  QuickSrot(a, right + 1, end);
}


优化点

三数取中

思路:

我们可以在数组的前后和中间选取中位数,然后把中位数和开头进行交换,

int TriNum(int *a,int begin, int end)
{
  int mid = (begin - end) / 2 + end;
  if (begin > end)
  {
    if (end > mid)
    {
      return end;
    }
    else if(begin < mid)
    {
      return begin;
    }
    return mid;
  }
  else
  {
    if (begin > mid)
    {
      return begin;
    }
    else if (end < mid)
    {
      return end;
    }
    else
      return mid;
  }
}
//快速排序
void QuickSrot(int* a, int begin, int end)
{
  //当只有一个元素就不用进行了
  if (begin >= end)
    return;
  //三数取中
  int key = 0;
  key = TriNum(a, begin, end);
  exchange(&a[begin], &a[key]);
  key = begin;
  int left = begin;
  int right = end;
  //普通方法
  //int key = begin;
  //int left = begin;//这里不能begin加1 否则在遇到有序的时候就会排序出错
  //int right = end;
  while (left < right)
  {
    // 找最小
    while (left < right)
    {
      if (a[right] < a[key])
      {
        break;
      }
      right--;
    }

    // 找最大
    while (left < right)
    {
      if (a[left] > a[key])
      {
        break;
      }
      left++;
    }
    
    excheng(&a[right], &a[left]);
  }

  excheng(&a[right], &a[key]);
  //左
  QuickSrot(a, begin, right - 1);
  // 右
  QuickSrot(a, right + 1, end);
}


小区间优化

当我们在使用快速排序的时候,一直排序知道递归到还剩下该数组的10%的数没有排序,我们如果使用递归就很对栈的空间浪费很大。那我们可以选择使用插入排序,

//快速排序
void QuickSrot(int* a, int begin, int end)
{
  //当只有一个元素就不用进行了
  if (begin >= end)
    return;
  if (end - begin  + 1 <= 10)
  {
    //插入排序
    InsertSort(a + begin, end - begin + 1);//我们要清楚要从哪里开始插入排序
  }
  else
  {
    //三数取中
    int key = 0;
    key = TriNum(a, begin, end);
    excheng(&a[begin], &a[key]);
    key = begin;
    int left = begin;
    int right = end;
    //普通方法,有可能会栈溢出
    //int key = begin;
    //int left = begin;//这里不能begin加1 否则在遇到有序的时候就会排序出错
    //int right = end;
    while (left < right)
    {
      // 找最小
      while (left < right)
      {
        if (a[right] < a[key])
        {
          break;
        }
        right--;
      }

      // 找最大
      while (left < right)
      {
        if (a[left] > a[key])
        {
          break;
        }
        left++;
      }

      excheng(&a[right], &a[left]);
    }

    excheng(&a[right], &a[key]);
    //左
    QuickSrot(a, begin, right - 1);
    // 右
    QuickSrot(a, right + 1, end);
  }
  
}

挖坑法

//挖坑法
void QuickSrot2(int* a, int begin, int end)
{
  if (begin >= end)
    return;
  if (end - begin + 1 <= 10)
  {
    InsertSort(a + begin, end - begin + 1);
  }
  else
  {
    //三数取中
    int key = TriNum(a, begin, end);
    excheng(&a[key], &a[begin]);
    //坑
    key = begin;
    int num = a[key];
    int left = begin;
    int right = end;
    while (left < right)
    {
      //找小
      while (left < right)
      {
        if (a[right] < num)
        {
          a[key] = a[right];
          key = right;
          break;
        }
        right--;
      }
      //找大
      while (left < right)
      {
        if (a[left] > num)
        {
          a[key] = a[left];
          key = left;
          break;
        }
        left++;
      }
    }
    a[key] = num;
    //左
    QuickSrot(a, begin, right - 1);
    // 右
    QuickSrot(a, right + 1, end);

  }
  
  

}

前后指针版本

思路:

cur遇见比key大的值,cur++

cur遇见比key小的值,prev++,交换prev和cur的值交换,然后cur++

//前后指针版本
// 快速排序版本3
void QuickSrot3(int* a, int begin, int end)
{
  if (begin >= end)
    return;
  int key = TriNum(a, begin, end);
  excheng(&a[key], &a[begin]);
  key = begin;
  int prev = begin;
  int cur = begin + 1;
  while (cur <= end)
  {
    //cur 比较
    if (a[cur] < a[key] && ++prev != cur)//增加++prev != cur可以有效解决相同位置进行交换
    {
      exchange(&a[cur], &a[prev]);
    }
    cur++;
  }
  exchange(&a[key], &a[prev]);
  //左
  QuickSrot(a, begin, prev - 1);
  // 右
  QuickSrot(a, prev + 1, end);
}

疑惑

  1. 为什么相遇位置比key小

原因:是right先走

两种情况:

(1).R遇见L —>(L和R交换后,R先走)R没有找到比key小的,一直走,直到R遇见L,(特殊情况除外)

(2)L遇见R----->(R找到小了),然后L没有找到比key大的,一直走,直到L遇见R,(特殊情况除外)

相关文章
|
8天前
|
搜索推荐 算法 Shell
数据结构和算法——排序算法的比较和排序综测测验
数据结构和算法——排序算法的比较和排序综测测验
6 0
|
8天前
|
算法 C语言
数据结构和算法——桶排序和基数排序(图示、伪代码、多关键字排序,基数排序代码)
数据结构和算法——桶排序和基数排序(图示、伪代码、多关键字排序,基数排序代码)
8 0
|
8天前
|
算法 搜索推荐
数据结构和算法——表排序(算法概述、物理排序、复杂度分析,包含详细清晰图示过程)
数据结构和算法——表排序(算法概述、物理排序、复杂度分析,包含详细清晰图示过程)
11 0
|
8天前
|
人工智能 算法 C语言
数据结构与算法——简单排序-冒泡排序、插入排序,时间复杂度下界(图示、代码、时间复杂度、定理)
数据结构与算法——简单排序-冒泡排序、插入排序,时间复杂度下界(图示、代码、时间复杂度、定理)
7 0
|
8天前
|
算法 C语言
数据结构与算法——拓扑排序(引例、拓扑排序、伪代码、代码、关键路径问题)
数据结构与算法——拓扑排序(引例、拓扑排序、伪代码、代码、关键路径问题)
7 0
|
9天前
|
搜索推荐
深入理解数据结构第六弹——排序(3)——归并排序
深入理解数据结构第六弹——排序(3)——归并排序
|
9天前
|
搜索推荐
深入理解数据结构第五弹——排序(2)——快速排序
深入理解数据结构第五弹——排序(2)——快速排序
|
9天前
|
存储 搜索推荐
深入了解数据结构第四弹——排序(1)——插入排序和希尔排序
深入了解数据结构第四弹——排序(1)——插入排序和希尔排序
10 0
|
22天前
|
存储 搜索推荐 算法
C语言数据结构算法,常用10种排序实战
插入排序(Insertion Sort) 希尔排序(Shell Sort) 选择排序(Selection Sort) 冒泡排序(Bubble Sort) 归并排序(Merge Sort) 快速排序(Quick Sort) 堆排序(Heap Sort) 基数排序(Radix Sort)
15 1
C语言数据结构算法,常用10种排序实战
|
22天前
|
存储 搜索推荐 算法
[数据结构]————排序总结——插入排序(直接排序和希尔排序)—选择排序(选择排序和堆排序)-交换排序(冒泡排序和快速排序)—归并排序(归并排序)
[数据结构]————排序总结——插入排序(直接排序和希尔排序)—选择排序(选择排序和堆排序)-交换排序(冒泡排序和快速排序)—归并排序(归并排序)