直接插入排序、希尔排序、选择排序

目录

直接插入排序和希尔排序

直接插入排序

​编辑

单趟

全过程

希尔排序

简单版本希尔排序

完整版希尔排序

选择排序

算法简介

代码实现


直接插入排序和希尔排序

直接插入排序实际上可以视作是希尔排序的组件,所以我这里将直接插入排序和希尔排序放到一起了

直接插入排序

下面是直接插入排序的升序例子

我们可以看到,直接插入排序就是将一个值**"拿出来",**将他和他将要插入的值的前一个值依次对比

如果拿出来的值小于该值,就将拿出来的值插入到前面比较过的位置,此时break

如果拿出来的值大于该值,就将对比后的值向后挪,继续向前遍历

单趟

cpp 复制代码
void InsertSort(int* arr, int n)
{
	int end;//按下不表,芝士拿出来的值的上一个值的下标
	int temp = arr[end + 1];
	while (end >= 0)
	{
		//当arr[end]大于temp是继续遍历
		if (arr[end] > temp)
		{
			arr[end + 1] = arr[end];
			end--;
		}
		else
		{
			break;
		}
	}
	arr[end + 1] = temp;
}

这里我将最后插入做了统一处理

当在end>=0时找到了合适的插入位置 ,此时end+1位置上的数已经移动到了上一个end+1 的位置上(按照此时的位置就是end+2),相当于是此时end+1上的值是无效的,此时我们将temp赋值给arr[end+1]

当在end>=0的范围内都没有找到合适得插入位置,此时end=-1(刚刚跳出循环),end+1正好是0,没有越界,也符合实际:此时temp是最小的,插到最前面。

全过程

