【数据结构】七大排序(一)

七大排序

排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次
序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排
序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。
常见的排序算法:

在这里插入图片描述
排序算法的实现

插入排序:

直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
**直接插入排序:**当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移
在这里插入图片描述
代码实现:
单次遍历:一个元素排序比如说a[end]=6,首先先定义一个tmp来保存其a[end+1]不保存则会被覆盖,然后比较end和tmp的值,若tmp比他大单次循环结束,若比他小,则将end的值后移一位(注意将值付给end+1这个位置,若直接赋tmp,则会导致原tmp数据丢失且比较错误),但是tmp还要循环依次跟前面的值进行比较,进行数据挪动,直到和a[0]比较完结束
多次遍历:外面在嵌套一层循环,遍历每个数,但是要注意,i<n-1;否则tmp会访问越界

void InsertSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)//如果i走到n-1的位置,则tmp访问越界
	{
		int end = i;//一次遍历
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
				end--;
			}
			else
			{
				break;
			}
		}
		a[end + 1] = tmp;
	}
}

直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度:O(N^2)
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定
    希尔排序( 缩小增量排序 )
    希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个
    组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工
    作。当到达=1时,所有记录在统一组内排好序。
    在这里插入图片描述代码实现
    1.首先通过`gap = gap / 3 + 1来进行分组,当gap为1时,是最后一趟。其原理和直接插入排序一样
void ShellSort(int* a, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 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;
		}
	}
}

希尔排序的特性总结:
5. 希尔排序是对直接插入排序的优化。
6. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就
会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
7. 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N1.3—N2)
8. 稳定性:不稳定

选择排序

基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
在这里插入图片描述

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
void SelectSort(int* a, int n)
{
	int begin = 0;
	int end = n - 1;
	//找到最大元素和最小元素的下标
	//奇数数组 begin = end;偶数数组 begin > end;
	while (begin <= end)
	{
		int min = begin;
		int max = end;
		for (int i = begin ; i <=end; i++)
		{
			if (a[min] >= a[i])
			{
				Swap(&(a[min]),&(a[i]));
			}
			if (a[max] <= a[i])
			{
				Swap(&(a[max]), &a[i]);
			}
			
		}
		Swap(&(a[min]), &(a[begin]));
		Swap(&(a[max]), &a[end]);
		if (max = begin)
		{
				max = min;
		}
		begin++;
		end--;
	}
}

堆排序
排升序为建大堆
讲解:首先建堆,把数据录入以后向下调整建堆.
首先堆向下调整后,很多人就懵逼了,应该就有序了吧?(假设建大堆只是满足了上一层比下一层大,但不一定有序)。
在这里插入图片描述
大堆排好已接近有序,根节点一定是最大值,我们需要循环节点进行排序,首先交换根节点和末尾节点的位置,则最大值在末尾,而根节点为起初的堆尾元素,腿破坏,但左右子树仍为大堆,则向下调整(末尾节点已经最大无需再调),根据大堆向下调整的规则,堆顶元素一定是最大值和末尾元素交换,次大的值在倒数第二个位置上(下次调整无需再调他了)。以此类推进行向下调整的值里最大的值会依次取出,最终有序。
代码实现:

//堆排序
//升序建大堆
//堆的初始化
void AdjustDown(int* array, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//找出最小孩子
		if (child + 1 < n && array[child] < array[child + 1])
		{
			child++;
		}
		//较大孩子大于双亲,交换
		if (array[child]>array[parent])
		{
			Swap(&(array[child]), &(array[parent]));
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapSort(int* a,int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}
	while (n > 1)
	{
		Swap(&(a[n - 1]), &(a[0]));
		n--;
		AdjustDown(a, n, 0);
	}
}
发布了29 篇原创文章 · 获赞 66 · 访问量 5868

猜你喜欢

转载自blog.csdn.net/qq_43676757/article/details/105027209