《算法导论》学习笔记

第4章入门篇(2)——算法初步(6)

4.6 two pointers

4.6.1 什么是 two pointers
two pointers是算法编程中一种编程技巧。two pointers的思想十分简洁,但却提供了非常高的算法效率。
第一个例子:给定一个递增的正整数序列和一个正整数M,求序列中的两个不同位置的数a和b,使它们的和恰好为M,输出所有满足条件的方案。
本题最直接的想法就是使用二重循环:

for(int i=0;i<n;i++){
	for(int j=i+1;j<n;j++){
		if(a[i]+a[j]==M){
			printf("%d %d\n",a[i],a[j]);
		}
	}
}

使用two pointers思想解答:
令下标i的初值为0,下标j的初值为n-1,即令i、j分别指向序列的第一个元素和最后一个元素,接下来根据a[i]+a[j]与M的大小来进行下面三种选择,使i不断向右移动、使j不断向左移动,直到i>=j成立。
①如果满足a[i]+a[j]==M说明找到了其中一组方案。令i=i+1、j=j-1(即令i向右移动,j向左移动)
②如果满足a[i]+a[j]>M。令j=j-1(令j向左移动)
③如果满足a[i]+a[j]<M。令i=i+1(令i向右移动)

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

two pointers的思想充分利用了序列递增的性质,以很浅显的思想降低了复杂度。

第二个问题: 序列合并问题:假设有两个递增序列A与B,要求将它们合并为一个递增序列C。
思路:①若A[i]<B[j],说明A[i]是当前序列A与序列B的剩余元素中最小的那个,因此把A[i]加入序列C中,并让i加1
②若A[i]>B[j],说明B[j]是当前序列A与序列B的剩余元素中最小的那个,因此把B[j]加入序列C中,并让j加1
③若A[i]=B[j],则任意选一个加入到序列C中,并让对应下标加1

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++];
		}else{
			C[index++]=B[j++];
		}
	}
	while(i<n) C[index++]=A[i++];
	while(j<n) C[index++]=A[j++];
	return index;
}

two pointers是利用问题本身与序列的特性,使用两个下标i、j对序列进行扫描,以较低的复杂度解决问题。

4.6.2 归并排序
原理:将序列两两分组,将序列归并为[n/2]个组,组内单独排序;然后将这些组再两两归并,生成[n/4]个组,组内再单独排序。以此类推,直到剩下一个组为止。
核心:如何将两个有序序列合并为一个有序序列
(1)递归实现
只需要反复将当前区间[left,right]分为两半,对两个子区间[left,mid]与[mid+1,right]分别递归进行归并排序,然后将两个已经有序的子区间合并为有序序列即可。

const int maxn=100;
void merge(int A[],int L1,int R1,int L2,int R2){
	int i=L1,j=L2;
	int temp[maxn],index=0;
	while(i<=R1&&j<=R2){
		if(A[i]<=a[j]){
			temp[index++]=A[i++];
		}else{
			temp[index++]=A[j++];
		}
	}
	while(i<=R1) temp[index++]=A[i++];
	while(j<=R2) temp[index++]=A[j++];
	for(i=0;i<index;i++){
		A[L1+i]=temp[i];
	}
}
//递归实现
void mergeSort(int A[],int left,int right){
	if(left<right){
		int mid=(left+right)/2;
		mergeSort(A,left,mid);  //将左子区间[left,mid]归并排序 
		mergeSort(A,mid+1,right);  //将右子区间[mid+1,right]归并排序 
		merge(A,left,mid,mid+1,right);   //将左子区间和右子区间合并 
	}
}

(2)非递归实现
思路:令步长step的初值为2,然后将数组中每step个元素作为一组,将其内部进行排序(即把左step/2个元素与右step/2个元素合并,而若元素个数不超过step/2,则不操作);再令step乘2,重复上面的操作,直到step/2超过元素个数n

