排序算法思想

目录

一、冒泡排序

二、选择排序

三、插入排序

四、希尔排序

五、堆排序

六、快速排序

七、归并排序


介绍几种常见的排序算法思想,以及他们的实现代码。

什么是排序?

看到这个问题,很多读者一定会觉得很可笑,排序还不简单,就是将一串无序数列中的数据按照一定顺序进行排列呗。我们日常比较常见的就是排升序和排降序,不管哪种排序,笔者这里希望提醒大家一下,一定要牢牢记住排序的本质(这里以排升序为例):较小数一定在较大数的前面。不管是那种算法,本质上都遵循这种思想。好了,话不多说,咱们进入正题。

一、冒泡排序

算法思想:将数组中前一个数和后一个数进行比较,如果前一个数大于后一个数,那么交换这两个数的位置,直到将最大数交换到数组的最后一个位置。当数组本身就是有序数组或者在不断循环的过程中变为有序时,冒泡排序还会不断的重复,导致效率变低。因此可以引入一个flag,让flag初始为1,当出现交换的时候让flag值为0。对flag值进行判定,为1时说明数组不存在交换,跳出循环,提高了效率。

算法实现:

void Swap(int* left, int* right)
{
	int tmp = *left;
	*left = *right;
	*right = tmp;
}
void Bubllue(int* a, int n)
{
	//n个数,需要循环n-1次
	for (int j = 0; j < n - 1; j++)
	{
		//判断是否出现了交换
		int flag = 1;
		//一趟冒泡排序
		for (int i = 1; i < n - j; i++)
		{

			//前一个数比后一个数大,交换
			if (a[i - 1] > a[i])
			{
				//当flag为0说明出现了交换,为1说明不存在交换,数组有序
				flag = 0;
				Swap(&a[i], &a[i - 1]);
			}
		}

		if (flag)
			return;
	}
}

二、选择排序

算法思想:选择排序是遍历数组,找到数组中最小的数,将最小的数和数组首元素交换。从首元素的下一个位置开始,找数组中最小的数放在数组首元素。不断循环,直到整个数组有序。

算法实现:

void Swap(int* left, int* right)
{
	int tmp = *left;
	*left = *right;
	*right = tmp;
}
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
        //在遍历数组的时候同时找到最大数和最小数,提高效率
		int mini = begin, maxi = begin;
		for (int i = begin + 1; i <= end; i++)
		{
			if (a[i] < a[mini])
				mini = i;
			if (a[i] > a[maxi])
				maxi = i;
		}
		Swap(&a[mini], &a[begin]);
		//当数组首元素为最大值时,mini和begin中的数据已经交换。
		if (maxi == begin)
		{
			maxi = mini;
		}
		Swap(&a[maxi], &a[end]);

		begin++;
		end--;
	}
}

三、插入排序

算法思想:从数组首元素开始,不断构建有序数组。将有序数组下一个元素作为新的值,不断插入到有序数组中,直到数组有序。

算法实现:

void InsertSort(int* a, int n)
{
	for (int end = 0; end < n - 1; end++)
	{
		//保存end下一位数
		int tmp = a[end + 1];
		while (end >= 0)
		{
			//如果tmp比end小,end往后挪动一位;反之跳出循环
			if (tmp < a[end])
			{
				a[end + 1] = a[end--];
			}
			else
			{
				break;
			}
		}
		//end指向的是空位前一位的数,所以要往后挪动一位
		a[end + 1] = tmp;
	}
}

四、希尔排序

算法思想:是插入算法的优化。将数组每隔gap个元素分为一组,在对每组的数据进行插入排序,当每组都完成一次插入排序,数组中较大数越靠后。提升了效率。

算法实现:

void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		//gap越大,大的数需要移动次数越少,数组相对混乱;gap越小,大的数需要移动次数越多,数组相对有序。
		gap = gap / 3 + 1;//gap的值会不断缩小,直到缩小到1,就是插入排序。
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			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 Swap(int* pchild, int* pparent)
{
	int tmp = *pchild;
	*pchild = *pparent;
	*pparent = tmp;
}
//向下调整算法
void AdjustDwon(int* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child + 1] > a[child])
			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)
{
	//向下调整建堆,O(N)必须满足左半边是个堆,右半边也是个堆,直接传过去最后一个叶子结点的父节点
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
		AdjustDwon(a, n, i);


	for (int i = n - 1; i > 0; i--)
	{
        //最后一个叶子结点与根交换
		Swap(&a[0], &a[i]);
		AdjustDwon(a, --n, 0);
	}
}

六、快速排序

算法思想:选取一个基准数key,遍历数组,将所有比key小的数放在key的左边,比key大的数都放在key的右边。在对[begin, key-1]区间和[key + 1, end]区间中选取新的key,不断递归直到所有相对较小数都在key左边,相对较大数都在key右边,也就实现了升序。

