1、堆排序
堆排序利用了大根堆(或小根堆)堆顶记录的
关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
(1)用大根堆排序的基本思想
① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
……
直到无序区只有一个元素为止。
时间复杂度为O(log
2
n)
#include<iostream>
using namespace std;
const int N = 10;
int arr[N];
int parent(int i)
{
return i / 2;
}//父节点
int left(int i)
{
return 2 * i;
}//孩子节点
int right(int i)
{
return 2 * i + 1;
}//右孩子节点
void heapadjust(int i,int n)
{
int l,r,largest;
l = left(i);
r = right(i);
if(l <= n && arr[l] > arr[i])
largest = l;
else largest = i;
if(r <= n && arr[r] > arr[largest] )
largest = r;
if(largest != i)
{
swap(arr[i],arr[largest]);
heapadjust(largest, n);
/*
交换后子部分可能不满足堆性质,所以递归调整。
堆:对任意一棵树的任意一个非叶子节点,该节点值应该大于等于(或小于)左右子节点的数据结构.
若满足大于等于,则为大顶堆;反之为小顶堆
*/
}
}
/*
初始调用buildheap将arr[1..n]变成最大堆
因为数组最大元素在arr[1],则可以通过将arr[1]与a[n]互换达到正确位置
现在新的根元素破坏了最大堆的性质,所以调用heapadjust调整,
使arr[1..n-1]成为最大堆,arr[1]又是arr[1..n-1]中的最大元素,
将arr[1]与arr[n-1]互换达到正确位置。
反复调用heapadjust(1, i - 1),使整个数组成从小到大排序。
*/
void buildheap(int n)//自底向上是因为要使最大元素升至堆顶
{
for(int i = n / 2;i > 0;i--)
heapadjust(i,n);
}
/*
交换只是破坏了以a[1]为根的二叉树最大堆性质,它的左右子二叉树还是具备最大堆性质。
这也是为何在build_maxheap时需要遍历n/2到1的结点才能构成最大堆,而这里只需要堆化arr[1]即可。
*/
void heap_sort(int n)
{
buildheap(n);
for(int i = n;i > 1;i--)
{
swap(arr[1],arr[i]);
heapadjust(1,i-1);
}
}
int main()
{
int i;
for(i = 1;i <= 10;i++)
cin>>arr[i];
heap_sort(10);
for(i = 1;i <= 10;i++)
cout<<arr[i]<<" ";
cout<<endl;
return 0;
}
2 、归并排序
归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经
排序序列之和,该空间用来存放合并后的序列
第二步:设定两个
指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
其中时间复杂度为nlogn,空间复杂度为o(n)
#include<iostream>
using namespace std;
int a[20];//定义数组a[]
int b[20];//定义辅助数组b[]
void merge(int low,int mid,int high)//合并函数
{
int i,j,k;
i = low;
j = mid + 1;
k = low;
while(i <= mid&&j <= high)
{
if(a[i] > a[j])//找出小的数给数组b[]
b[k++] = a[j++];
else b[k++] = a[i++];
}
while(i <= mid)//如果前一半没用完就继续给数组b[]
b[k++] = a[i++];
while(j <= high)//如果后一半没用完就继续给数组b[]
b[k++] = a[j++];
for(i = low;i <= high;i++)//将合并完的数组b[]复制给数组a[]
a[i] = b[i];
}
void merge_sort(int low,int high)//归并排序(其实就是递归的调用)
{
int mid;
if(low < high)
{
mid = (low+high) / 2;
merge_sort(low,mid);//左边的一半再用merge_sort
merge_sort(mid+1,high);//左边的一半再用merge_sort
merge(low,mid,high);//将排好序的前一半数和后一半数合并了
}
}
int main()
{
int n,i;
cin>>n;
for(i = 0;i < n;i++)
cin>>a[i];
merge_sort(0,n-1);
for(i = 0;i < n;i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}