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