(1)本质:每一趟从给定待排序序列A[ 1......n ] ,选择出第i小元素,并和A[i]交换。
代码:
/************************************************* 算法:简单选择排序(升序) 时间复杂度为O(n^2) **************************************************/ void simpleSelectSort(int *arr , int len) { for(int i =0;i运行结果:
(2)性能分析
简单选择排序记录移动操作次数较少,这点优于冒泡排序。有两层循环,所以时间复杂度为O(n^2)
2.堆排序
2.1 堆的定义n个元素的序列{k1,k2,…,kn}当且仅当满足下列关系之一时,称之为堆。
情形1:ki <= k2i 且ki <= k2i+1 (最小化堆或小顶堆)
情形2:ki >= k2i 且ki >= k2i+1 (最大化堆或大顶堆)
其中i=1,2,…,n/2向下取整;
2.2 堆排序(以大顶堆为例,根节点从0开始)
输出堆顶最大值之后,使得剩余n-1个元素的序列重建一个堆;如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。(升序---大顶堆,降序---小顶堆)
(1)堆的存储
一般用数组来表示堆,若根结点存在序号0处, i结点的父结点下标就为(i-1)/2。i结点的左右子结点下标分别为2*i+1和2*i+2。
(注:如果根结点是从1开始,则左右孩子结点分别是2i和2i+1。)
最大化堆如下:
实现堆排序需要解决两个问题:
1.如何由一个无序序列建成一个堆?
2.如何在输出堆顶元素之后,调整剩余元素成为一个新的堆?
问题2: 一般在输出堆顶元素之后,视为将这个元素排除,然后用表中最后一个元素填补它的位置,自上向下进行调整:首先将堆顶元素和它的左右子树的根结点进行比较,把最大的元素交换到堆顶;然后顺着被破坏的路径一路调整下去,直至叶子结点,就得到新的堆。自堆顶至叶子的调整过程为"筛选"过程。无序序列建立堆的过程就是一个反复"筛选"过程。
帅选过程代码:
/**************************************************** 堆排序是一种选择排序;在最坏的情况下,其时间复杂度也为O(nlogn) 其运行时间主要耗费在建初始堆和调整建新堆时进行的反复“筛选”上 ****************************************************/ /* 函数功能:当堆的某一个节点破坏了堆的性质,调用此函数进行调整剩余的元素,使其为新堆 k :k节点破坏了堆的性质 */ void maxHeap(int *arr,int len,int k) { //递归边界条件 if (k>len/2-1) //最后一个非叶子节点的下标为len/2-1; { return; } //1.比较k节点左右孩子的值,找出左右孩子的较大值 int i=0; //因为假设根结点的序号为0而不是1,所以i结点左孩子和右孩子分别为2i+1和2i+2 if ((2*k+2<=len-1)&&arr[2*k+1]
问题1:构建初始堆
构建堆就是对所有的非叶子节点进行筛选。最后一个非叶子节点的下标为([n/2]-1),所以筛选只需从([n/2]-1)节点开始,自下向上的进行调整,直到堆顶。
/***************************************************************************** 1.给定一个数组,首先根据该数组元素构造一个完全二叉树。 2.从最后一个非叶子结点开始,每次都是从父结点、左孩子、右孩子中进行比较交换; 3.交换可能会引起孩子结点不满足堆的性质,所以每次交换之后需要重新对被交换的孩子结点进行调整。 最后一个非叶子节点下标:[n/2]-1 (根节点的下标为0), n表示节点个数 ******************************************************************************/ void createHeap(int *arr , int len) { //最大堆----------自下向上调整,数组的下标从0开始 for (int i=len/2-1;i>=0;i--) { maxHeap(arr,len,i); } }(3)堆排序
首先输出堆顶元素(因为它是最值),将堆顶元素和最后一个元素交换,这样,第n个位置(即最后一个位置)作为有序区,前n-1个位置仍是无序区,对无序区进行调整,得到堆之后,再交换堆顶和最后一个元素,这样有序区长度变为2。不断进行此操作,将剩下的元素重新调整为堆,然后输出堆顶元素到有序区。每次交换都导致无序区-1,有序区+1。不断重复此过程直到有序区长度增长为n-1,排序完成。
void heapSort(int *arr , int len) { //1. 构建堆 createHeap(arr,len); //2.堆排序 for (int i=0;i
(4)反思堆排序
仔细回想一下筛选法调整堆的过程我们发现,第i次调整堆,其实就是把A中的第i大元素放到首位置A[0],然后A[0]和A[n-i-1]互换.这样A[(n-i-1)...n]就已经有序,于是又把A[0...n-i-2]看成一个堆再进行排序,如此重复。调整堆不就是选择出待排序序列中的最大值吗?所以堆排序的本质和选择排序的本质是一样的。选择一个待排序序列中的最小(大)值,这就是选择排序的本质。所以,堆排序是一种选择排序。
(5)性能分析
堆排序时间=建堆时间+调整堆时间。从上文中知道建堆时间复杂度为O(n*log2n)。筛选法调整堆(maxHeap函数)时间O(log2n),总共循环了n-1次maxHeap函数,所以调整堆时间复杂度为O(n*log2n)。得出堆排序时间复杂度O(n*log2n)。且在最坏情况下时间复杂度也是O(n*log2n)。此外堆排序是不稳定的原地排序算法。
(6)测试代码void test() { int arr[]={16,20,8,7,17,3,30,1,51,9,6}; int len = sizeof(arr)/sizeof(int); cout<<"排序前:"; printArr(arr,len); cout<
附件:辅助函数
void swap(int *p,int *q) { int tmp; tmp =*p; *p = *q; *q = tmp; } void printArr(int *arr,int len) { for (int i=0;i