sorting(按从小到大排序)
一、堆排序(选择排序)
不稳定
步骤:
大顶堆:每个节点的值都大于等于其左右子节点的值(用于升序)
小顶堆:每个节点的值都小于等于其左右子节点的值(用于排序)
1、构造初始堆
将序列构造成二叉树(按下标构造)
2、构造大顶堆
(1)从最后一个非叶子节点开始,从右向左,从下至上进行调整(找出父节点、左子节点、右子节点中最大的与父节点交换,使得父节点最大),使得堆顶元素最大
(2)将堆顶元素与末尾元素交换,这样末尾元素最大(固定此末尾元素),重复(1),找到第二大元素,循环直到排好序
c++代码:
#include <math.h> #include <iostream> #include <vector> using namespace std;
//树结构 struct Tree{ int val; Tree* left; Tree* right; Tree(int i) : val(i),left(NULL),right(NULL) {} }; void HeapSort(vector<int>& sequence) { /***********************/ /******1、构造初始堆*******/ const int c_len = sequence.size(); //待排序序列长度 vector<Tree> trees; for(int i=0; i<c_len; ++i) { Tree tree(sequence[i]); trees.push_back(tree); } //计算二叉树层数 float f_layers = log(c_len+1) / log(2); int layers = ceil(f_layers); //二叉树层数 //按层序遍历构造二叉树 int layer_nodes, init_nodes, next_nodes; int node_index, left_index, right_index; for(int i=1; i<layers; ++i) { //第i层的节点编号为[init_nodes,next_nodes) layer_nodes = pow(2, i-1); //第i层节点个数 next_nodes = pow(2, i) - 1; //第i+1层第一个节点编号 init_nodes = next_nodes - layer_nodes; //第i层第一个节点编号 for(node_index=init_nodes; node_index<next_nodes; ++node_index) { left_index = next_nodes + (node_index - init_nodes) * 2; //左子节点的编号 if(left_index >= c_len) break; trees[node_index].left = &trees[left_index]; right_index = left_index + 1; //右子节点的编号 if(right_index >= c_len) break; trees[node_index].right = &trees[right_index]; } } /********************/ /*******************/ /****2、构造大顶堆*****/ int temp; //交换用的临时值 int len = c_len; //待排序序列有效长度(去除末尾已经排好序的节点) int current_layer, valid_layer, last2_index; float f_current_layer, f_valid_layer; while(len > 1) { f_valid_layer = log(len+1) / log(2); valid_layer = ceil(f_valid_layer); //去除末尾已经排好序的节点之后,树的有效层数 last2_index = pow(2, valid_layer) - 2; //按有效层数,倒数第二层最后一个节点的编号 /********************************************/ /****2(1)对堆进行调整,使得堆顶元素最大******/ for(int i=last2_index; i>=0; --i) if(trees[i].left != NULL) { f_current_layer = log(i+1) / log(2) + 1; current_layer = floor(f_current_layer); //当前节点(第i个节点)所在层数 left_index = pow(2, current_layer) - 1 + (i - pow(2, current_layer-1) + 1) * 2; //当前节点的左子节点编号 right_index = left_index + 1; //当前节点的右子节点编号 if(left_index <= len-1) //左子节点为有效节点 if((right_index<=len-1) && (trees[i].right!=NULL)) //右子节点为有效节点 if((trees[i].left->val) >= (trees[i].right->val)) if((trees[i].left->val) > (trees[i].val)) { temp = trees[i].val; trees[i].val = trees[i].left->val; trees[i].left->val = temp; } else if((trees[i].right->val) > (trees[i].val)) { temp = trees[i].val; trees[i].val = trees[i].right->val; trees[i].right->val = temp; } else if((trees[i].left->val) > (trees[i].val)) { temp = trees[i].val; trees[i].val = trees[i].left->val; trees[i].left->val = temp; } } /**********************************/ /**********************************/ /*****2(2)将堆顶元素与末尾元素交换*****/ temp = trees[0].val; trees[0].val = trees[len-1].val; trees[len-1].val = temp; --len; } /******************************/ /*****得到排序后的sequence*****/ len = sequence.size(); for(int i=0; i<len; i++) sequence[i] = trees[i].val; }
空间复杂度:如果原序列以初始堆的形式给出,只有temp占用空间
S(n) = 1
时间复杂度:
任意情况:时间复杂度 = 外层循环次数(while) x 内层循环次数(for) x 常数
外层循环次数为序列长度n, 内层循环次数为树的高度lgn+1向下取整
即T(n) = (n x lgn) x 常数 = θ(nlgn)