数据结构复习(八)—— 排序

第九章 排序

排序(sort):重新排序表中的元素,使表中的元素满足按关键字有序的过程。
稳定性:
稳定的:关键字相同的元素在排序之后相对位置不变。
排序算法:
内部排序:数据都都在内存中。关注算法时间、空间复杂度更低。
外部排序:数据太多,无法全部放入内存。 还需关注使读/写磁盘次数更少。
1. 直接插入排序
算法思想:每次将一个待排序的记录按其关键字大小插入到前面已排好序的子序列中,直到全部记录插入完成。
//直接插入排序
void InsertSort(int A[],int n)
{
    int i,j,temp;
    for(i=1;i<n;i++)    //将各元素插入已排好序的序列中
        if(A[i]<A[i-1])     //若A[i]关键字小于前驱
        {
            temp=A[i];      //用temp暂存A[i]
            for(j=i-1;j>=0 && A[j]>temp;j--)    //检查所有前面已排好序的元素
                A[j+1]=A[j];
            A[j+1]=temp;     //复制到插入位置
        }
}
2.折半插入排序:直接插入排序的优化。
思路:先用折半查找找到应该插入的位置,再移动元素。
时间复杂度:O(n^2)
void InsertSort(int A[],int n)
{
    int i,j,low,high,mid;
    for(i=2;i<=n;i++)   //依次将A[2]~A[n]插入前面的已排序序列
    {
        A[0]=A[i];  //将A[i]暂存到A[0]
        low=1;high=i-1; //设置折半查找的范围
        while(low<=high)    //折半查找(默认递增有序)
        {
            mid=(low+high)/2;   //取中间点
            if(A[mid]>A[0])
                high=mid-1; //查找左半子表
            else
                low=mid+1;  //查找右半子表
        }//当low>high时,折半查找停止,此时high+1=low
        //应将[low,i-1](或[high+1,i-1])内的元素全部右移,
        //并将A[0]复制到low(high+1)所指位置。
        for(j=i-1;j>=high+1;j--)  
            A[j+1]=A[j];
        A[high+1]=A[0]; //将A[0]复制到low(high+1)所指位置。
    }
}
3. 希尔排序:先追求表中元素部分有序,再逐渐逼近全局有序
//希尔排
void ShellSort(int A[],int n)
{
    int d,i,j;
    //A[0]只是暂存单元,不是哨兵,当j<=0时,插入位置已到
    for(d=n/2;d>=1;d=d/2)   //步长变化
        for(i=d+1;i<=n;i++) //i首先指向第一个子表的第二个元素
            if(A[i]<A[i-d]) //需将A[i]插入有序增量子表
            {
                A[0]=A[i];  //当前指向的元素暂存在A[0]
                for(j=i-d;j>0 && A[0]<A[j];j=j-d)
                    A[j+d]=A[j];    //j指向的元素后移到j+d,查找插入的位置
                A[j+d]=A[0];
            }//if
}

4. 冒泡排序
(1)从后往前冒泡
//交换
void swap(int &a,int &b)
{
    int temp = a;
    a=b;
    b=temp;
}

//冒泡排序--从后往前冒
void BubbleSort(int A[],int n) //从后往前冒
{
    for(int i=0;i<n-1;i++)  //i所指位置之前的元素已经"有序"
    {   bool flag=false;    //表示本趟冒泡是否发生交换的标志
        for(int j=n-1;j>i;j--)  //一趟冒泡排序
        {
            if(A[j-1]>A[j]) //若为逆序
            {
                swap(A[j-1],A[j]);  //交换
                flag=true;
            }
        }
        if(flag==false)
            return;     //若未发生交换,说明表已经有序
    }    
}
(2)从前往后冒泡
void BubbleSort(int A[],int n) 
{
    for(int i=0;i<n-1;i++)  //i所指位置之前的元素已经"有序"
    {   bool flag=false;    //表示本趟冒泡是否发生交换的标志
        for(int j=0;j<n-1-i;j++)  //一趟冒泡排序
        {
            if(A[j]>A[j+1]) //若为逆序
            {
                swap(A[j],A[j+1]);  //交换
                flag=true;
            }
        }
        if(flag==false)
            return;     //若未发生交换,说明表已经有序
    }    
}
5. 快速排序
//快速排序
void QuickSort(int A[],int low,int high)
{
    if(low<high)    //递归跳出条件
    {
        int pivotpos=Partition(A,low,high); //划分
        QuickSort(A,low,pivotpos-1);    //划分左子表
        QuickSort(A,pivotpos+1,high);   //划分右子表
    }
}
//用第一个元素将待排序序列划分为左右两个部分
int Partition(int A[],int low,int high)
{
    int pivot=A[low];   //第一个元素作为枢轴
    while(low<high)     //用low、high搜索枢轴的最终位置
    {
        while(low<high && A[high]>=pivot)
            high--;
        A[low]=A[high]; //比枢轴小的元素移动到左端
        while(low<high && A[low]<=pivot)
            low++; 
        A[high]=A[low]; //比枢轴大的元素移动到右端
    }
    A[low]=pivot;   //枢轴元素存放到最终位置
    return low;     //返回存放枢轴的最终位置
}
6. 简单选择排序
//交换
void swap(int &a,int &b)
{
    int temp=a;
    a=b;
    b=temp;
}
void SelectSort(int A[],int n)
{
    for(int i=0;i<n-1;i++)  //共进行n-1趟排序
    {
        int min=i;          //记录最小元素位置
        for(int j=i+1;j<n;j++)//在A[i,n-1]中选择最小的元素
            if(A[j]<A[min])     //更新最小元素位置
                min=j;
        if(min!=i)
            swap(A[i],A[min]);  //swap()共移动元素3次
    }
}
7. 堆排序
(1)建立大根堆
//建立大根堆
void BuildMaxHeap(int A[],int len)
{
    for(int i=len/2;i>0;i--)    //从后往前调整所以非叶子结点
        HeadAdjust(A,i,len);
}

