您的位置:首页 > 其它

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

2017-03-01 20:27 239 查看
【排序一】插入排序

一、选择排序

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
交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R

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);
}


运行结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: