数据结构(十一):排序(简单选择排序、堆排序、归并排序)

目录

一、简单选择排序

二、堆排序

三、归并排序


一、简单选择排序

●  简单选择排序的动画演示详见:https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html

 原理:

假设排序表为 L [1...n],第 i 趟排序即从 L[1...n] 中选择关键字最小的元素与 L[i] 交换,每一趟排序可以确定一个元素的最终位置。

 代码:

void SelectSort(ElemType A[], int n)
{
	for(int i=0;i<n-1;i++) //一共进行 n-1趟
	{
		int min = i;
		for(int j=i+1;j<n;j++)
			if (A[j] < A[min]) min = j;
		if (min != i) swap(A[i], A[min]); //封装的swap函数共移动元素3次
	}
}

二、堆排序

●  堆排序的动画演示详见:https://www.cs.usfca.edu/~galles/visualization/HeapSort.html

●  原理:

1.堆的定义
  1. n 个关键字序列 L[1...n] 称为堆
  2. 将这个一维数组看做一颗完全二叉树,则
    满足 L(i)>=L(2i) 且 L(i)>=L(2i+1) 的堆称为大根堆,
    满足
     L(i)<=L(2i) 且 L(i)<=L(2i+1) 的堆称为小根堆。
  3. 大根堆的最大元素存放在根节点,且其任一非根节点的值小于等于其双亲结点的值;小根堆与之相反。
大根堆

2.堆排序  整体思路

        【2.1】 将存放 n 个关键字的一维数组建成初始堆(以大根堆为例)。

        【2.2】堆顶元素是最大值,输出堆顶元素。

        【2.3】将堆底元素送入堆顶,大根堆被破坏。

        【2.4】将堆顶元素依次向下调整,直至再次满足大根堆的性质。

        【2.5】继续输出堆顶元素,以此往复。。。直至堆中只剩一个元素。

🎉🎉🎉  🎈🎈🎈  ✨✨✨ 

【2.1】将存放 n 个关键字的一维数组建成初始堆(以大根堆为例)。

如何构造初始堆?


        首先,我们从不同的数据结构视角去看待一个一维数组,它本身也是一个已经构建好的完全二叉树,只不过目前不满足大根堆的性质。

         对于n个结点的完全二叉树,最后一个结点是第 ⌊n/2⌋ 个结点的孩子。对第 ⌊n/2⌋ 个结点为根的子树筛选(对于大根堆,若根结点的关键字小于左右孩子中关键字较大者,则交换),使该子树成为堆。之后向前依次对各结点( ⌊n/2⌋-1 ~ 1)为根的子树进行筛选,看该结点值是否大于其左右子结点的值,若不大于,则将左右子结点中的较大值与之交换,交换后可能会破坏下一级的堆,于是继续采用上述方法构造下一级的堆,直到以该结点为根的子树构成堆为止。反复利用上述调整堆的方法建堆,直到根结点。

       ●  初步总结: 从最后一个分支节点 ⌊n/2⌋ 一直到根结点依次调整

         图示:

【2.2】堆顶元素是最大值,输出堆顶元素。

【2.3】将堆底元素送入堆顶,大根堆被破坏。

【2.4】将堆顶元素依次向下调整,直至再次满足大根堆的性质。

怎么重新调整为大根堆?


        ●  方法:将堆顶元素向下筛选

        ●  图示:

【2.5】继续输出堆顶元素,以此往复。。。直至堆中只剩一个元素。

●  代码:

//堆排序
void HeadAdjust(ElemType A[], int k, int len)
{
	//函数headAdjust只对元素为k的根进行调整
	A[0] = A[k]; //A[0]暂存子树的根节点
	for (int i = k * 2; i <= len; i *= 2)
	{
		if (i < len && A[i] < A[i + 1]) //i<len确保有右孩子
			i++; //选取key值较大的子结点的下标
		if (A[0] > A[i]) break; //不需要调整,筛选结束
		else
		{
			A[k] = A[i]; //将A[i]调整到双亲结点上
			k = i; //修改k值,以便继续向下进行筛选
		}
	}
	A[k] = A[0];
}

void BuildMaxHeap(ElemType A[], int len)
{
	for (int i = len / 2; i > 0; i--)
		HeadAdjust(A, i, len);
}

void HeapSort(ElemType A[], int len)
{
	BuildMaxHeap(A, len);
	for (int i = len; i > 1; i--)
	{
		swap(A[i], A[1]); //堆顶元素与堆底元素交换
		HeadAdjust(A, 1, i - 1); //调整剩余的i-1个元素
	}
}

int main()
{
	ElemType A[10] = {9999, 5, 3, 7, 54, 24, 23, 8, 12, 167};
	HeapSort(A, 9);
	for (int i=1;i<10;i++)
	{
		cout << A[i] << " ";
	}
}

三、归并排序

●  归并排序的动画演示详见:https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html

 原理:

2-路归并排序

将排序表中的 n 个记录看成 n 个有序的子表,每个子表长度为1。然后两两归并,得到 \small \left \lceil n/2 \right \rceil 个长度为1或2的有序表,继续两两归并......如此重复,直到合并为一个长度为 n 的有序表。

 图示:

 代码:

ElemType *B = (ElemType *)malloc((10 + 1) * sizeof(ElemType)); //辅助数组B

void Merge(ElemType A[],int low,int mid,int high)
{
	int i, j, k;
	//表的两段A[low,mid],A[mid+1,high]各自有序,将他们合并成一个有序表
	for (k = low; k <= high; k++)
		B[k] = A[k]; //全部复制
	for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++)
	{
		if (B[i] <= B[j]) //比较左右两段的元素
			A[k] = B[i++];//将较小的值复制到A中
		else
			A[k] = B[j++];
	}
	while (i <= mid) A[k++] = B[i++]; //若第一个表未检测完,复制
	while (j <= high) A[k++] = B[j++];//若第二个表未检测完,复制
}

void MergeSort(ElemType A[],int low,int high)
{
	if(low<high)
	{
		int mid = (low + high) / 2;
		MergeSort(A, low, mid);
		MergeSort(A, mid + 1, high);
		Merge(A, low, mid, high); //归并
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值