您的位置:首页 > 其它

选择排序、锦标赛排序、堆排序)

2014-07-24 08:15 197 查看
根据排序过程中涉及的存储器的不同,排序分为内部排序和外部排序。

内部排序:在计算机随机存储器中进行的排序过程。包括:选择排序(堆排序),插入排序(希尔排序),快速排序、基数排序、归并排序

外部排序:排序过程中需对外存进行访问的排序过程。

一、选择排序

----------------------------------------------------------------------------------------------------------------

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;

}


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