《算法笔记》4.6 two pointers

一.two pointers的两个示例
示例一.求递增序列中两个不同位置的数,使它们的和为一个正整数

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

示例二.序列合并问题
思路:
从两个数中选出一个作为新序列的第 index 个数;
代码:

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 < m) C[index++] = B[j++] ;
	return index;
}

二.归并排序(2-路归并)
思路:
在这里插入图片描述
代码:

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[i++];
	}
	while(i <= R1) temp[index++] = A[i++];
	while(j <= R2) temp[index++] = A[j++];
	for(i = 0; i < index; i++) A[L1 + 1] = temp[i];
}

1.递归实现

void mergeSort(int A[], int left, int right) {
	if(left < right) {
		int mid = (left + right) / 2;
		mergeSort(A, left, mid);
		mergeSort(A, mid + 1, right);
		merge(A, left, mid, mid + 1, right);
	}
}

2.非递归实现

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));
		}
	}
}

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));
		}
	}
}

三.快速排序
1.使左侧元素均小于 A[1],右侧元素均大于 A[1] 的代码
思路:
取出 A[1] (1~n),将两个下标 left,right 分别指向序列首尾,移动,若left 指向的元素大于A[1] 或者 right 指向的元素小于 A[1],则交换 A[left] 和 A[right],直到 left 和 right 相遇;
在这里插入图片描述
代码:

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;
}

2.快速排序

void quickSort(int A[], int left, int right) {
	if(left < right) {
		int pos = Partition(A, left, right);
		quickSort(A, left, pos);
		quickSort(A, pos + 1, right);
	}
}

递归结束条件是当 left = right ,即区间只有一个元素;

3.生成随机数
1).

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main() {
	srand((unsigned)time(NULL));
	printf("%d", rand());
	return 0;
}

生成随机数的范围是 [0, RAND_MAX];

2).生成给定范围内的随机数(范围不超过 [0, RAND_MAX] )

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main() {
	srand((unsigned)time(NULL));
	printf("%d", rand() % 2); // [0,1]
	printf("%d", rand() % 5 + 3); // [3,7]
	return 0;
}

3).生成大范围随机数(范围超过 [0, RAND_MAX] )
用生成的随机数除以 RAND_MAX 得到一个浮点数,再乘以范围长度,此浮点数就是范围内的比例位置;

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main() {
	srand((unsigned)time(NULL));
	printf("%d", (int)(1.0 * rand() / RAND_MAX * 50000 + 10000)); //[10000,60000]
	return 0;
}

4).对快速排序算法的改进
生成一个范围在 [left, right] 内的随机数 p,交换 A[left] 与 A[p] ,以A[p]为第一次划分的主元;

四.题目
1.PAT B1030
思路:
若 A[j] <= A[i] * p,则 A[k] <= A[i] * p (i <= k <= j) ,由这个性质引导我们往 two pointers 思想去考虑;
第一次循环得到了count1,在第二次循环中,若 j 满足 A[j] <= A[i] * p,则可以继续向右走,得到 count2(>=count1),若不满足,则退出此次循环,count 的值还是 count1 的值( j 之前的值肯定满足第二次循环条件,所以 count2 最小值是 count1 - 1);

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int main() {
	int N, p;
	scanf("%d%d", &N, &p);
	int A[N];
	for(int i = 0; i < N; i ++) scanf("%d", &A[i]);
	sort(A, A + N);
	int i = 0, j = 0, count = 1;
	while(i < N && j < N) {
		while(i < N && j < N && A[j] <= (long long)A[i] * p) {
			count = max(count, j - i + 1);
			j++;
		}
		i++;
	}
	printf("%d", count);
	return 0;
}

2.PAT B1035
思路:
思路很简单,把插入排序和归并排序每一次的结果与目标数组比较;

注意:
1).如何输出下一次排序结果:设一个 flag 变量,满足条件后再执行一次排序,再 return true,在主函数中直接输出 tempOri;
2).数组做函数参数,直接写数组名;
3).记得初始化;

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 111;
int origin[maxn], tempOri[maxn], changed[maxn];
int n;
bool isSame(int A[], int B[]) {
	for(int i = 0; i < n; i++) {
		if(A[i] != B[i]) return false;
	}
	return true;
}

void showArray(int A[]) {
	for(int i = 0; i < n; i++) {
		printf("%d",A[i]);
		if(i != n - 1) printf(" ");
	}
	printf("\n");
}

bool insertSort() {
	bool flag = false;
	for(int i = 1; i < n; i ++) {
		if(i != 1 && isSame(tempOri, changed)) flag = true;
		int temp = tempOri[i];
		int j = i;
		while(j > 0 && temp < tempOri[j - 1]) {
			tempOri[j] = tempOri[j - 1];
			j--;
		}
		tempOri[j] = temp;
		if(flag) return true;
	}
	return false;
}

void mergeSort() {
	bool flag = false;
	int step;
	for(step = 2; step / 2 <= n; step *= 2) {
		if(step != 2 && isSame(tempOri, changed)) flag = true;
		for(int i = 0; i < n; i += step) sort(tempOri + i, tempOri + min(step + i, n));
		if(flag == true) {
			showArray(tempOri);
			return;
		}
	}
}
int main() {
	scanf("%d", &n);
	for(int i = 0; i < n; i ++) {
		scanf("%d", &origin[i]);
		tempOri[i] = origin[i];
	}
	for(int i = 0; i < n; i ++) scanf("%d", &changed[i]);
	if(insertSort()) {
		printf("Insertion Sort\n");
		showArray(tempOri);
	}
	else {
		for(int i = 0; i < n; i++) tempOri[i] = origin[i];
		printf("Merge Sort\n");
		mergeSort();
	}
	return 0;
}

3.PAT A1029
思路:
1.count 等于中位数位置时,从 s1[i] 和 s2[j] 中选择一个较小值作为中位数;
2.在两个数组的末尾添加一个 int 类型的最大值 0x7fffffff,防止当一个数组已经扫描完但count还没有到中位数位置而产生的越界;

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int INF = 0x7fffffff;
const int maxn = 1e6 + 10;
int s1[maxn], s2[maxn];
int main() {
	int n, m;
	scanf("%d", &n);
	for(int i = 0; i < n; i ++) scanf("%d", &s1[i]);
	scanf("%d", &m);
	for(int i = 0; i < m; i ++) scanf("%d", &s2[i]);
	s1[n] = s2[m] = INF;
	int i = 0, j = 0, count = 0;
	int median = (n + m - 1) / 2;
	while(count < median) {
		if(s1[i] > s2[j]) j++;
		else i++;
		count++;
	}
	printf("%d",min(s1[i], s2[j]));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值