【数据结构入门】排序算法之插入排序与选择排序

news/2024/9/23 4:26:46/

目录

前言

一、排序的概念及运用

1.排序的概念

2.排序的运用

3.常见排序算法

二、插入排序选择排序

2.1插入排序

2.1.1直接插入排序

1)基本思想

2)具体步骤

3)算法特性

4)算法实现

2.1.2希尔排序

1)  基本思想

2)具体步骤

3)算法特性

4)算法实现

2.2选择排序

2.2.1直接选择排序

1)  基本思想

2)具体步骤

3)算法特性

4)算法实现

 2.2.2 堆排序

1)  基本思想

2)具体步骤

3)算法特性

4)算法实现

三、算法复杂度及稳定性分析

总结


前言

在数据结构中,排序是指将一组数据按照特定的规则重新排序的过程。排序可以使数据按照升序或者降序排列,从而方便后续的操作和查找。

一、排序的概念及运用

1.排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

例如:

内部排序:数据元素全部放在内存中的排序。

外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

2.排序的运用

排序在生活中的使用无处不在,成绩排名、商品排名、电影榜单等等数不胜数。

 

3.常见排序算法

 

二、插入排序选择排序

2.1插入排序

2.1.1直接插入排序

1)基本思想

直接插入排序是一种简单的排序算法,它的基本思想是将待排序的数据分成已排序和未排序两部分,每次从未排序部分中取出一个元素,然后将其有序地插入到已排序部分的合适位置。

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移

动画演示:

2)具体步骤
  1. 将第一个元素视为已排序部分,将剩余的元素视为未排序部分。

  2. 从未排序部分取出第一个元素,将其插入到已排序部分的合适位置。插入时,从后往前逐个比较已排序部分的元素,将大于待插入元素的元素依次后移,直到找到一个小于或等于待插入元素的位置。

  3. 重复步骤2,直到未排序部分中的所有元素都插入到已排序部分。

3)算法特性
  1. 元素集合越接近有序,直接插入排序算法的时间效率越高

  2. 时间复杂度:直接插入排序的时间复杂度是O(n^2),其中n是待排序数据的个数。当输入数据已经基本有序时,直接插入排序的性能较好,时间复杂度可以降低到O(n)。但当输入数据完全逆序时,直接插入排序的性能较差,时间复杂度会达到最大值O(n^2)。

  3. 空间复杂度:O(1),它是一种稳定的排序算法

  4. 稳定性:稳定

4)算法实现
void InsertSort(int* a, int n)
{for (int i = 0; i < n; i++){int end = i-1;int tmp = a[i];//将tmp插入到[0, end]区间中,保持有序while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];--end;}else{break;}}a[end + 1] = tmp;}	
}

2.1.2希尔排序

希尔排序法又称缩小增量法。

1)  基本思想

将待排序的数据按照一定的增量分组,对每组数据进行插入排序,然后逐渐减小增量,重复上述步骤,直至增量为1,完成最后一轮的插入排序

图示:

2)具体步骤
  1. 选择一个增量序列,常用的增量序列是希尔增量(N/2, N/4, N/8...,直到增量为1)。

  2. 对于每个增量,以增量作为间隔将待排序的数据分成多个组,分别对每个组进行插入排序

  3. 逐渐减小增量,重复上述步骤,直至增量为1。

3)算法特性
  1. 希尔排序是对直接插入排序的优化。

  2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

  3. 希尔排序的时间复杂度较为复杂,最好情况下为O(n log^2 n),最坏情况下为O(n^2),平均情况下为O(n log n)。希尔排序的性能优于直接插入排序,尤其是对于数据量较大的情况,其性能优势更加明显。这里一般可以认为时间复杂度为O(N^1.3)左右。

  4. 稳定性:不稳定

4)算法实现

多组同时排法:

