【排序二】选择排序(选择排序&&堆排序)

29 篇文章 1 订阅
21 篇文章 8 订阅

【排序一】插入排序



一、选择排序

1、基本思想

    顾名思义,选择排序就是每次选一个数据放到其应该出现的位置,以升序(降序)为例,首先选最小(最大)的数据放到正确位置,接着再选次小(次大)的数据放到合适的位置,以此类推,直到最大(最小)的数据被放入最后一个位置,排序就算完成。

总体算法分三步完成:选数据--->将所选数据放入合适位置--->缩小需要排序的范围

图解(以升序为例):



2、排序效果图:



3、时间复杂度&&空间复杂度

选择排序的比较次数O(n^2),比较次数与关键字的初始状态无关,总的比较次数:

N=(n-1)+(n-2)+...+1=n*(n-1)/2。 

交换次数O(n),最好情况是,已经有序,交换0次;最坏情况是,逆序,交换n-1次。

故此,选择排序的时间复杂度为O(N^2)

选择排序的空间复杂度为O(1)


4、实现代码

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;

//选择排序:选一个数(最大或最小)-->将此数放在正确的位置上-->缩小范围
void PrintArray(const int* a,const size_t n)
{
	for (size_t i = 0; i < n; ++i)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
}

//C语言风格的选择排序
void SelectSort1(int* a,size_t n)
{
	assert(a);
	for (size_t i = 1; i < n; ++i)
	{
		int minIndex = i;//未排序区间最小数据的位置下标
		int start = i - 1;//未排序区间的第一个数据下标

		//选出第二部分最小的数据
		for (size_t j = i+1; j < n-1; ++j)//用j = i+1来缩小未排序区间范围
		{
			if (a[j+1]<a[minIndex])
			{
				swap(a[j+1],a[minIndex]);
			}
		}
		//第二部分最小的数据和第二部分第一个数据进行比较
		if (a[minIndex] < a[start])
		{
			swap(a[start],a[minIndex]);
		}
	}
}

//C++风格的选择排序
void SelectSort2(int* a,size_t n)
{
	assert(a);
	int minIndex = 0;
	for (size_t i = 0; i < n - 1; ++i)
	{
		minIndex = i;      //未排序区间最小数据的位置下标
		size_t pos = i + 1;//未排序区间的第一个数据下标

		while(pos < n)//选出未排序区间最小的数据
		{
			if (a[pos] < a[minIndex])
			{
				minIndex = pos;
			}
			++pos;
		}
		swap(a[i],a[minIndex]);//将所选数据放到正确位置
	}
}

//选择排序的优化:每次既选出最大的数,也选出最小的数
void SelectSort3(int* a,size_t n)
{
	assert(a);
	int left = 0;//未排序区间的左下标
	int right = n - 1;//未排序区间的右下标

	while (left < right)
	{
		int minIndex = left;//未排序区间最小数据的位置下标
		int maxIndex = right;//未排序区间最大数据的位置下标

		//选出最大和最小数据的下标
		for (int i = left; i <= right; ++i)
		{
			if (a[i] < a[minIndex])
			{
				minIndex = i;
			}
			if (a[i] > a[maxIndex])
			{
				maxIndex = i;
			}
		}
		//修正:最大值在最小位置或最小值在最大位置
		swap(a[maxIndex],a[right]);//将最大数据放到区间最右侧
		if (minIndex == right)
		{
			minIndex = maxIndex;
		}
		swap(a[minIndex],a[left]);//将最小数据放在区间最左侧

		left++;//缩小区间范围
		right--;//缩小区间范围
	}
}

void TestSelectSort()
{
	int a[] = {9,5,4,2,3,6,8,7,1,0};
	size_t sz = sizeof(a)/sizeof(a[0]);

	//SelectSort1(a,sz);
	//SelectSort2(a,sz);
	SelectSort3(a,sz);
	PrintArray(a,sz);
}

运行结果:



二、堆排序

1、堆介绍

数据结构是一种数组对象,它可以被视为一棵完全二叉树结构

堆结构的二叉树存储是:
(最)大堆:每个父节点的都大于孩子节点
(最)小堆:每个父节点的都小于孩子节点

如图所示:



2、堆排序的基本思想

      堆积排序(Heapsort)是指利用这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

1)将初始待排序关键字序列(R1,R2....Rn) 构建 大顶堆 ,此堆为初始的无序区;
2) 将堆顶元素R[1]与最后一个元素R[n]交换 ,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n]; 
3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1) 调整 新堆 ,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

图解(假设为升序):



3、堆排序效果图



4、时间复杂度&&空间复杂度

   假设一棵完全二叉树有N个节点,则它的的高度为lgN,堆排序中的比较次数为2*lgN,交换次数为N次,

堆排序的时间复杂度为O(N*lgN)

由于占用了有限个空间,所以堆排序的空间复杂度为O(1)


5、代码实现

#pragma once
#include <iostream>
#include <assert.h>
using namespace std;

//选择排序:选一个数(最大或最小)-->将此数放在正确的位置上-->缩小范围
void PrintArray(const int* a,const size_t n)
{
	for (size_t i = 0; i < n; ++i)
	{
		cout<<a[i]<<" ";
	}
	cout<<endl;
}
void TestSelectSort()
{
	int a[] = {9,5,4,2,3,6,8,7,1,0};
	size_t sz = sizeof(a)/sizeof(a[0]);

	//SelectSort1(a,sz);
	//SelectSort2(a,sz);
	SelectSort3(a,sz);
	PrintArray(a,sz);
}

//堆调整:将堆的末端子结点作调整,使得子结点永远小于父结点
void AdjustDown(int* a,size_t n,size_t pos)
{
	size_t parent = pos;//父节点的下标
	size_t child = parent*2 + 1;//左孩子的下标
	while(child < n)
	{
		//选左右孩子中较大的
		if ((child+1 < n) && (a[child] < a[child+1]))
		{
			++child;
		}
		if (a[child] > a[parent])//如果孩子节点大于父节点的值,则交换
		{
			swap(a[child],a[parent]);

			//向下更新父节点和孩子节点
			parent = child;
			child = parent*2 + 1;
		}
		else
		{
			break;
		}
	}
}
//默认升序,所以应该建大堆
void HeapSort(int* a,size_t n)
{
	assert(a);
	//从最后一个非叶子节点开始向下调整
	for (int i = (n-2)>>1; i >= 0; --i)
	{
		AdjustDown(a,n,i);//创建最大堆:将堆所有数据重新排序
	}

	//缩小范围
	for (int i = n-1; i > 0; --i)
	{
		swap(a[0],a[i]);//堆排序:移除位在第一个数据的根结点
		AdjustDown(a,i,0);//并做最大堆调整的递归运算
	}
}

void TestHeapSort()
{
	int a[] = {9,5,4,2,3,6,8,7,1,0};
	size_t sz = sizeof(a)/sizeof(a[0]);

	HeapSort(a,sz);
	PrintArray(a,sz);
}

运行结果:



  • 7
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值