//非递归实现一
void mergeSort(int A[]){
	for(int step=2;step/2<=n;step*=2){
		for(int i=1;i<=n;i+=step){
			int mid=i+step/2-1;
			if(mid+1<=n){
				merge(A,i,mid,mid+1,min(i+step-1,n));
			}
		}
	}
}
//非递归实现二 
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));
		}
	}
}

4.6.3 快速排序
首先引入一个问题:对一个序列A[1]、A[2]、……A[n],调整序列中元素的位置,使得A[1]的左侧所有元素都不超过A[1]、右侧所有元素都大于A[1]。
思想就是two pointers

int Partition(int A[],int left,int right){
	int temp=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;
} 

快速排序思路:①调整序列中的元素,使当前序列最左端的元素在调整后满足左侧所有元素均不超过该元素、右侧所有元素均大于该元素
②对该元素的左侧和右侧分别递归进行①的调整,直到当前调整区间的长度不超过1.

void quickSort(int A[],int left,int right){
	if(left<right){
		int pos=Partition(A,left,right);
		quickSort(A,left,pos-1);  //对左子区间递归进行快速排序 
		quickSort(A,pos+1,right);  //对右子区间递归进行快速排序
	}
}

随机选择主元的方法

int randPartition(int A[],int left,int right){
	int p=(round(1.0*rand()/RAND_MAX*(right-left)+left));
	swap(A[p],A[left]);
	int temp=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;
} 

备注:随机函数的使用
随机数据的函数,需要添加stdlib.h头文件与time.h头文件。首先再main函数开头加上srand((unsigned)time(NULL));,这个语句将生成随机数种子。然后,在需要使用随机数的地方使用rand()函数。

#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
int main(){
	srand((unsigned)time(NULL));
	for(int i=0;i<10;i++){
		printf("%d ",rand());
	}
	return 0;
} 

rand()函数只能生成[0,RAND_MAX]范围内的整数,如果想要输出给定范围[a,b]内的随机数,需要使用rand()%(b-a+1)+a。

#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
int main(){
	srand((unsigned)time(NULL));
	for(int i=0;i<10;i++){
		printf("%d ",rand()%2); //[0,1] 
	}
	printf("\n");
	for(int i=0;i<10;i++){
		printf("%d ",rand()%5+3); //[3,7] 
	}
	return 0;
} 
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
int main(){
	srand((unsigned)time(NULL));
	for(int i=0;i<10;i++){
		printf("%d ",(int)(round(1.0*rand()/RAND_MAX*50000+10000)); //[10000,60000] 
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
计算机科学导论是计算机科学专业的入门课程,它涵盖了计算机科学及其相关领域的基本概念、基本理论和基本技术。以下是一些学习笔记,希望对您有所帮助: 1. 计算机科学的定义: 计算机科学是研究计算机及其在信息处理中的应用的学科,它涉及计算机硬件、软件、算法、数据结构、数据、网络、人工智能等方面的知识。 2. 计算机科学的历史: 计算机科学的发展经历了多个阶段,从最初的机械计算器到现代的超级计算机,计算机科学的发展一直在推动着人类社会的进步。 3. 计算机科学的基本概念: 计算机科学的基本概念包括:二进制、位、字节、字符、编码、算法、数据结构、程序、操作系统、编译器等。 4. 计算机科学的基本理论: 计算机科学的基本理论包括:图灵机、计算复杂性理论、自动机理论、信息论等。 5. 计算机科学的基本技术: 计算机科学的基本技术包括:计算机网络、数据、人工智能、图形学、软件工程、计算机安全等。 6. 计算机科学的研究方法: 计算机科学的研究方法包括:实验研究、理论研究、模拟研究、仿真研究等。 7. 计算机科学的应用领域: 计算机科学的应用领域包括:信息技术、通信技术、金融、医疗、教育、交通、娱乐等。 以上是一些计算机科学导论的学习笔记,希望对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值