void ShellSort(int* a, int n)
{//预处理int gap = n;while (gap > 1){//这里必须保证gap最后一次是1//gap /= 2;gap = gap / 3 + 1;for (int i = gap; i < n; i++){int end = i - gap;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 = n;while (gap > 1){//这里必须保证gap最后一次是1//gap /= 2;gap = gap / 3 + 1;for (int j = 0; j < gap; j++){for (int i = gap+j; i < n; i += gap){int end = i - gap;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;}}}	
}

2.2选择排序

2.2.1直接选择排序

1)  基本思想

每一次从待排序的数据中选择最小(或最大)的元素,放到已排序序列的末尾,直到全部待排序的数据元素排完 。

动画演示:

2)具体步骤
  1. 找到待排序序列中最小(或最大)的元素,记为A。

  2. 将A与待排序序列的第一个元素交换位置。

  3. 然后在剩余的待排序序列中找到最小(或最大)的元素,再与待排序序列的第二个元素交换位置。

  4. 重复上述步骤,直到待排序序列变为空。

3)算法特性
  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

  2. 时间复杂度:直接选择排序的时间复杂度是O(n^2),无论输入数据的情况如何,都需要进行n-1次的比较和若干次元素交换。虽然直接选择排序的时间复杂度较高,但是它的优点是原地排序,不需要额外的空间。

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

  4. 稳定性:不稳定

4)算法实现
void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void SelectSort(int* a, int n)
{int left = 0, right = n - 1;while (left < right){int mini = left, maxi = left;for (int i = left + 1; i <= right; i++){if (a[i] < a[mini]){mini = i;}if (a[i] > a[maxi]){maxi = i;}}Swap(&a[left], &a[mini]);//如果left和maxi重叠,交换后修正一下,否则这里会出问题,换两次换回去了if (maxi == left){maxi = mini;}Swap(&a[right], &a[maxi]);left++;right--;}
}

 2.2.2 堆排序

堆排序是一种利用堆的数据结构进行排序的算法。堆是一种特殊的二叉树,满足堆性质:任意节点的值都大于等于(或小于等于)其子节点的值。需要注意的是排升序要建大堆,排降序建小堆。

1)  基本思想

将待排序的数据构造成一个堆,然后将堆顶元素与末尾元素交换,再对剩余的元素重新构造堆,以此类推,最终得到有序的序列。

2)具体步骤
  1. 构建最大堆(或最小堆),将待排序的数据转换为堆。

  2. 将堆顶元素(即最大值或最小值)与堆的最后一个叶子节点交换位置。

  3. 重新调整堆,将堆顶元素下沉,使得堆仍然满足堆性质。

  4. 重复上述步骤,直到堆只剩一个元素或为空。

3)算法特性
  1. 堆排序使用堆来选数,效率就高了很多。

  2. 时间复杂度:堆排序的时间复杂度是O(nlogn),堆的构建需要O(n)的时间,每次调整堆的时间为O(logn)。堆排序是一种原地排序算法,不需要额外的空间。由于堆排序具有良好的局部性,适合用于大规模数据的排序。

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

  4. 稳定性:堆排序是一种不稳定的排序算法,相同元素的相对位置可能会改变。