算法实现:

void Swap(int* left, int* right)
{
	int tmp = *left;
	*left = *right;
	*right = tmp;
}
int PartSort1(int* a, int begin, int end)
{	
    //让key的值为数组首元素,先从后往前找比key小的数,在从前往后找比key大的数,再将相遇的位置的数和key交换可以保证key左边的数都比key小,右边的数都比key大
	int keyi = begin;
	while (begin < end)
	{
		//从右往左找,比下标为keyi的值小的数
		while (begin < end && a[end] >= a[keyi])
		{
			end--;
		}

		//从左往右找,比小标为keyi的值大的数
		while (begin < end && a[begin] <= a[keyi])
		{
			begin++;
		}
		//交换这两个数
		Swap(&a[begin], &a[end]);
	}
	//交换相遇数和下标为keyi的数
	Swap(&a[begin], &a[keyi]);

	return begin;
}

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort1(a, begin, end);


	//递归[begin, keyi - 1]keyi[keyi + 1, right]
	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
}

七、归并排序

算法思想:将数组分成两段,让左半段、右半段实现有序,创建一块和原来数组等长的空间tmp。用两个指针分别指向左半段和右半段,将两个指针中较小数依次放入新的数组tmp中,当两个数组中都为空时,新数组tmp中就是有序数组,再将其拷贝回原数组。

算法实现:

void _MergeSort(int* a, int begin, int end, int* tmp)
{
    //当数组区间不存在时,返回
	if (begin >= end)
		return;

    //将数组从中间分成左右两个子数组
	int mid = begin + (end - begin) / 2;
	//两个数组的区间分别为[begin, mid][mid+1, end]

	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid + 1, end, tmp);

	//归并[begin, mid][mid+1, end]
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;
	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
        //选两个数组中小的数,拷贝到新数组tmp中
		if (a[begin1] < a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	//因为tmp里面的值要拷贝回a。begin2有剩余的时候,直接将tmp拷贝回去就可以。
	//[0 1 2 3 4 5 6 ]
	memcpy(a + begin, tmp + begin, sizeof(int) * (i - begin));

	return;
}
void MergeSort(int* a, int n)
{
    //大小为n的新空间tmp
	int* tmp = (int*)malloc(sizeof(int) * n);
	assert(tmp);

	//[0, n-1]
	_MergeSort(a, 0, n - 1, tmp);

	free(tmp);
	tmp = NULL;
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
直接插入排序是一种简单的排序算法,其基本思想是将待排序的元素插入到已经排好序的序列中,从而得到一个新的、元素个数增加1的有序序列。具体来说,算法的步骤如下: 1. 将待排序的元素分为两个部分,第一部分包含一个元素,就是原序列的第一个元素,第二部分包含剩下的元素。 2. 从第二部分开始,依次将每个元素插入到第一部分中已经排好序的序列中,直到所有元素都插入到第一部分中。 3. 插入元素的过程中,需要将待插入元素与第一部分中的元素从后往前依次比较,找到插入位置,然后将待插入元素插入到该位置。 4. 重复步骤2和3,直到所有元素都插入到第一部分中,此时第一部分就是一个有序序列。 以待排序记录的关键字序列为{15,26,2,3,5,36,27,4,46,48,50,38,44,19,47}为例,演示一下直接插入排序的过程: 1. 第一次排序:15已经是有序序列,将26插入到15后面,得到序列{15,26}; 2. 第二次排序:将2插入到15前面,得到序列{2,15,26}; 3. 第三次排序:将3插入到2后面,得到序列{2,3,15,26}; 4. 第四次排序:将5插入到3后面,得到序列{2,3,5,15,26}; 5. 第五次排序:将36插入到26后面,得到序列{2,3,5,15,26,36}; 6. 第六次排序:将27插入到26前面,得到序列{2,3,5,15,27,26,36}; 7. 第七次排序:将4插入到3前面,得到序列{2,4,3,5,15,27,26,36}; 8. 第八次排序:将46插入到36后面,得到序列{2,4,3,5,15,27,26,36,46}; 9. 第九次排序:将48插入到46后面,得到序列{2,4,3,5,15,27,26,36,46,48}; 10. 第十次排序:将50插入到48后面,得到序列{2,4,3,5,15,27,26,36,46,48,50}; 11. 第十一次排序:将38插入到36后面,得到序列{2,4,3,5,15,27,26,36,38,46,48,50}; 12. 第十二次排序:将44插入到38后面,得到序列{2,4,3,5,15,27,26,36,38,44,46,48,50}; 13. 第十三次排序:将19插入到15前面,得到序列{2,4,3,5,15,19,27,26,36,38,44,46,48,50}; 14. 第十四次排序:将47插入到46前面,得到序列{2,4,3,5,15,19,27,26,36,38,44,46,47,48,50}; 最终得到的序列就是一个有序序列。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ljiyu0506

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值