【数据结构】排序

本篇简单梳理了下常见的七种排序算法:冒泡排序、简单选择排序、直接插入排序、希尔排序、堆排序、归并排序、快速排序,以图例形式,解释如何进行排序。

该图为几种排序算法的复杂度、稳定性等。

1. 冒泡排序
  • 思路:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
    在这里插入图片描述
void BubbleSort(int* a, int n) //冒泡排序
{
        int end = n - 1;
        while (end > 0)
        {
               for (int i = 0; i < end; ++i)
               {
                       if (a[i] > a[i + 1])
                              Swap(&a[i], &a[i + 1]);
               }
               --end;
        }
}
2. 简单选择排序
  • 思路:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
    在这里插入图片描述
void SelectSort(int* a, int n) //选择排序
{
        int begin = 0;
        int end = n - 1;
        int max_index, min_index; //index->指针
        while (begin <= end)
        {
               min_index = max_index = begin;
               for (int i = begin + 1; i < end; ++i)
               {
                       if (a[i] > a[max_index])
                              max_index = i; //将大的元素地址给与max,方可经行下一轮比较
                       if (a[i] < a[min_index])
                              min_index = i;
                       Swap(&a[i], &a[max_index]); //&
                       if (max_index == begin)  //当最大值在开始时,需要将最小地址交换给最大地址,否则最大值的位置会有冲突
                       {
                              max_index = min_index;
                       }
                       Swap(&a[i], &a[min_index]); //&
               }
               --end;
               ++begin;
        }
}
3. 直接插入排序
  • 思路:当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。
    在这里插入图片描述
void InsertSort(int* a, int n) //直接插入排序
{
        //思路:在已排好的序列中插入数据,大于的数往后移动覆盖,插入的数要大于前一个或者小于后一个(插入a[0]不用比较前一个)
        //插入的数据的位置为a[0]-a[n-1],最多比较到n-2的位置
        //时间 O(n*n):最好O(n)、最坏O(n*n), 空间 O(1)、稳定
        for (int i = 0; i < n - 1; ++i)
        {
               int end = i; 
               int tmp = a[end + 1]; //要插入的数
               while (end >= 0) //=0 往原序列的头插入
               {
                       if (a[end] > tmp)  //小往前插
                       {
                              a[end + 1] = a[end]; //覆盖数
                              --end;
                       }
                       else
                       {
                              break;
                       }
               }
               a[end + 1] = tmp;  //前面已排好序,直接插入最后一个w位置
        }
}
4. 希尔排序
  • 思路:设置一个gap,gap的数值代表数据之间间隔gap,然后预排序,预排序完成后基本接近于排序,再进行一次插入排序。
    在这里插入图片描述
void ShellSort(int* a, int n)  //哈希排序
{
        int gap = n;
        while (gap > 1)
        {
               gap = gap / 3 + 1; //设置间距,+1确保进行插入排序 gap = 1 表示直接插入排序
               for (int i = 0; i < n - gap; ++i) //当i走到n-1-gap进行最后比较数据交换
               {
                       int end = i;
                       int tmp = a[end + gap];
                       while (end >= 0)
                       {
                              if (a[end] > tmp)
                              {
                                      a[end + gap] = a[end];
                                      end = end - gap;
                              }
                              else
                              {
                                      break;
                              }
                       }
                       a[end + gap] = tmp;
               }
        }
}
5. 堆排序
  • 排升序要建大堆,排降序建小堆。
    在这里插入图片描述
void AdujustDown(int* a, int n, int parent) //向下调整
{
        int child = parent * 2 + 1;
        while (child < n)
        {
               if (child + 1 < n && a[child] < a[child + 1]) //选择小的孩子
                       ++child;
               if (a[child] > a[parent])
               {
                       Swap(&a[child], &a[parent]); //交换数据
                       parent = child;
                       child = parent * 2 + 1; //迭代
               }
               else
               {
                       break;
               }
        }
}

void HeapSort(int* a, int n) //堆排序
{
        for (int i = (n - 2) / 2; i >= 0; --i) //建堆
        {
               AdujustDown(a, n, i);
        }
        int end = n - 1; //堆排序
        while (end > 0)
        {
               Swap(&a[0], &a[end]); //*已建立大堆,将a[0]与a[end]交换,再经行堆调整
               AdujustDown(a, end, 0);
               --end;
        }
}
6. 归并排序
  • 将已有序的子序列合并,得到完全有序的序列,即先使每个子序列有序,再使子序列段间有序;若将两个有序表合并成一个有序表,称为二路归并。
    在这里插入图片描述
void _MergeSort(int* a, int left, int right, int* tmp)
{
        //分解
        if (left == right) //子问题只左右两个数
               return;
        int mid = left + (right - left) / 2;
        _MergeSort(a, left, mid, tmp);  //区间划分[left, mid][mid+1, right]
        _MergeSort(a, mid + 1, right, tmp);

        //合并
        //左右之间有序,归并
        int begin1 = left; int end1 = mid;
        int begin2 = mid + 1; int end2 = right;
        int  i = left; //tmp
        while (begin1 <= end1 && begin2 <= end2)
        {
               if (a[begin1] < a[begin2])
               {
                       tmp[i++] = a[begin1++];
               }
               else
               {
                       tmp[i++] = a[begin2++];
               }
        }
        while (begin1 <= end1)
        {
               tmp[i++] = a[begin1++];
        }
        while (begin2 <= end2)
        {
               tmp[i++] = a[begin2++];
        }
        //拷回原数组中,开始将数据均放于临时数组中
        memcpy(a + left, tmp + left, sizeof(int)*(i - left)); //原、目标、大小(right-left+1)但针对每个小区间
}

void MergeSort(int* a, int n) //归并排序
{
        int* tmp = (int*)malloc(sizeof(int)*n); //合并时,先把数放到tmp数组中,经排序后再覆盖到原数组,直到最后一趟完成即排序完成
        _MergeSort(a, 0, n - 1, tmp);
        free(tmp);
}
7. 快速排序
  • 思路:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
    在这里插入图片描述
int OneSort(int* a, int left, int right)
{
        int key = a[right];
        int key_index = right;
        
        while (left < right)
        {
               while (left < right && a[left] <= key) //a[left] <= key,进入循环++,大于时出来与右指针所找的数经行交换
                       ++left;
               while (left < right && a[right] >= key)
                       --right;
               Swap(&a[left], &a[right]);
               if (left < right)
               {
                       Swap(&a[left], &a[right]);
                       ++left;
                       --right;
               }
        }
        Swap(&a[left], &a[key_index]); //left = right时,选取右key,则a[left] 与 key所指的数值交换
        
        return left;
}
void QuickSort1(int* a, int left, int right) //快速排序
{
        //思路:左右指针法,在最左或最右选取一个key
        //左指针向左找大于key所对应的数,右指针找小于key所对应的数值,找到左右指针对应的数值交换
        //右边作key,先走左,后走右;左边为key,先走右,后走左;指针相遇时,left与右key交换、right与左key交换
        if (left >= right)  //判断左右区间大小
               return;

        int key_index = OneSort1(a, left, right); //区间分为三部分:[left, key_index-1]  key [key_index + 1, right]
        QuickSort1(a, left, key_index-1);
        QuickSort1(a, key_index + 1, right);
}

【Yang】

  • 哪里有问题,希望大家提出来,我会及时改正 o( ̄▽ ̄)ブ 。
发布了19 篇原创文章 · 获赞 23 · 访问量 6368

猜你喜欢

转载自blog.csdn.net/qq_41286373/article/details/104621953
今日推荐