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

目录

直接插入排序和希尔排序

直接插入排序

​编辑

单趟

全过程

希尔排序

简单版本希尔排序

完整版希尔排序

选择排序

算法简介

代码实现


直接插入排序和希尔排序

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

直接插入排序

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

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

如果拿出来的值小于该值,就将拿出来的值插入到前面比较过的位置,此时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赋值给arrend+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 (arr0=5是最大值)

  • min = 1 (arr1=1是最小值)

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

相关推荐
mmmmath_326 分钟前
二叉树の递归遍历
算法
All for pursuit.42 分钟前
【位运算-1】338.比特位计数
数据结构·c++·算法·leetcode
小师兄吃牛肉1 小时前
C语言中的nullptr有必要吗
c语言·c++·算法
一只小小的芙厨3 小时前
【线性DP】
学习·算法·动态规划
xxwl5853 小时前
数据结构知识点和代码实现总结(C语言实现)
c语言·开发语言·数据结构
奋发向前wcx3 小时前
CSP-J复赛模拟赛5 王晨旭补题 2026.10.5
算法
罗湖老棍子5 小时前
C Looooops(信息学奥赛一本通- P1640)
算法·数论·同余·裴蜀定理·扩展欧几里得
飘尘7 小时前
从"算子"到"AI Infra":大模型背后看不见的那群人在忙什么
人工智能·算法·面试
mokongJ8 小时前
17 份异构销售数据的合并清洗方案:30 万行处理耗时 9.64 秒
数据结构
夏幻灵8 小时前
每日一题:LeetCode 3:无重复字符的最长子串——滑动窗口思路详解
算法·leetcode·职场和发展