cpp 复制代码
void InsertSort(int* arr, int n)
{
	for (int begin = 0; begin < n - 1; begin++)
	{
		int end = begin;
		int temp = arr[end + 1];
		while (end >= 0)
		{
			//当arr[end]大于temp是继续遍历
			if (arr[end] > temp)
			{
				arr[end + 1] = arr[end];
				//这一步是挪动arr中的值
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = temp;
		//这里将所有的情况统一处理,并处理了end = -1的边界情况
	}
}

这里相当于是每次都对 **begin+1(也是end+1)**的数据进行排序

所以for循环是这样写的:

cpp 复制代码
for (int begin = 0; begin < n - 1; begin++)

这里的直接插入排序实际上改几下就是希尔排序

希尔排序

希尔排序(Shell Sort),全称"缩小增量排序"(Diminishing Increment Sort),是插入排序的一种更高效的改进版本

直接插入排序在碰到全是逆序的数据使会变得非常的坑,这时,希尔排序就通过将数据大踏步的向后调整的预排序使数据更趋向于有序,从而大大减少了计算量

希尔排序的本质是分组。它不再把数组看作一个整体,而是根据一个"增量"(gap)将数组分割成若干个子序列,这个过程中所有的值都被遍历了一遍。

希尔排序通过让元素"跳跃式"地向最终位置移动,大幅减少了数据交换和移动的次数

简单版本希尔排序

我们先写一个gap=3的,只进行一次预排序的希尔排序

cpp 复制代码
void ShellSort(int* arr, int n)
{
	int gap = 3;
	for(int i = 0; i <gap; i++)//分组写法
	{
		for (int begin = i; begin < n - gap; begin += gap)
		{
			int end = begin;
			int temp = arr[end + gap];
			while (end >= i)
			{
				if (arr[end] > temp)
				{
					arr[end + gap] = arr[end];
					end--;
				}
				else
				{
					break;
				}
			}
			arr[end + gap] = temp;
		}
	}
	for (int begin = 0; begin < n - 1; begin++)
	{
		int end = begin;
		int temp = arr[end + 1];
		while (end >= 0)
		{
			if (arr[end] > temp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = temp;
	}
}

这个希尔排序只进行了一次粗排,而且gap也是写死的,这实际上是不够完善的,对于大量 的数据,只进行这一次预排序显然是不够的

这时我们就应该将gap写成动态的

完整版希尔排序

这里的gap取值也是有讲究的

gap取得大一点跳得更快,但是排的数据有序性较差

gap取得小一点跳得更慢,但是排的数据有序性较好

学术界经过研究得出:每次gap除3是最好的,整个排序算下来的时间复杂度是O( n^1.3 )

cpp 复制代码
void ShellSort(int* arr, int n)
{
	int gap = n / 3 + 1;
	//这里的do_while是一个小巧思,当gap==1时可以只进行一次排序,在首个gap
	do
	{
        //先使用再改变gap
		for (int j = 0; j < gap; j++)
		{
			for (int i = j; i < n - gap; i += gap)
			{
				int end = i;
				int temp = arr[end + gap];
				while (end >= j)//这里是一个边界条件,我们应该将预排序视作是划分了一个新的组
				{
					if (arr[end] > temp)
					{
						arr[end + gap] = arr[end];
						end -= gap;
					}
					else
					{
						break;
					}
				}
				arr[end + gap] = temp;
			}

		}
		gap = gap / 3 + 1;
	
	} while (gap > 1);
}

选择排序

算法简介

选择排序是一种简单直观的排序算法。它的工作原理是:

  1. 在未排序序列中找到最小(或最大)元素

  2. 将其存放到排序序列的起始位置

  3. 然后,再从剩余未排序元素中继续寻找最小(或最大)元素

  4. 放到已排序序列的末尾

  5. 重复以上步骤,直到所有元素均排序完毕

代码实现

cpp 复制代码
void SelectSort(int* arr, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int max = begin, min = begin;
		for (int i = begin + 1; i <= end; i++)
		{
			if (arr[max] < arr[i])
			{
				max = i;
			}
			if (arr[min] > arr[i])
			{
				min = i;
			}
		}
		Swap(&arr[begin], &arr[min]);
		if (begin == max)
			max = min;
		Swap(&arr[end], &arr[max]);

		begin++;
		end--;
	}
}

这里有一步需要解释一下

cpp 复制代码
if (begin == max)
	max = min;

这一步是为了处理begin==max的边界情况

示例arr = [5, 1, 4, 2, 3]

第一轮循环:

  • begin = 0, end = 4

  • max = 0 (arr[0]=5是最大值)

  • min = 1 (arr[1]=1是最小值)

如果没有这一步的话,**直接交换arr[begin]和arr[min]**会导致max指向的值被移动,后面的逻辑就错了。

相关推荐
驭渊的小故事2 分钟前
简单模板笔记
数据结构·笔记·算法
YuTaoShao17 分钟前
【LeetCode 每日一题】1653. 使字符串平衡的最少删除次数——(解法一)前后缀分解
算法·leetcode·职场和发展
VT.馒头23 分钟前
【力扣】2727. 判断对象是否为空
javascript·数据结构·算法·leetcode·职场和发展
goodluckyaa33 分钟前
LCR 006. 两数之和 II - 输入有序数组
算法
孤狼warrior34 分钟前
YOLO目标检测 一千字解析yolo最初的摸样 模型下载,数据集构建及模型训练代码
人工智能·python·深度学习·算法·yolo·目标检测·目标跟踪
Σίσυφος19001 小时前
PCL法向量估计 之 RANSAC 平面估计法向量
算法·机器学习·平面
xhbaitxl1 小时前
算法学习day39-动态规划
学习·算法·动态规划
I_LPL1 小时前
day23 代码随想录算法训练营 回溯专题2
算法·hot100·回溯算法·求职面试
智者知已应修善业1 小时前
【洛谷P9975奶牛被病毒传染最少数量推导,导出多样例】2025-2-26
c语言·c++·经验分享·笔记·算法·推荐算法
m0_736919101 小时前
C++中的委托构造函数
开发语言·c++·算法