《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer

4.6.1什么是two pointers

two pointers的引入:
《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第1张图片

for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
{
	if(a[i]+a[j]==m)
		cout << a[i] << a[j] << endl;
}

此方法效率低下原因:
《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第2张图片
使用two pointers方法:
《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第3张图片
使用two pointers方法的时间复杂度分析:
在这里插入图片描述
使用two pointers序列合并问题:
《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第4张图片

int merge(int A[], int B[], int C[], int n, int m)
{
	int i=0,j=0,index=0;
	while(i<n && j<m)
	{
		if(A[i]<=B[j])
			C[index++]=A[i++];
		if(A[i]>B[j])
			C[index++]=B[j++];
	}
	while(i<n)
		C[index++]=A[i++];
	while(j<m)
		C[index++]=B[j++];
	return index;
}

two pointers的实质是设置两个下标i,j对序列进行扫描(可以同向,也可以反向),时间复杂度一般为O(n)


4.6.2 归并排序:

1.归并排序的引入:

《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第5张图片

2.递归实现:

//将数组A[L1,R1]与[L2,R2]合并为一个有序区间
void merge(int A[], int L1, int R1, int L2, int R2)
{
    int i=L1,j=L2;          //i指向L1,j指向L2
    int temp[maxn],index=0;   //temp为临时数组,index为其下标
    while(i<=R1 && j<=R2)    
    {
        if(A[i]<=A[j])      //如果A[i]<=A[j]
            temp[index++]=A[i++];     //将A[i]放入数组中
        else
            temp[index++]=A[j++];    //否则将A[j]放入数组中
    } 
    while(i<=R1)       //将[L1,R1]剩余的数字放入到数组中
        temp[index++]=A[i++];
    while(j<=R2)         //将[L2,R2]剩余的数字放入数组中
        temp[index]=A[j++];
    for(int i=0; i<index; i++)      //将合并后的临时序列放回序列A中
        A[L1+i]=temp[i];
}

void mergeSort(int A[],int left, int right)
{
    if(left<right)      //只要left小于right 
    {
        int mid=(left+right)/2;    //mid为中间
        mergeSort(A,left,mid);          //将一个序列分开后的左字序列
        mergeSort(A,mid+1,right);   //将一个序列分开后的右字序列
        merge(A,left,mid,mid+1,right);   //将左右子序列整合
    }

}

2.非递归实现:

void merge(int A[])   
{
    //step为组内元素个数,step/2表示组内左字区间个数
    for(int step=2; step/2<=n; step*=2)
    {
        for(int i=1; i<=n; i+=step)    //找出个数为step的每组第一数字的下标
        {
            int mid=i+step/2-1;        //mid设置为每组元素中的中间位置
            if(mid+1<=n)
            {
                merge(A,i,mid,mid+1,min(i+step-1,n));   //将左右区间进行合并,注意最后的那个数的位置
            }
        }
    }
}

3.如果题目中要求给出每一趟归并排序的序列,用sort函数代替merge函数

void mergeSort(int A[])
{
    for(int step=2; step/2<=n; step*=2)
    {
        for(int i=1; i<=n; i+=step)
        {
            sort(A+i,A+min(i+step,n+1));  //注意这里由于sort必须是在数组中要排序的数列最后后一位的下一位,所以相比于之前+1
        }
    }
}

4.6.3 快速排序:

1.快速排序的定义:

《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第6张图片
2.用two powinters的思想构建一个快速排序的例子:
《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第7张图片
《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第8张图片

int Partition(int A[],int left,int right)
{
    int temp=A[left];   //将最左边的值设置为temp
    while(left<right)   //循环一直到left与right相遇
    {
        while(left < right && A[right]>temp)   //A[right]的值比temp大则将right下标--,找到第一个比temp小的值,然后将值放到前面
        {
            right--;
        }
        A[left]=A[right];
        while(left < right && A[left]>temp)  //A[left]的值比temp小,则left++。找到第一个比temp值大的值然后把他放到后面
        {
            left++;
        }
        A[right]=A[left];
    }
    A[left]=temp;  //将temp放到既定位置
    return left;
}

3.真正的快速排序
《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第9张图片

void quickSort(int A[],int left,int right)  //整个递归的思想是:对一组无序的数组,
              //先以第一个数为列,然后把他放到左边的数都比他小,右边的数都比他大,返回其位置,然后进行同样的操作,分别对于左子区间和右子区间
{
    if(left<right)
    {
        int pos=partition(A,left,right);   //partition函数的作用是把要放的数字,左边都比他小,右边都比他大,然后返回他的位置
        quickSort(A,left,pos-1);   
        quickSort(A,pos+1,right);
    }
}

4.快速排序的弊端和解决方法:
《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第10张图片

5.设置随机数

#include
#include
#include
#include
using namespace std;
int main()
{
    srand((unsigned)time(NULL)); //设置随机数字时,不要忘记加随机中子
    for(int i=0; i<10; i++)
        cout << rand() << " ";
}

5.设置指定范围随机数
《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第11张图片

#include
#include
#include
#include
using namespace std;
int main()
{
    srand((unsigned)time(NULL));       //必须设置随机数种子
    for(int i=0; i<10; i++)   //随机输出[0,1]的随机数
        cout << rand()%(2) << " ";
    cout << endl;
    for(int i=0; i<10; i++)   //随机输出[3,7]的随机数
        cout << rand()%(5)+3 << " ";

}

6.设置可以超过RAND_MAX的随机数
《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第12张图片

#include
#include
#include
#include
using namespace std;
int main()
{
    srand((unsigned)time(NULL));
    for(int i=0; i<10; i++)
    {
        cout << (int)(1.0*rand()/RAND_MAX*50000+10000) << " ";   //设置一个长度为[10000,50000]的随机数
    }
    return 0;
}

7.在A[left…right]的范围内设置一个随机数,将A[p]作为主元进行划分,随机选取一个主元进行划分
《算法笔记》第4章 入门篇(2)---算法初步 4.6 two pointer_第13张图片

int randPartition(int A[],int left,int right)
{
    int p=1.0*rand()/RAND_MAX*(right-left)+left; //在[left,right]的范围内设置一个随机数
    swap(A[p],A[left]);    //将设置随机的数字和A[left]交换
    int temp=A[left];       //将A[left]设置为临时变量
    while(left<right)    //进行调整
    {
        while(left < right && A[right]>temp)
            right--;
        A[left]=A[right];
        while(left < right && A[left]<temp)
            left++;
        A[right]=A[left];
    }
    A[left]=temp;
    return left;   //相遇时,返回下标
}

你可能感兴趣的:(《算法笔记》)