算法实验~

本文包含一系列算法实验,如使用减治法解决K-最小元素问题,分治法处理最近点对问题,以及运用FFT快速傅里叶变换。此外,还涉及FLOYD算法求最短路径和KMP字符串比对算法以及最长公共子序列算法的实现。所有代码均包含文件读写功能。
摘要由CSDN通过智能技术生成

注:所有代码都加入了文件读写


实验一

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

在这里插入图片描述
参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值