two pointers、归并排序、快速排序问题

算法学习第十二篇

1.two pointers

概念:two pointers广义上概念就是利用问题本身与序列的特性,利用下标i、j对序列进行扫描,以较低的复杂度来解决问题,其实也不太像是一种算法,说来可以看做是一种编程技巧,一种思想比较适合。

例如在一个递增序列中找到a+b=c的数然后输出a,b的值,c是我们自己指定的数,常规做法很容易想到,两个下标遍历序列做个二重循环就可以解决问题,时间复杂度为o(n^2),但是这个方法是比较烂的,当序列数字较多时时间复杂度会非常的高,程序可行性大打折扣,这时候就要另辟蹊径。

加入现在定义i为A序列首位,j为A序列末位,两下标往中间移动,设计while循环终止条件(i>=j)即可,然后比对是否存在满足条件的i,j然后进行输出。如果此时已经找到一个A[i]+A[j]=c,想要继续分析找到合适的i和j,下一步应该如何选择才能更加的高效,显然可以从给出的背景条件来看,由A序列是个递增序列可以知道,如果在找到的上述A[i]+A[j]=c条件下将i单独后移显然结果大于c的,而将j前移显然后来的结果都是小于c的,所以下一个查找区间应该[i+1,j-1]开始。再如果找到的A[i]+A[j]>c,显然此时如果将i后移这个结果只会继续增加,所以下一个查找区间应该只会在[i,j-1]内,反之同样成立。说的有点模糊,但请好好分析下,这个思想应该是不太难的,用此方法实现复杂度显然降低不少,此时复杂度为o(n),实现的伪代码如下:

while(i<j)
{
    if(a[i]+a[j]==c)
    {
        printf("%d %d\n", i,j)
        i++;
        j--;
    }
    else if(a[i]+a[j]<m)
    {
        i++;
    }
    else
    {
        j--;
    }
}

2.归并排序

归并排序是一种比较特殊的排序,因为它不仅可以存在于内排序中,它也是外排序的基本方法,使用范围较广,在磁盘排序中也有很多应用,最基本的归并就是二路归并,其实不然也可以进行k路归并,思路都是一样的,下面对二路归并进行详解。

二路归并是将一个序列两两进行分组,每个组内进行排序,然后再将这些组组两两归并,两个组合并并排序,从上到下的图解如下:

                                                                    

思路不是很难,实现方法难点就在于序列的合并问题了,其实在链表里面这种序列合并并使其递增是个司空见惯的操作了,在归并排序里面只不过就是一个嵌入的方法了,基本思想不变,比较a[i],b[j]的大小,两个都是各自序列里面的首位,取较小者加入到新序列中,并将对应的下标增加即可,递归实现方法如下:

const max=100;
void mergesort(int a[],int l1,int r1,int l2,int r2)
{
    int i=l1,j=l2;
    int b[max],k=0;
    while(i<r1&&j<r2)
    {
        if(a[i]<=a[j])
        {
            b[k++]=a[i++];
        }
        else
        {
            b[k++]=a[j++];
        }
    }
    while(i<=r1) b[k++]=a[i++];   //剩余元素加入
    while(j<=r2) b[k++]=a[j++];
    for(int i=0;i<k;i++)
    {
        a[l1+i]=b[i];  //新数组返回到原数组内
    }
}

void gbsort(int a[],int left,int right)
{
    if(left<right)
    {
        int mid=(left+right)/2;
        gbsort(a,left,mid);
        gbsort(a,mid,right);
        mergesort(a,left,mid,mid+1,right);
    }
} 

3.快速排序

快速排序是一种非常经典的排序算法,无论是在各大考试还是面试上面都是常考的内容之一,快速排序的平均复杂度为o(nlogn),当所选序列基本上呈现有序时达到最坏的复杂度o(n^2)。

快速排序第一步就是选择基准元素,序列中随机选择即可,然后调整序列元素位置,使得基准元素左边元素都小于它,右边都大于它,如下图所示:

                                        

如上图所示序列,以示方便选择第一个元素作为基准并取出,随后定义左右下标,若right指向元素大于基准元素,元素不懂,right--,若小于基准元素,则交换left right元素,并将比较对象换成left指向元素,同理按照right指针工作,最后right、left指向同一元素,再将基准元素放回即完成一次排序,快速排序则是在这次排序基础上,以这次基准元素为界分为左右两个区间,然后两个区间进行同样的排序过程,以此类推,最后形成有序的序列。实现代码如下:

int partion(int a[],int left,int right)
{
    int p=(round(1.0*rand()/RAND_MAX)*(right-left)+left);  //生成随机基准数下标
    swap(a[p],a[left]);
    int k=a[left];
    while(left<right)
    {
        while(left<right&&a[right]>k)
            right--;
        a[left]=a[right]
        while(left<right&&a[left]<=k)
            left++;
        a[right]=a[left];
    }
    a[left]=k;   //基准元素放回
    return left;
}

void quicksort(int a[],int left,int right)
{
    if(left<right)
    {
        int pp=partion(a,left,right);
        quicksort(a,left,pp-1);
        quicksort(a,pp+1,right);
    }
}

 

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页