4)算法实现
void ADjustDown(int* a, int sz, int parent)
{int child = parent * 2 + 1;while (child < sz){//选出左右孩子中大的一个//这里child+1的判断在前,不要先访问再判断//这里a[child + 1] > a[child] 建大堆用>, 建小堆用<if (child + 1 < sz && a[child + 1] > a[child]){//这地方可能会越界++child;}//这里a[child] > a[parent] 建大堆用>, 建小堆用<if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapSort(int* a, int sz)
{//1.建堆 -- 向上调整建堆   NlogN//左右子树必须是大堆/小堆/*for (int i = 1; i < sz; i++){ADjustUp(a, i);}*///2.向下调整建堆  N//左右子树必须是大堆/小堆for (int i = (sz - 1 - 1) / 2; i >= 0; i--){ADjustDown(a, sz, i);}int end = sz - 1;while (end > 0){Swap(&a[end], &a[0]);ADjustDown(a, end, 0);--end;}
}

堆排序在前文中已经详细介绍了,具体见: 

【数据结构入门】二叉树之堆排序及链式二叉树

三、算法复杂度及稳定性分析

 

总结

        排序算法的选择可以根据数据的特点、数据量以及排序的要求来确定。不同的排序算法具有不同的时间复杂度和空间复杂度,因此在实际应用中需要根据具体情况选择合适的排序算法

直接插入排序

  • 直接插入排序是一种简单直观的排序算法,其思想是将待排序的序列分为已排序和未排序两部分,每次从未排序部分选择一个元素插入到已排序部分的合适位置,直到所有元素都插入到已排序部分为止。
  • 直接插入排序的时间复杂度为O(n^2),是稳定的排序算法

希尔排序

直接选择排序

  • 直接选择排序是一种简单直观的排序算法,其思想是每次从未排序的序列中选择最小(或最大)的元素,将其与未排序部分的第一个元素交换位置,直到所有元素都排序完成。
  • 直接选择排序的时间复杂度为O(n^2),是不稳定的排序算法

堆排序

  • 堆排序利用二叉堆这种数据结构进行排序,将待排序的元素依次插入到堆中,然后从堆顶依次取出最大(或最小)的元素,直到所有元素都取出为止。
  • 堆排序的时间复杂度为O(nlogn),是不稳定的排序算法

 


http://www.ppmy.cn/news/1517428.html

相关文章

mp总结 mybatisPlus

一、准备 1.引入依赖&#xff08;引入后可以不再引mybatis依赖&#xff09; <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version> </dependency> 2.…

大数据学习路线基础指南‌

随着信息技术的迅猛发展&#xff0c;‌大数据已成为当今社会的热门话题。‌无论是企业决策、‌市场分析还是科学研究&#xff0c;‌大数据都扮演着举足轻重的角色。‌对于想要投身这一领域的学习者来说&#xff0c;‌制定一份清晰、‌系统的大数据学习路线是至关重要的。‌提供…

Day50 | 108.冗余连接 109.冗余连接II

108.冗余连接 108. 冗余连接 题目 题目描述 树可以看成是一个图&#xff08;拥有 n 个节点和 n - 1 条边的连通无环无向图&#xff09;。 现给定一个拥有 n 个节点&#xff08;节点标号是从 1 到 n&#xff09;和 n 条边的连通无向图&#xff0c;请找出一条可以删除的边&…

主流短视频评论采集python爬虫(含一二级评论内容)

声明 仅用于学习交流&#xff0c;不用于其他用途 正文 随着主流短视频评论采集更新需要登录&#xff0c;由于不懈的努力&#xff0c;攻破这一难点&#xff0c;不需要登录采集作品所有评论信息 话不多说上代码看效果&#xff1a; 输入作品id: 这样就拿到评论信息了&#xff…

解决 `java.sql.SQLException` 的正确方法

在开发过程中&#xff0c;java.sql.SQLException 是一个常见但令人头疼的问题。这篇博客将带你一步步分析该异常的产生原因&#xff0c;并提供切实有效的解决方案。 1. 问题分析 java.sql.SQLException 是 JDBC 中的通用异常&#xff0c;通常在数据库操作失败时抛出。它可以由…

图表检测检测系统源码分享 # [一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]

图表检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

经验笔记:Hadoop

Hadoop经验笔记 一、Hadoop概述 Hadoop是一个开源软件框架&#xff0c;用于分布式存储和处理大规模数据集。其设计目的是为了在商用硬件上运行&#xff0c;具备高容错性和可扩展性。Hadoop的核心是Hadoop Distributed File System (HDFS) 和YARN (Yet Another Resource Negot…

【C++ STL哈希容器】unordered_set 无序集合

【 1. 基本原理 】 <unordered_set> 头文件&#xff0c;std 命名空间。类模板定义 以下 4 个参数中&#xff0c;只有第一个参数没有默认值&#xff0c;这意味着如果我们想创建一个 unordered_set 容器&#xff0c;至少需要手动传递 1 个参数。事实上&#xff0c;在 99% …