//将以k为根的子树调整为大根堆
void HeadAdjust(int A[],int k,int len)
{
    A[0]=A[k];  //A[0]暂存子树的根结点
    for(int i=2*k;i<=len;i*=2)  //沿key较大的子节点向下筛选
    {
        if(i<len && A[i]<A[i+1])
            i++;    //取key较大的子节点的下标
        if(A[0]>=A[i])
            break;  //筛选结束
        else
        {
            A[k]=A[i];  //将A[i]调整到双亲结点上
            k=i;
        }
    }
    A[k]=A[0];  //被筛选结点的值放入最终位置
}
(2)堆排序的完整逻辑——大顶堆得到的排序序列是递增的
void HeapSort(int A[],int len)
{
    BuildMaxHeap(A,len);    //初始建立大顶堆
    for(int i=len;i>1;i--)  //n-1趟的交换和建堆过程
    {                       //i指向当前待排序元素序列中的最后一个(堆底元素
        swap(A[i],A[1]);    //堆顶元素和堆底元素交换
        HeadAdjust(A,1,i-1);//把剩余待排序元素整理成大顶堆
    }
}
完整代码:
//建立大根堆:时间复杂度O(n)
void BuildMaxHeap(int A[],int len)
{
    for(int i=len/2;i>0;i--)    //从后往前调整所以非叶子结点
        HeadAdjust(A,i,len);
}

//将以k为根的子树调整为大根堆
void HeadAdjust(int A[],int k,int len)
{
    A[0]=A[k];  //A[0]暂存子树的根结点
    for(int i=2*k;i<=len;i*=2)  //沿key较大的子节点向下筛选
    {
        if(i<len && A[i]<A[i+1])
            i++;    //取key较大的子节点的下标
        if(A[0]>=A[i])
            break;  //筛选结束
        else
        {
            A[k]=A[i];  //将A[i]调整到双亲结点上
            k=i;
        }
    }
    A[k]=A[0];  //被筛选结点的值放入最终位置
}

//堆排序的完整逻辑---大顶堆得到的排序序列是递增的
void HeapSort(int A[],int len)
{
    BuildMaxHeap(A,len);    //初始建立大顶堆
    for(int i=len;i>1;i--)  //n-1趟的交换和建堆过程
    {                       //i指向当前待排序元素序列中的最后一个(堆底元素
        swap(A[i],A[1]);    //堆顶元素和堆底元素交换
        HeadAdjust(A,1,i-1);//把剩余待排序元素整理成大顶堆
    }
}
8. 归并排序
//归并排序
int *B=(int *)malloc(sizeof(int));  //辅助数组B,长度与要排序的元素个数一致

//A[low...mid]和A[mid+1,high]各自有序,将两部分归并
void Merge(int A[],int low,int mid,int high)
{
    int i,j,k;
    for(k=low;k<=high;k++)  //把[low,high]内的元素复制到辅助数组B
        B[k]=A[k];  //将A中所有元素复制到B中
    for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++)  //归并
    {
        if(B[i]<=B[j])  //两元素相等时,优先使用靠前的那个(稳定性)
        {
            A[k]=B[i];  //将较小值复制到A中
            i++;
        }    
        else
        {
            A[k]=A[j];
            j++;
        }
    }//for

    /*没有归并完的部分复制到尾部*/
    while(i<=mid)
        A[k++]=B[i++];
    while(j<=high)
        A[k++]=B[j++];
}

void MergeSort(int A[],int low,int high)
{
    if(low<high)
    {
        int mid=(low+high)/2;   //从中间划分
        MergeSort(A[],low,mid); //对左半部分归并排序
        MergeSort(A[],mid+1,high);//对右半部分归并排序
        Merge(A[],low,mid,high);//归并
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wings(hha)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值