排序问题

1.堆排序

要求:对于大顶堆,孩子节点的值小于父节点。

说明:数据会存储在数组中,从数组下标1开始存储根节点,假如i为父节点,则2*i是左孩子,2*i+1是右孩子。

方法:向上调整

步骤:

第一步:建堆,从有孩子节点p开始调整,将p的两个孩子节点较大者与父节点(p)交换,再调整交换过的以子节点为根的树,直到以p为根节点的树满足要求。

第二步:调整堆,因为根节点是最大的,保存后;将最后一个节点与根节点交换,调整树,直到满足要求。调用建堆的方法即可。

#include "iostream"
#include "vector"
using namespace std;
//大顶堆的构造,传入的p是父节点,使得以p为根节点的树,满足大顶堆
void HeapAdjust(vector<int> &vec,int p,int n){
    int temp=vec[p];
    for (int i = 2 * p; i <= n;i*=2)  {
        if (i < n&&vec[i] < vec[i + 1])
            i++;//找到两个孩子结点中最大的
        if (temp >= vec[i])
            break;//根节点大于子节点,则退出
        vec[p] = vec[i];    //将孩子节点放到父节点上
        p = i;
    }
    vec[p] = temp;//将开始的p节点放到最终位置
}

//大顶堆排序,调整堆
void HeapSort(vector<int> &vec){
    int n=vec.size()-1;
    //首先将无序数列转换为大顶堆
    for (int i = n / 2; i > 0;i--)    //注意由于是完全二叉树,所以我们从一半向前构造,传入父节点
        HeapAdjust(vec, i, n);
    
    //将最大的元素放入目标位置,然后将剩余元素重新构造大顶堆
    for (int i = n; i >1;i--){
        swap(vec[1], vec[i]);
        HeapAdjust(vec, 1, i - 1);
    }
}
int main(){
    //注意数组中第一个值是不参与排序的,无意义的
    vector<int> vec={0,23,4,1,2,5,45,3,345,55};
    HeapSort(vec);
    //1 2 3 4 5 23 45 55 345
    for (auto item:vec)cout<<item<<" ";
    return 0;
}

 

 

 

2.快排

快排有两个步骤:

第一步:选取数组的第一个数值为枢轴值,而后将数组调整为左边小于枢轴值,右边大于枢轴值。

第二步:分别对枢轴值左边和右边部分执行上述操作。

具体示例如下:

假如有如下数组

 一趟排序之后

而后分别对枢轴值左边部分和右边部分做同样的操作,最终得到有序数组。

对应的程序代码如下:

#include "iostream"
#include "vector"
using namespace std;
//一趟排序的第一种写法
int ajust_one(vector<int> &vec,int left,int right){
    int pivot=vec[left];
    //跳出循环是left=right
    while (left<right){
        //如下顺序不可调换
        while (left<right&&vec[right]>=pivot)right--;
        vec[left]=vec[right];
        while (left<right&&vec[left]<=pivot)left++;
        vec[right]=vec[left];

    }
    //将枢轴值放到归位
    vec[left]=pivot;
    return left;
}
//一趟排序的第二种写法
int ajust_two(vector<int> &vec,int left,int right){
    int left_start=left;
    int pivot=vec[left];
    //跳出循环是left=right
    while (left<right){
        //如下顺序不可调换
        while (left<right&&vec[right]>=pivot)right--;
        while (left<right&&vec[left]<=pivot)left++;
        swap(vec[left],vec[right]);//交换两个值
    }
    //将枢轴值放到归位
    vec[left_start]=vec[left];
    vec[left]=pivot;
    return left;
}
void quickSort(vector<int> &vec,int left,int right){
    if(left<right){
        int mid=ajust_one(vec,left,right);
        quickSort(vec,left,mid-1);
        quickSort(vec,mid+1,right);
    }
}
int main(){
    vector<int> vec={23,4,1,2,5,45,3,345,55};
    quickSort(vec,0,vec.size()-1);
    //1 2 3 4 5 23 45 55 345
    for (auto item:vec)cout<<item<<" ";
    return 0;
}

 

3.归并排序

经典的分治策略,先分成小问题,而后再合并,最后问题得到求解。

#include "iostream"
#include "vector"
using namespace std;
void ajust(vector<int> &vec,int left,int mid,int right){
    vector<int> temp(right-left+1,0);
    int left_start=left;//保存开始位置
    int mid_right=mid+1;//
    int i=0;
    while (left<=mid&&mid_right<=right){
        if(vec[left]<vec[mid_right])
            temp[i++]=vec[left++];
        else
            temp[i++]=vec[mid_right++];
    }
    //将未完成的部分,补到数组后面
    while(left<=mid)temp[i++]=vec[left++];
    while(mid_right<=right) temp[i++]=vec[mid_right++];
    //将临时数组的值拷贝到原数组中
    i=0;
    while(left_start<=right)vec[left_start++]=temp[i++];
}
void merge(vector<int> &vec,int left,int right){
    if(left<right){
        int mid=(left+right)/2;
        merge(vec,left,mid);//left到mid是有序,包括边界
        merge(vec,mid+1,right);//mid+1到right有序,包括边界
        ajust(vec,left,mid,right);
    }
}
int main(){
    vector<int> vec={23,4,1,2,5,45,3,345,55};
    merge(vec,0,vec.size()-1);
    //1 2 3 4 5 23 45 55 345
    for (auto item:vec)cout<<item<<" ";
    return 0;
}

 

堆排序图解可参考:https://www.cnblogs.com/chengxiao/p/6129630.html

归并排序详解可参考:https://www.cnblogs.com/chengxiao/p/6194356.html

posted @ 2019-11-18 13:47  suwenyuan  阅读(213)  评论(0编辑  收藏  举报