注:所有代码都加入了文件读写
实验一
k-smallest element problem
减治法思想
package exp1;
import java.io.*;
import java.util.Scanner;
public class K_smallest {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
//打开读入文件
File f_in = new File("K_smallest_in.txt");
Scanner sc=new Scanner(new FileReader(f_in));
//打开输出文件
File f_out = new File("K_smallest_out.txt");
FileWriter f_w=new FileWriter(f_out);
//读出数据
while(sc.hasNext()) {
int n=sc.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++)
a[i]=sc.nextInt();
int k=sc.nextInt();
int ans=Find_k_small(a,0,n-1,k);
//写入数据
if(ans==-1)
f_w.write("can't find!\n");
//System.out.println("can't find!");
else
f_w.write("the "+k+"-smallest element is:"+ans+"\n");
//System.out.println("the "+k+"-smallest element is:"+ans);
}
//关闭输入输出流
sc.close();
f_w.close();
}
static int Find_k_small(int[] a,int l,int r,int k) {
int n=r-l+1;
if(k>n)
return -1;
if(l==r)
return a[l];
else {
//5个一组,每组的中位数组成数组m,将数组m的中位数作为partition的pivot
int n1=n%5==0?n/5:n/5+1;//数组m的长度
int[] m=new int[n1];
int i;
for(i=0;i<n1-1;i++) {
m[i]=find_medium(a,l+i*5,l+i*5+4);
}
m[n1-1]=find_medium(a,l+i*5,n-1);
int pivot=Find_k_small(m,0,n1-1,n1/2);
i=Partition(a,l,r,pivot);
if(i-l+1>=k)
return Find_k_small(a,l,i,k);
else
return Find_k_small(a,i+1,r,k-(i-l+1));
}
}
static int Partition(int[] a,int l,int r,int pivot) {
int temp=pivot;
for(int i=l;i<=r;i++)
if(a[i]==pivot)
a[i]=a[l];
while(l!=r) {
while(l!=r&&a[r]>=pivot)
r--;
if(l!=r&&a[r]<pivot) {
a[l]=a[r];
l++;
}
while(l!=r&&a[l]<=pivot)
l++;
if(l!=r&&a[l]>pivot) {
a[r]=a[l];
r--;
}
}
a[l]=pivot;
return l;
}
static int find_medium(int[] a,int l,int r) {
if((r-l+1)<=2)
return a[l];
else {
//建辅助数组排序
int[] b=new int[r-l+1];
//copy
for(int i=0,j=l;j<=r;i++,j++)
b[i]=a[j];
//sort
for(int i=0;i<b.length;i++) {
for(int j=0;j<b.length-i-1;j++) {
if(b[j]>b[j+1]) {
int temp=b[j+1];
b[j+1]=b[j];
b[j]=temp;
}
}
}
return b[(r-l+1)/2];
}
}
}
最近点对问题
分治法思想
/*
* 最近点对距离算法描述:
* 1.如果只有一个点,返回无穷;如果有两个点,返回两点距离;否则均分为两部分,求出min_dist1、min_dist2
* 2.合并两部分的点,并按y坐标排序
* 3.记录距中线距离小于等于min(min_dist1,min_dist2)的点,将每个点与其5个基准点进行距离计算,并更新min_dist
*/
package exp1;
import java.util.Scanner;
import java.io.*;
class Point{
int x;
int y;
}
public class closestPair {
static Point[] point;
public static void main(String[] args) throws IOException {
File f_in=new File("closest_pair_in.txt");
Scanner sc=new Scanner(new FileReader(f_in));
File f_out=new File("closest_pair_out.txt");
FileWriter f_w=new FileWriter(f_out);
while(sc.hasNext()) {
int n=sc.nextInt();
point=new Point[n];
for(int i=0;i<n;i++) {
point[i]=new Point();
point[i].x=sc.nextInt();
point[i].y=sc.nextInt();
}
//按横坐标排序
Point temp=new Point();
for(int i=0;i<n;i++) {
for(int j=0;j<n-i-1;j++) {
if(point[j].x>point[j+1].x) {
temp=point[j];
point[j]=point[j+1];
point[j+1]=temp;
}
}
}
Point[] closest_pair=new Point[2];
double min_dist=closest_pair_dist(0,n-1,closest_pair);
String min_dist_s=Double.toString(min_dist);
f_w.write(min_dist_s+"\n");
f_w.write("("+closest_pair[0].x+","+closest_pair[0].y+")"+
" ("+closest_pair[1].x+","+closest_pair[1].y+")"+"\n");
}
sc.close();
f_w.close();
}
//定义计算距离函数
static double dis(Point p1, Point p2)
{
return Math.sqrt(Math.pow((p1.x - p2.x), 2) + Math.pow((p1.y - p2.y), 2));
}
//对点进行纵坐标排序
static void Merge(int left, int mid, int right)
{
//首先完成两侧点的深复制,
int n1 = mid - left + 1; //左侧点集大小
int n2 = right - mid; //右侧点集大小
Point[] L = new Point[n1];
Point[] R = new Point[n2];
//完成两侧点集的复制
for (int i = 0; i < n1; i++)
L[i] = point[left + i];
for (int i = 0; i < n2; i++)
R[i] = point[mid + 1 + i];
//由于整个算法利用分治法,可以利用类似归并排序完成点对的纵坐标排序
int i = 0, j = 0, k;
for (k = left; i < n1 && j < n2; k++)
{
if (L[i].y < R[j].y)
point[k] = L[i++];
else
point[k] = R[j++];
}
//如果归并时两侧数组存在剩余元素,则对剩余元素进行合并
while (i < n1)
point[k++] = L[i++];
while (j < n2)
point[k++] = R[j++];
}
//求最近点对距离函数
static double closest_pair_dist(int left,int right,Point[] closest_pair) {
double min_dist;
//如果只有一个点则返回无穷
if (left == right)
{
return Integer.MAX_VALUE;
}
//如果有两个点则直接进行合并,并对y进行排序 ???为什么要排序??? ***因为归并排序在每一个小块进行排序,最后再合并
else if (left + 1 == right)
{
if(point[left].y>point[right].y) {
Point temp=new Point();
temp=point[left];
point[left]=point[right];
point[right]=temp;
}
closest_pair[0]=point[left];
closest_pair[1]=point[right];
return dis(point[left], point[right]);
}else {
//如果点数大于2
//缩小问题规模实现“分而治之”
int mid = (right + left) >> 1; //通过位操作快捷获得中点
int mid_x=(point[mid].x+point[mid+1].x)/2;//划分线坐标
double min_dist_left=closest_pair_dist(left, mid,closest_pair);
//暂存左半部分的最近点对
Point[] temp_pair1=new Point[2];
temp_pair1[0]=closest_pair[0];
temp_pair1[1]=closest_pair[1];
double min_dist_right=closest_pair_dist(mid+1, right,closest_pair);
//暂存右半部分的最近点对
Point[] temp_pair2=new Point[2];
temp_pair2[0]=closest_pair[0];
temp_pair2[1]=closest_pair[1];
if(min_dist_left<=min_dist_right) {
min_dist=min_dist_left;
closest_pair[0]=temp_pair1[0];
closest_pair[1]=temp_pair1[1];
}
else {
min_dist=min_dist_right;
closest_pair[0]=temp_pair2[0];
closest_pair[1]=temp_pair2[1];
}
Merge(left, mid, right); //以y进行排序
// 使用temp数组记录跨线异侧点,并进行6个边界点的判断
Point[] temp = new Point[right - left + 1];
int i_size = 0;
for (int i = left; i <= right; i++)
{
//如果点在划分线异侧且在最小距离内则记录入temp数组
if (point[i].x > mid_x - min_dist && point[i].x < mid_x+ min_dist)
temp[i_size++] = point[i];
}
for (int i = 0; i < i_size; i++)
{
for (int j = i + 1; j < i_size && j < i + 6; j++)
{
if ((temp[j].y - temp[i].y) > min_dist)
//如果两个点的纵坐标之差大于当前最小值,则一定不符合条件,直接进行下一组判断即可
break;
if (dis(temp[i], temp[j]) < min_dist)
{
//如果当前点距离小于等于当前最小值,则对最小距离进行更新
min_dist = dis(temp[i], temp[j]);
closest_pair[0]=temp[i];
closest_pair[1]=temp[j];
}
}
}
}
return min_dist;
}
}
代码和图片参考该文章 点此访问
FFT快速傅里叶变换
减治法思想
/*FFT 快速傅里叶变换
将系数表示转换为点值表示
1、点值表示求多项式乘积更快,O(n)
2、偶函数时,找正负对,只用求一半的点
3、引入复数,1的根号n能使每次递归后都有正负对
4、将原函数P(X)分为两个函数Pe(X)和P0(X)
另外:
系数个数n必须为2的整数次幂
*/
#include<iostream>
#include<complex>
#include<vector>
#include<fstream>
#define cp complex<double>
#define pi 3.14
using namespace std;
void fft(vector<cp> &a, int l, int r);
int main() {
int n;//多项式项数
ifstream ifs;
ifs.open("FFT_in.txt");
if (!ifs.is_open()) {
cout << "Error opening file"; exit(1);
}
ifs >> n;
vector<cp> a(n);
//读入系数列
for (int i = 0; i < n; i++) {
int real;//实部
ifs >> real;
a[i].real(real);
}
ifs.close();
fft(a, 0, n - 1);
ofstream ofs;
ofs.open("FFT_out.txt");
for (int i = 0; i < n; i++) {
cout << a[i] << endl;
ofs<< a[i] << endl;
}
ofs.close();
}
void fft(vector<cp> &a, int l, int r)
{
int n = r - l + 1;
if (n == 1) return;//递归出口
int mid = n / 2;
vector<cp> b(n);
for (int i = 0; i < mid; i++) {
b[i] = a[l + i * 2];
b[i + mid] = a[l + i * 2 + 1];
}
//前半段是Pe系数,后半段是P0系数
for (int i = 0; i < n; i++)
a[l + i] = b[i];
//分治
fft(a, l, l + mid - 1);
fft(a, l + mid, r);
for(int i= 0;i< mid ;i++)
{
cp x(cos(2 * pi * i / n), sin(2 * pi * i / n));
b[i] = a[i+l] + x * a[l+i + mid];//P[i]=ye[i]+wj*y0[i]
b[i + mid] = a[i+l] - x * a[l+i + mid];//P[i+n/2]=ye[i]-wj*y0[i];
}
for (int i = 0;i<n;i++)
a[i+l] = b[i];
}
even 偶数 , odd 奇数
实验二
求最短路径的FLOYD算法
不断增添中转点,更新最短路径
#include<iostream>
#include<fstream>
using namespace std;
const int INF = 99999999;
int main()
{
int e[10][10], n, m, t1, t2, t3;
fstream ifs;
ifs.open("Floyd_in.txt");
if (!ifs.is_open()) {
cout << "Error opening file"; exit(1);
}
ifs >> n >> m; //n表示顶点个数,m表示边的条数
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (i == j)
e[i][j] = 0;
else
e[i][j] = INF;
}
}
for (int i = 1; i <= m; i++)
{
ifs >> t1 >> t2 >> t3;
e[t1][t2] = t3;
}
ifs.close();
//核心代码
for (int k = 1; k <= n; k++)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (e[i][j] > e[i][k] + e[k][j])
e[i][j] = e[i][k] + e[k][j];
}
}
}
ofstream ofs;
ofs.open("Floyd_out.txt");
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
ofs<<e[i][j]<<" ";
}
ofs << endl;
}
ofs.close();
return 0;
}
/*
4 8
1 2 2
1 3 6
1 4 4
2 3 3
3 1 7
3 4 1
4 1 5
4 3 12
*/
字符串的最优比对算法(KMP)
用KMP算法实现查找子串在原串中第一次出现的位置
注意next数组只与目标串相关,与待查找串无关
package exp1;
import java.io.*;
import java.util.Scanner;
public class KMP {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
//打开读入文件
File f_in = new File("KMP_in.txt");
if(!f_in.exists())
f_in.createNewFile();
Scanner scan=new Scanner(new FileReader(f_in));
//打开输出文件
File f_out = new File("KMP_out.txt");
FileWriter f_w=new FileWriter(f_out);
while(scan.hasNext()) {
String haystack=scan.next();
String needle=scan.next();
int ans=strStr(haystack,needle);
f_w.write(ans+"\n");
}
scan.close();
f_w.close();
}
static int strStr(String haystack, String needle) {
// KMP
int n = haystack.length();
int m = needle.length();
if(m == 0)
return 0;
// next数组,pi[j - 1]为当i与j不匹配时j应该指向的位置
int[] pi = new int[m];
// needle内部构建next数组
for(int i = 1, j = 0; i < m; i++){
// 这里当i与j对应的值不相等时,通过next数组将j回滚,与暴力(直接将j = 0)不同
while(j > 0 && needle.charAt(i) != needle.charAt(j))
j = pi[j - 1];
// 这里注意:当i与j匹配时,j应该+1然后赋值给pi[i]
if(needle.charAt(i) == needle.charAt(j))
j++;
// 当i与j不匹配时,直接将j赋值给pi[i]
pi[i] = j;
}
// haystack与needle之间进行判断
for(int i = 0, j = 0; i < n; i++){
while(j > 0 && haystack.charAt(i) != needle.charAt(j)){
j = pi[j - 1];
}
if(haystack.charAt(i) == needle.charAt(j))
j++;
if(j == m)
return i - m + 1;
}
return -1;
}
}
最长公共子序列算法(LCS)
公共子序列(Longest Common Subsequence)指的是字符串中不一定连续但先后顺序一致的n个字符
类比编辑距离(Edit Distance)
编辑距离:min(cost)
cost:the cost of an alignment(比对)is the number of columns in which the letters differ
所以编辑距离(最小不同列个数)的情况就是最长公共子序列的情况
动态规划算法
dp[i][j] = max(dp[i-1][j], dp[i][j-1],dp[i-1][j-1] + (A[i]==B[j] ? 1 : 0))
#include <iostream>
#include<fstream>
using namespace std;
char a[1001], b[1001];
int dp[1001][1001], len1, len2;
/*
A串前x个和B串前y个的LCS(x, y) =
(1) LCS(x - 1, y - 1) + 1 如果A[x] = B[y]
(2) max(LCS(x – 1, y), LCS(x, y – 1)) 如果A[x] ≠ B[y]
(3) 0 如果x = 0或者y = 0
注:字符串下标是从0开始的*/
void lcs(int i, int j)
{
for (i = 1; i <= len1; i++)
{
for (j = 1; j <= len2; j++)
{
if (a[i-1 ] == b[j-1 ])
dp[i][j] = dp[i - 1][j - 1] + 1;
else if (dp[i - 1][j] > dp[i][j - 1])
dp[i][j] = dp[i - 1][j];
else
dp[i][j] = dp[i][j - 1];
}
}
}
void llcs(ofstream ofs)
{
int i, j, z = 0;
char c[1001];
memset(c, 0, sizeof(c));
i = len1, j = len2;
while (i != 0 && j != 0)
{
if (a[i - 1] == b[j - 1])
{
c[z++] = a[--i];
j--;
}
else if (dp[i - 1][j] < dp[i][j - 1])
j--;
else if (dp[i][j - 1] <= dp[i - 1][j])
i--;
}
for (i = z - 1; i >= 0; i--)
ofs<<c[i]<<endl;
}
int main()
{
ifstream ifs;
ifs.open("LCS_in.txt");
if (!ifs.is_open())
cout << "ERROR OPENING FILE" << endl;
ofstream ofs;
ofs.open("LCS_out.txt");
while (ifs >> a) {
ifs >> b;
memset(dp, 0, sizeof(dp));
len1 = strlen(a);
len2 = strlen(b);
lcs(len1, len2);
ofs<<dp[len1][len2]<<endl;
//追溯找出LCS
int i, j, z = 0;
char c[1001];
memset(c, 0, sizeof(c));
i = len1, j = len2;
while (i != 0 && j != 0)
{
if (a[i - 1] == b[j - 1])
{
c[z++] = a[--i];
j--;
}
else if (dp[i - 1][j] < dp[i][j - 1])
j--;
else if (dp[i][j - 1] <= dp[i - 1][j])
i--;
}
for (i = z - 1; i >= 0; i--)
ofs << c[i];
ofs << endl;
}
return 0;
}