选择排序、锦标赛排序、堆排序)
2014-07-24 08:15
197 查看
根据排序过程中涉及的存储器的不同,排序分为内部排序和外部排序。
内部排序:在计算机随机存储器中进行的排序过程。包括:选择排序(堆排序),插入排序(希尔排序),快速排序、基数排序、归并排序
外部排序:排序过程中需对外存进行访问的排序过程。
一、选择排序
----------------------------------------------------------------------------------------------------------------
1.简单选择排序
原理:1.从左到右遍历,选择最小(大)的元素,与第一个交换,作为第一个。
2.遍历剩下的n-i+1个元素,再选最小(大)的元素,与第i个交换。
3.以此类推,直到排序完毕。
之所以称选择排序,是因为总要选择最小或最大的,选择排序图片。
最差时间复杂度:O(n^2)
最优时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
稳定性:不稳定
-----------------------------------------------------------------------------------------------------------------
2.树形选择排序(锦标赛排序)
原理:对N个记录的关键字进行两两比较,选出最小(大)的n/2个数,再进行新一轮的比较,直到选出最小(大)的。
1.把N个数放到完全二叉树的叶子节点,两两比较,选出最小的作为根节点,且保存到数组中
2.把最小的原始值设为无穷大,从那个地方开始新一轮比较
第一次需要比较n-1次,之后都是log2(n)次
复杂度:O(n*log2 n)
3.堆排序
1. 堆的性质:子节点的值总是小于他的父节点(大顶堆)
通常堆用一位数组来实现,起始数组为a[0]的情形下,节点i的左右节点为:
其左子节点的下标为 (2*i+1);
其右子节点的下标为 (2*i+2);
2. 原理:分为三步:
[1]最大堆调整:
在假定节点的子节点的二叉树都是最大堆的前提下,比较父节点和子节点,若小于子节点则调整,确保i为根的子树为最大堆。主要用 于刚开始创建最大堆和堆排序后调整a[0]和a[i]时,让a[0]比较后调整下沉,使新的堆成为最大堆。
[2]创建最大堆
将数组按顺序层序放在堆中,然后对所有非叶子节点进行最大堆调整,进行n/2次调整
[3]堆排序
创建最大堆以后,依次将末节点与根节点交换,删除末节点(即把length-1),然后从上到下对根节点进行最大对调整,直到堆中节点数为1,排序结束
最差时间复杂度:O(nlogn)
最优时间复杂度:O(nlogn)
平均时间复杂度:O(nlogn)
稳定性:不稳定
至此,选择排序总结完毕。
全部代码如下:
内部排序:在计算机随机存储器中进行的排序过程。包括:选择排序(堆排序),插入排序(希尔排序),快速排序、基数排序、归并排序
外部排序:排序过程中需对外存进行访问的排序过程。
一、选择排序
----------------------------------------------------------------------------------------------------------------
1.简单选择排序
原理:1.从左到右遍历,选择最小(大)的元素,与第一个交换,作为第一个。
2.遍历剩下的n-i+1个元素,再选最小(大)的元素,与第i个交换。
3.以此类推,直到排序完毕。
之所以称选择排序,是因为总要选择最小或最大的,选择排序图片。
最差时间复杂度:O(n^2)
最优时间复杂度:O(n^2)
平均时间复杂度:O(n^2)
稳定性:不稳定
#include<iostream> #include<algorithm> using namespace std; /************简单选择排序******************/ void SelectSort(int *a,int len) { for(int i = 0;i!=len;i++) { int k = i; int key = a[i]; for(int j=i+1;j!=len;j++) { if(a[j] < key)//不断遍历变换最小值 { k = j; key = a[j]; } } if(k != i) swap(a[i],a[k]);//把第i次遍历的最小值和a[i[交换 cout << a[i] <<" "; } } int main() { int length = 0; int i = 0; cin >> length; int a[100] = {0}; for(;i!=length;i++) cin>>a[i]; SelectSort(a,length); system("pause"); return 0; }
-----------------------------------------------------------------------------------------------------------------
2.树形选择排序(锦标赛排序)
原理:对N个记录的关键字进行两两比较,选出最小(大)的n/2个数,再进行新一轮的比较,直到选出最小(大)的。
1.把N个数放到完全二叉树的叶子节点,两两比较,选出最小的作为根节点,且保存到数组中
2.把最小的原始值设为无穷大,从那个地方开始新一轮比较
第一次需要比较n-1次,之后都是log2(n)次
复杂度:O(n*log2 n)
/************树形选择排序or锦标赛排序******************/ struct TreeNode{ int data; int arr_index;//记录当前节点是原数组的哪个元素 }; TreeNode *tree; void UpdataTree(TreeNode tree[],int index)//从上一次胜出的节点中开始更新 { while(index) { if(index % 2)//如果是左孩子 { if(tree[index].data <= tree[index+1].data)//左孩子小于右孩子 { tree[(index-1)/2].data = tree[index].data;//更新父节点的值 tree[(index-1)/2].arr_index = tree[index].arr_index;//更新父节点的arr_index } else { tree[(index-1)/2].data = tree[index+1].data; //更新父节点的 tree[(index-1)/2].arr_index = tree[index+1].arr_index; //更新父节点的arr_index } index = index/2;//更新索引,准备比较上一层父节点及其兄弟 } else//如果是右孩子 { if(tree[index-1].data <= tree[index].data)//左孩子小于右孩子 { tree[index/2-1].data = tree[index-1].data; //更新父节点的值 tree[index/2-1].arr_index = tree[index-1].arr_index;//更新父节点的arr_index } else//左孩子大于右孩子 { tree[index/2-1].data = tree[index].data; //更新父节点的值 tree[index/2-1].arr_index = tree[index].arr_index; //更新父节点的arr_index } index = index/2-1;//更新索引,准备比较上一层父节点及其兄弟 } } //while结束时得到一个胜者 } void ChampionSort(int arr[],int num)//锦标赛排序 { int LeafNodeSize= 1;//满二叉树的叶子节点数 int height = 1;//满二叉树的高度 while(LeafNodeSize < num)//循环结束后,满二叉树叶子节点应该是2的幂 { LeafNodeSize*=2; ++height; } int const TreeSize = LeafNodeSize *2 -1;//满二叉树所有节点个数正好是叶子个数的两倍 tree = new TreeNode[TreeSize];//动态分配比较树 int IndexLeafStart = LeafNodeSize -1;//叶子节点的开始下标,即最后一行 for(int i = IndexLeafStart,j=0;i<TreeSize;i++)//.初始化树,把Treesize个数a[0]……a[treesize]依次放到叶子节点tree[IndexLeafStart]上 { if(j < num) { tree[i].data = arr[j]; tree[i].arr_index = j; j++; } else { tree[i].data = DATA_MAX; tree[i].arr_index = -1; } } for(int k = TreeSize-1;k>1;k-=2)//左右子树两个节点比较,推选出小的作为父节点,求父节点标号 { if(tree[k-1].data <= tree[k].data) { tree[k/2-1].data = tree[k-1].data; //更新父节点的值 tree[k/2-1].arr_index = tree[k-1].arr_index; //更新父节点的arr_index } else { tree[k/2-1].data = tree[k].data; //更新父节点的值 tree[k/2-1].arr_index = tree[k].arr_index; //更新父节点的arr_index } } //此for结束后既得到首个获胜者树 for(int m = 0;m<num-1;m++)//处理剩余的num-1个元素 { arr[m]=tree[0].data; //每次都将获胜树存入数组 int winIndex = tree[0].arr_index + IndexLeafStart;//直接保存获胜者的根信息 tree[winIndex].data = DATA_MAX; //把获胜者的值设为无穷大之后再一轮比较 UpdataTree(tree, winIndex); cout << arr[m] <<" "; } arr[num-1] = tree[0].data; cout << arr[num-1] <<" "; delete [] tree; }
<span style="font-weight: bold; color: rgb(204, 0, 0); font-size: 24px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">---------------------------------------------------------------------------------------------------------------------</span>
3.堆排序
1. 堆的性质:子节点的值总是小于他的父节点(大顶堆)
通常堆用一位数组来实现,起始数组为a[0]的情形下,节点i的左右节点为:
其左子节点的下标为 (2*i+1);
其右子节点的下标为 (2*i+2);
2. 原理:分为三步:
[1]最大堆调整:
在假定节点的子节点的二叉树都是最大堆的前提下,比较父节点和子节点,若小于子节点则调整,确保i为根的子树为最大堆。主要用 于刚开始创建最大堆和堆排序后调整a[0]和a[i]时,让a[0]比较后调整下沉,使新的堆成为最大堆。
[2]创建最大堆
将数组按顺序层序放在堆中,然后对所有非叶子节点进行最大堆调整,进行n/2次调整
[3]堆排序
创建最大堆以后,依次将末节点与根节点交换,删除末节点(即把length-1),然后从上到下对根节点进行最大对调整,直到堆中节点数为1,排序结束
最差时间复杂度:O(nlogn)
最优时间复杂度:O(nlogn)
平均时间复杂度:O(nlogn)
稳定性:不稳定
/********************堆排序********************************* 1,堆调整 2.创建最大堆 3.堆排序 ****************** ****************** *********************/ void MaxHeapAdjust(int a[],int i,int n)//对节点i进行调整,n是数组元素个数 { int largest = 0; int left = i*2 + 1;//节点i的左节点 int right = i*2 + 2;//节点i的右节点 if(left< n && a[left]>a[i])//左节点和父节点比较 largest =left; else largest = i; if(right<n && a[right]>a[largest])//如果有右节点,且左小于右 largest = right; if(i != largest)//如果最大节点不是父节点 { swap(a[i],a[largest]); MaxHeapAdjust(a,largest,n);//下降父节点,跟子树最大堆比,其实经过创建最大堆时,子树都为最大堆了 } } //创建最大堆 void BuildMaxHeap(int a[],int length) { for(int i = length/2-1;i>=0;i--)//从非叶子节点开始进行最大堆调整 { MaxHeapAdjust(a,i,length); } } //堆排序 void HeapSort(int a[],int length) { BuildMaxHeap(a,length); for(int i = length-1;i>=0;i--)//从堆中删除数 { swap(a[0],a[i]); //cout << a[i] <<" "; MaxHeapAdjust(a,0,i); } for(int i = 0;i!=length;i++) { cout << a[i]<<" "; } }
至此,选择排序总结完毕。
全部代码如下:
#include<iostream>
#include<algorithm>
#define DATA_MAX (int)((unsigned)(~(int)0)>>1)
using namespace std;
/************选择排序******************/
void SelectSort(int *a,int len)
{
for(int i = 0;i!=len;i++)
{
int k = i;
int key = a[i];
for(int j=i+1;j!=len;j++)
{
if(a[j] < key)//不断遍历变换最小值
{
k = j;
key = a[j];
}
}
if(k != i)
swap(a[i],a[k]);//把第i次遍历的最小值和a[i[交换
cout << a[i] <<" ";
}
}
/************树形选择排序or锦标赛排序******************/
struct TreeNode{
int data;
int arr_index;//记录当前节点是原数组的哪个元素
};
TreeNode *tree;
void UpdataTree(TreeNode tree[],int index)//从上一次胜出的节点中开始更新
{
while(index)
{
if(index % 2)//如果是左孩子
{
if(tree[index].data <= tree[index+1].data)//左孩子小于右孩子
{
tree[(index-1)/2].data = tree[index].data;//更新父节点的值
tree[(index-1)/2].arr_index = tree[index].arr_index;//更新父节点的arr_index
}
else
{
tree[(index-1)/2].data = tree[index+1].data; //更新父节点的
tree[(index-1)/2].arr_index = tree[index+1].arr_index; //更新父节点的arr_index
}
index = index/2;//更新索引,准备比较上一层父节点及其兄弟
}
else//如果是右孩子
{
if(tree[index-1].data <= tree[index].data)//左孩子小于右孩子
{
tree[index/2-1].data = tree[index-1].data; //更新父节点的值
tree[index/2-1].arr_index = tree[index-1].arr_index;//更新父节点的arr_index
}
else//左孩子大于右孩子
{
tree[index/2-1].data = tree[index].data; //更新父节点的值
tree[index/2-1].arr_index = tree[index].arr_index; //更新父节点的arr_index
}
index = index/2-1;//更新索引,准备比较上一层父节点及其兄弟
}
} //while结束时得到一个胜者
}
void ChampionSort(int arr[],int num)//锦标赛排序
{
int LeafNodeSize= 1;//满二叉树的叶子节点数
int height = 1;//满二叉树的高度
while(LeafNodeSize < num)//循环结束后,满二叉树叶子节点应该是2的幂
{
LeafNodeSize*=2;
++height;
}
int const TreeSize = LeafNodeSize *2 -1;//满二叉树所有节点个数正好是叶子个数的两倍
tree = new TreeNode[TreeSize];//动态分配比较树
int IndexLeafStart = LeafNodeSize -1;//叶子节点的开始下标
for(int i = IndexLeafStart,j=0;i<TreeSize;i++)//.初始化树
{
if(j < num)
{
tree[i].data = arr[j];
tree[i].arr_index = j;
j++;
}
else
{
tree[i].data = DATA_MAX;
tree[i].arr_index = -1;
}
}
for(int k = TreeSize-1;k>1;k-=2)//求父节点标号
{
if(tree[k-1].data <= tree[k].data)
{
tree[k/2-1].data = tree[k-1].data; //更新父节点的值
tree[k/2-1].arr_index = tree[k-1].arr_index; //更新父节点的arr_index
}
else
{
tree[k/2-1].data = tree[k].data; //更新父节点的值
tree[k/2-1].arr_index = tree[k].arr_index; //更新父节点的arr_index
}
} //此for结束后既得到首个获胜者树
for(int m = 0;m<num-1;m++)//处理剩余的num-1个元素
{
arr[m]=tree[0].data; //每次都将获胜树存入数组
int winIndex = tree[0].arr_index + IndexLeafStart;//直接保存获胜者的根信息
tree[winIndex].data = DATA_MAX; //把获胜者的值设为无穷大之后再一轮比较
UpdataTree(tree, winIndex);
cout << arr[m] <<" ";
}
arr[num-1] = tree[0].data;
cout << arr[num-1] <<" ";
delete [] tree;
}
/********************堆排序********************************* 1,堆调整 2.创建最大堆 3.堆排序 ****************** ****************** *********************/ void MaxHeapAdjust(int a[],int i,int n)//对节点i进行调整,n是数组元素个数 { int largest = 0; int left = i*2 + 1;//节点i的左节点 int right = i*2 + 2;//节点i的右节点 if(left< n && a[left]>a[i])//左节点和父节点比较 largest =left; else largest = i; if(right<n && a[right]>a[largest])//如果有右节点,且左小于右 largest = right; if(i != largest)//如果最大节点不是父节点 { swap(a[i],a[largest]); MaxHeapAdjust(a,largest,n);//下降父节点,跟子树最大堆比,其实经过创建最大堆时,子树都为最大堆了 } } //创建最大堆 void BuildMaxHeap(int a[],int length) { for(int i = length/2-1;i>=0;i--)//从非叶子节点开始进行最大堆调整 { MaxHeapAdjust(a,i,length); } } //堆排序 void HeapSort(int a[],int length) { BuildMaxHeap(a,length); for(int i = length-1;i>=0;i--)//从堆中删除数 { swap(a[0],a[i]); //cout << a[i] <<" "; MaxHeapAdjust(a,0,i); } for(int i = 0;i!=length;i++) { cout << a[i]<<" "; } }
int main()
{
int length = 0;
int i = 0;
cout << "请输入排序数组长度:" << endl;
cin >> length;
int a[100] = {0};
cout << "请输入排序数组:" << endl;
for(;i!=length;i++)
cin>>a[i];
cout << "简单选择排序:" << endl;
SelectSort(a,length);
cout << endl;
cout << "锦标赛排序:" << endl;
ChampionSort(a,length);
cout << endl;
cout << "堆排序:" << endl;
HeapSort(a,length);
cout << endl;
system("pause");
return 0;
}
相关文章推荐
- 常见的五类排序算法图解和实现(选择类:简单选择排序,锦标赛排序,树形选择排序,堆排序)
- 排序算法(二)选择类排序:简单选择排序,堆排序,锦标赛排序
- 常见的五类排序算法图解和实现(选择类:简单选择排序,锦标赛排序,树形选择排序,堆排序)
- 各种排序算法总结----基数排序、归并排序、插入排序、冒泡排序、选择排序、快速排序、堆排序、希尔排序
- 21 改良的选择排序--堆排序
- 选择排序之堆排序
- 程序员必知的8大排序(二)-------简单选择排序,堆排序(java实现)
- java实现七种排序 (插入排序, 希尔排序, 插入排序, 快速排序, 简单选择排序, 堆排序, 归并排序)
- 几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 选择排序--选择排序和堆排序
- C# 插入排序 冒泡排序 选择排序 高速排序 堆排序 归并排序 基数排序 希尔排序
- 八大排序方法汇总(选择排序,插入排序-简单插入排序、shell排序,交换排序-冒泡排序、快速排序、堆排序,归并排序,计数排序)
- 七种排序(直接插入、折半插入、希尔、起泡、快速、简单选择、堆排序)
- 选择排序--堆排序
- 插入法排序、选择排序、冒泡法、快速排序、堆排序的C实现
- 【排序二】选择排序(选择排序&&堆排序)
- 排序之选择排序、堆排序、归并排序、高速排序
- 选择排序——堆排序
- 选择排序----直接选择排序与堆排序
- 插入排序、冒泡排序、选择排序、希尔排序、快速排序、归并排序、堆排序和LST基数排序——C++实现