线性结构之最接近数及移动窗口

线性结构之最接近数及移动窗口

目录:

1、需求简单说明:

2、工程目录图:

3、源代码及运行结果:


1、需求简单说明:

最接近数:求一个线性表中每个数之前的所有数中,它最接近的数。

移动窗口:线性表长度为n,设置一个k<=n的窗口,窗口一开始在线性表最左边的位置,能看到下标为从1到k的元素,每次向右移动一个元素,直到窗口的右边界达到线性表的下标为n的元素位置。


2、工程目录图:


图2.1 工程目录图


3、源代码及运行结果:

(1)最接近数:

A)ClosestNumberService.java

package com.remoa.section1.Q2.service;

import com.remoa.section1.Q2.domain.ClosestNumberVO;
import com.remoa.section1.Q2.util.QuickSort;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CountDownLatch;

/**
 * 表中每个数之前的所有数中,它最接近的数
 * @author Remoa
 * @since 2017/12/20
 */
public class ClosestNumberService extends Thread {
    private static final int MAX_VALUE = 10000000;
    private int method;
    private List<Integer> list;
    private String path;
    private String filename;
    private CountDownLatch countDownLatch;

    public ClosestNumberService(int method, List<Integer> list, String path, String filename, CountDownLatch countDownLatch) {
        this.method = method;
        this.list = list;
        this.path = path;
        this.filename = filename;
        this.countDownLatch = countDownLatch;
    }

    public static List<Integer> initNumber(int number) {
        Random random = new Random();
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < number; i++) {
            int randomNumber = random.nextInt(10000);
            list.add(randomNumber);
        }
        return list;
    }

    /**
     * 暴力法
     *
     * @param list 测试样例数据集
     * @return list 返回结果集
     */
    public List<ClosestNumberVO> method1(List<Integer> list) {
        List<ClosestNumberVO> resultList = new ArrayList<>();
        if (list.size() <= 1) {
            System.out.println("size less than two, no answer!");
            return null;
        }
        ClosestNumberVO firstVO = new ClosestNumberVO();
        firstVO.setIndex(0);
        firstVO.setClosestIndex(-1);
        resultList.add(firstVO);
        for (int i = 1; i < list.size(); i++) {
            int minValue = Math.abs(list.get(i) - list.get(0));
            int minIndex = 0;
            for (int j = 0; j < i; j++) {
                if (Math.abs(list.get(i) - list.get(j)) <= minValue) {
                    minValue = Math.abs(list.get(i) - list.get(j));
                    minIndex = j;
                }
            }
            ClosestNumberVO closestNumberVO = new ClosestNumberVO();
            closestNumberVO.setClosestIndex(minIndex);
            closestNumberVO.setIndex(i);
            resultList.add(closestNumberVO);
        }
        return resultList;
    }

    /**
     * 使用类似双向链表的思想进行查找,但这方法也很猪,因为用了Map来模拟链表,删Map时,Map的前后元素并没有联系,相当于暴力判定空
     *
     * @param list 测试样例数据集
     * @return list 返回结果集
     */
    public List<ClosestNumberVO> method2(List<Integer> list) {
        List<ClosestNumberVO> resultList = new LinkedList<>();
        for (int i = 0; i < list.size(); i++) {
            ClosestNumberVO closestNumberVO = new ClosestNumberVO();
            closestNumberVO.setIndex(i);
            resultList.add(closestNumberVO);
        }
        //对无序序列进行排序
        QuickSort quickSort = new QuickSort();
        //resultList是排序后的结果,排序后的结果将按照值的升序,ClosestNumberVO存放的是索引值
        resultList = quickSort.startAlgorithm(resultList, list);
        //Map存放的键值对分别为:map<key : index位置, value : 元素在有序列表中位置>
        Map<Integer, Integer> map = new HashMap<>();
        //将Map的键值对翻转存储为convertMap,键值对分别为:convertMap<key : 元素在有序列表中位置. value : index位置>
        Map<Integer, Integer> convertMap = new HashMap<>();
        for (int i = 0; i < resultList.size(); i++) {
            map.put(resultList.get(i).getIndex(), i);
            convertMap.put(i, resultList.get(i).getIndex());
        }
        //对线性表中的数组从尾部的元素到头部进行遍历,每次遍历后删除有序线线性表中该尾部的元素,保证双向链表中剩下的元素就是遍历的现元素之前的元素
        for (int i = map.size() - 1; i > 0; i--) {
            //获得尾部元素所在有序列表中的位置
            int loca = map.get(i);
            /*
                rightValue:在有序列表中该元素右边且位于现有元素之前的最接近值的索引
                leftValue:在有序列表中该元素左边且位于现有元素之前的最接近值的索引
                finalValue:在比对rightValue及leftValue后的到的最接近值的最终结果
             */
            int rightValue = MAX_VALUE, leftValue = MAX_VALUE, finalResult;
            //遍历左部数据,注意当该数左部木有数据时不要再遍历,loca - 1 >= 0:保证向左遍历时的范围
            while (loca - 1 >= 0) {
                if (map.get(convertMap.get(loca - 1)) != null && convertMap.get(loca - 1) <= i) {
                    leftValue = convertMap.get(loca - 1);
                    break;
                } else {
                    loca--;
                }
            }
            //将loca的值复原到枢纽位置
            loca = map.get(i);
            //遍历右部数据,注意当该数右部木有数据时不要再遍历,loca + 1 <= list.size() - 1,保证向右遍历的范围
            while (loca < list.size() - 1) {
                if (map.get(convertMap.get(loca + 1)) != null && convertMap.get(loca + 1) <= i) {
                    rightValue = convertMap.get(loca + 1);
                    break;
                } else {
                    loca++;
                }
            }
            if (leftValue == MAX_VALUE) {
                finalResult = rightValue;
            } else if (rightValue == MAX_VALUE) {
                finalResult = leftValue;
            } else {
                int result1 = Math.abs(list.get(leftValue) - list.get(i));
                int result2 = Math.abs(list.get(rightValue) - list.get(i));
                finalResult = result1 > result2 ? rightValue : leftValue;
            }
            //对i的最近索引赋值
            resultList.get(i).setClosestIndex(finalResult);
            //删除map中该键值对
            map.remove(i);
        }
        resultList.get(0).setClosestIndex(-1);
        return resultList;
    }

    /**
     * 使用LinkedList模拟测试
     *
     * @param list 测试样例数据集
     * @return list 返回结果集
     */
    public List<ClosestNumberVO> method3(List<Integer> list) {
        List<ClosestNumberVO> resultList = new LinkedList<>();
        //Map存放的键值对分别为:map<key : 源数据的值, value : 元素在源列表中位置>
        for (int i = 0; i < list.size(); i++) {
            ClosestNumberVO closestNumberVO = new ClosestNumberVO();
            closestNumberVO.setIndex(i);
            resultList.add(closestNumberVO);
        }
        //对无序序列进行排序
        QuickSort quickSort = new QuickSort();
        //resultList是排序后的结果,排序后的结果将按照值的升序,ClosestNumberVO存放的是索引值
        resultList = quickSort.startAlgorithm(resultList, list);
        //存放排好序的index值
        List<Integer> integerList = new LinkedList<>();
        for (int i = 0; i < resultList.size(); i++) {
            integerList.add(resultList.get(i).getIndex());
        }
        int count = integerList.size() - 1;
        while(count != 1){
            for (int i = 0; i <= integerList.size() - 1; i++) {
                //找到原序列中尾部的元素
                if(integerList.get(i) == count){
                    int rightValue = MAX_VALUE, leftValue = MAX_VALUE, finalResult;
                    //往前找
                    for(int j = i - 1; j >= 0; j--){
                        if(integerList.get(j) < count){
                            leftValue = j;
                            break;
                        }
                    }
                    //往后找
                    for(int j = i + 1; j <= integerList.size() - 1; j++){
                        if(integerList.get(j) < count){
                            rightValue = j;
                            break;
                        }
                    }
                    //对比选接近
                    if (leftValue == MAX_VALUE) {
                        finalResult = integerList.get(rightValue);
                    } else if (rightValue == MAX_VALUE) {
                        finalResult = integerList.get(leftValue);
                    } else {
                        int result1 = Math.abs(list.get(integerList.get(leftValue)) - list.get(count));
                        int result2 = Math.abs(list.get(integerList.get(rightValue)) - list.get(count));
                        finalResult = result1 > result2 ? integerList.get(rightValue) : integerList.get(leftValue);
                    }
                    //对i的最近索引赋值
                    resultList.get(count).setClosestIndex(finalResult);
                    //从integerList中删除该值
                    integerList.remove(i);
                    count--;
                    break;
                }
            }
        }
        resultList.get(0).setClosestIndex(-1);
        return resultList;
    }

    public void writetoFile(String path, String methodname, List<ClosestNumberVO> resultList, List<Integer> list) {
        try {
            if (!(path.endsWith("\\") || path.endsWith("/"))) {
                path = path + "/";
            }
            File file = new File(path + methodname + ".txt");
            if (!file.exists()) {
                file.createNewFile();
            }
            OutputStream outputStream = new FileOutputStream(file);
            outputStream.write(("In all number before the number " + list.get(0) + ", the closest number is nothing.\r\n").getBytes());
            for (int i = 0; i < resultList.size(); i++) {
                if (resultList.get(i).getClosestIndex() != -1) {
                    String result = "In all number before the number " + list.get(i) + ", the closest number is :" + list.get(resultList.get(i).getClosestIndex());
                    byte[] bytes = (result + "\r\n").getBytes();
                    outputStream.write(bytes);
                }
            }
            outputStream.close();
        } catch (IOException e) {
            System.out.println(methodname + "写出文件过程抛出异常");
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        List<ClosestNumberVO> resultList = new ArrayList<>();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if (method == 1) {
            System.out.println("method1, start time:" + simpleDateFormat.format(new Date()));
            resultList = method1(list);
            System.out.println("method1, end time:" + simpleDateFormat.format(new Date()));
        } else if (method == 2) {
            System.out.println("method2, start time:" + simpleDateFormat.format(new Date()));
            resultList = method2(list);
            System.out.println("method2, end time:" + simpleDateFormat.format(new Date()));
        }else if (method == 3) {
            System.out.println("method3, start time:" + simpleDateFormat.format(new Date()));
            resultList = method3(list);
            System.out.println("method3, end time:" + simpleDateFormat.format(new Date()));
        }
        writetoFile(path, filename, resultList, list);
        countDownLatch.countDown();
    }

}
B)ClosestNumberVO.java

package com.remoa.section1.Q2.domain;

/**
 * 将每个数封装为实体
 * @author Remoa
 * @since 2017/12/20
 */
public class ClosestNumberVO {
    private int index;//该数字的索引
    private int closestIndex;//该数字的最接近数的索引

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public int getClosestIndex() {
        return closestIndex;
    }

    public void setClosestIndex(int closestIndex) {
        this.closestIndex = closestIndex;
    }

}
C)QuickSort.java

package com.remoa.section1.Q2.util;

import com.remoa.section1.Q2.domain.ClosestNumberVO;

import java.util.LinkedList;
import java.util.List;

/**
 * 快排算法工具类
 * @author Remoa
 * @since 2017/12/20
 */
public class QuickSort{
    public List<ClosestNumberVO> startAlgorithm(List<ClosestNumberVO> closestNumberVOS, List<Integer> list) {
        QuickSortVO quickSortVO = new QuickSortVO();
        List<ClosestNumberVO> newList = new LinkedList<>();
        for (int i = 0; i < closestNumberVOS.size(); i++) {
            ClosestNumberVO closestNumberVO = new ClosestNumberVO();
            closestNumberVO.setIndex(i);
            newList.add(closestNumberVO);
        }
        quickSortVO.setList(newList);
        quickSortVO.setPivotloca(0);
        quickSortVO = quicksort(quickSortVO, 0, list.size() - 1, list);
        return quickSortVO.getList();
    }

    /**
     * 对序列进行划分
     * @param quickSortVO quickSortVO实体
     * @param low 位标low指向待排记录的第一个记录
     * @param high 位标high指向最后一个记录
     * @return QuickSortVO实体
     */
    public QuickSortVO partition(QuickSortVO quickSortVO, int low, int high, List<Integer> list){
        int pivlot = quickSortVO.getList().get(low).getIndex();
        ClosestNumberVO pivlotVO = quickSortVO.getList().get(low);
        while(low < high){
            while(low < high && list.get(quickSortVO.getList().get(high).getIndex()) >= list.get(pivlot)){
                high--;
            }
            quickSortVO.getList().set(low, quickSortVO.getList().get(high));
            while(low < high && list.get(quickSortVO.getList().get(low).getIndex()) <= list.get(pivlot)){
                low++;
            }
            quickSortVO.getList().set(high, quickSortVO.getList().get(low));
        }
        quickSortVO.getList().set(low, pivlotVO);
        quickSortVO.setPivotloca(low);
        return quickSortVO;
    }

    /**
     * 快速排序算法
     * @param quickSortVO quickSortVO实体
     * @param s 位标s指向待排记录的第一个记录
     * @param t 位标t指向最后一个记录
     * @return QuickSortVO实体
     */
    public QuickSortVO quicksort(QuickSortVO quickSortVO, int s, int t, List<Integer> list){
        if(s < t){
            quickSortVO = partition(quickSortVO, s, t, list);
            int temp = quickSortVO.getPivotloca();
            quickSortVO = quicksort(quickSortVO, s, temp - 1, list);
            quickSortVO = quicksort(quickSortVO, temp + 1, t, list);
        }
        return quickSortVO;
    }

}

/**
 * QuickSortVO实体,封装了待排序列和枢纽位置
 * @author Remoa
 *
 */
class QuickSortVO{
    private List<ClosestNumberVO> list;
    private int pivotloca;//枢纽位置
    public List<ClosestNumberVO> getList() {
        return list;
    }
    public void setList(List<ClosestNumberVO> list) {
        this.list = list;
    }
    public int getPivotloca() {
        return pivotloca;
    }
    public void setPivotloca(int pivotloca) {
        this.pivotloca = pivotloca;
    }

}
D)ClosestNumberAction.java

package com.remoa.section1.Q2.action;

import com.remoa.section1.Q2.service.ClosestNumberService;

import java.util.List;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;

/**
 * 最接近数程序入口
 * 测试样例:
 *      100
 *      C:\Users\Administrator\Desktop
 * @author Remoa
 * @since 2017/12/20
 */
public class ClosestNumberAction {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String flag = "0";
        String input = "";
        while (!flag.equals(input)){
            try {
                System.out.print("Please input the number for test:");
                String number = scanner.next();
                System.out.println("Please input the file output path:");
                String path = scanner.next();
                if(Integer.valueOf(number) <= 0) {
                    throw new IllegalArgumentException();
                }else{
                    CountDownLatch countDownLatch = new CountDownLatch(3);
                    List<Integer> list = ClosestNumberService.initNumber(Integer.valueOf(number));
                    new ClosestNumberService(1, list, path, "method1", countDownLatch).start();
                    new ClosestNumberService(2, list, path, "method2", countDownLatch).start();
                    new ClosestNumberService(3, list, path, "method3", countDownLatch).start();
                    countDownLatch.await();
                }
                System.out.print("Input '0' to exit, input others will continue testing:");
                input = scanner.next();
            }catch (IllegalArgumentException e){
                System.out.println("input error, please input again!");
            }catch (Exception ex){
                System.out.println("input error, please input again!");
            }
        }
    }

}
运行结果:


图3.1 运行结果生成文件截图1


图3.2 运行结果控制台截图1

(2)移动窗口:

A)MovedWindowService.java

package com.remoa.section1.Q1.service;

import com.remoa.section1.Q1.domain.MovedandWindowVO;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CountDownLatch;

/**
 * 表中“移动窗口”求最小值
 * @author Remoa
 * @since 2017/12/20
 */
public class MovedWindowService extends Thread{

    private int method;
    private int divided;
    private List<Integer> list;
    private String path;
    private String filename;
    private CountDownLatch countDownLatch;

    public MovedWindowService(int method, int divided, List<Integer> list, String path, String filename, CountDownLatch countDownLatch){
        this.divided = divided;
        this.method = method;
        this.list = list;
        this.path = path;
        this.filename = filename;
        this.countDownLatch = countDownLatch;
    }

    public static List<Integer> initNumber(int number){
        Random random = new Random();
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < number; i++) {
            int randomNumber = random.nextInt(10000);
            list.add(randomNumber);
        }
        return list;
    }

    /**
     * 暴力法
     * @param list 测试样例数据集
     * @param divided 移动窗口容量
     * @return list 移动窗口实体集
     */
    public List<MovedandWindowVO> method1(List<Integer> list, int divided){
        List<MovedandWindowVO> resultList = new ArrayList<>();
        int i, m;
        for (int k = 0; k < list.size() - divided + 1; k++) {
            MovedandWindowVO movedandWindowVO = new MovedandWindowVO();
            List<Integer> subList = new ArrayList();
            for (i = k, m = 0; m < divided; i++, m++) {
                subList.add(list.get(i));
            }
            int minValue = subList.get(0);
            for (int j = 1; j < subList.size(); j++) {
                if(minValue > subList.get(j)){
                    minValue = subList.get(j);
                }
            }
            movedandWindowVO.setList(subList);
            movedandWindowVO.setMinValue(minValue);
            resultList.add(movedandWindowVO);
        }
        return resultList;
    }

    /**
     * 类双向队列优化法
     * @param list 测试样例数据集
     * @param divided 移动窗口容量
     * @return list 移动窗口实体集
     */
    public List<MovedandWindowVO> method2(List<Integer> list, int divided){
        List<MovedandWindowVO> resultList = new LinkedList<>();
        //双向队列存放list下标索引
        Deque<Integer> deque = new LinkedList<>();
        for (int i = 0; i < divided; i++) {
            //保证双向队列的尾部的元素值最小
            while(deque.size() != 0 && list.get(i) <= list.get(deque.getLast())){
                deque.removeLast();
            }
            deque.add(i);
        }
        int i;
        for (i = divided; i < list.size(); i++) {
            MovedandWindowVO movedandWindowVO = new MovedandWindowVO();
            movedandWindowVO.setList(list.subList(i - divided, i));
            movedandWindowVO.setMinValue(list.get(deque.getFirst()));
            resultList.add(movedandWindowVO);
            while(deque.size() != 0 && list.get(i) <= list.get(deque.getLast())){
                deque.removeLast();
            }
            if(deque.size() != 0 && deque.getFirst() <= i - divided){
                deque.removeFirst();
            }
            deque.addLast(i);
        }
        MovedandWindowVO movedandWindowVO = new MovedandWindowVO();
        movedandWindowVO.setList(list.subList(list.size() - divided, list.size()));
        movedandWindowVO.setMinValue(list.get(deque.getFirst()));
        resultList.add(movedandWindowVO);
        return resultList;
    }

    public void writetoFile(String path, String methodname, List<MovedandWindowVO> resultList){
        try {
            if(! (path.endsWith("\\") || path.endsWith("/"))){
                path = path + "/";
            }
            File file = new File(path + methodname + ".txt");
            if(!file.exists()){
                file.createNewFile();
            }
            OutputStream outputStream = new FileOutputStream(file);
            for(Iterator<MovedandWindowVO> iterator = resultList.iterator(); iterator.hasNext(); ){
                String result = "Field = [ ";
                MovedandWindowVO movedandWindowVO = iterator.next();
                for (int i = 0; i < movedandWindowVO.getList().size(); i++) {
                    result = result + movedandWindowVO.getList().get(i).toString() + " ";
                }
                result = result + "], minvalue = " + movedandWindowVO.getMinValue();
                byte[] bytes = (result+"\r\n").getBytes();
                outputStream.write(bytes);
            }
            outputStream.close();
        } catch (IOException e) {
            System.out.println(methodname + "写出文件过程抛出异常");
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        List<MovedandWindowVO> resultList = new ArrayList<>();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(Thread.currentThread().getName() + ", start time:" + simpleDateFormat.format(new Date()));
        if(method == 1){
            resultList = method1(list, divided);
        }else if(method == 2){
            resultList = method2(list, divided);
        }
        System.out.println(Thread.currentThread().getName() + ", end time:" + simpleDateFormat.format(new Date()));
        writetoFile(path, filename, resultList);
        countDownLatch.countDown();
    }

}
B)MovedandWindowVO.java

package com.remoa.section1.Q1.domain;

import java.util.List;

/**
 * 将每个移动窗口封装为实体
 * @author Remoa
 * @since 2017/12/20
 */
public class MovedandWindowVO {
    private List<Integer> list;//当前移动窗口中存储的数封装为一个List
    private int minValue;//当前移动窗口中存储的数中的最小值

    public List<Integer> getList() {
        return list;
    }

    public void setList(List<Integer> list) {
        this.list = list;
    }

    public int getMinValue() {
        return minValue;
    }

    public void setMinValue(int minValue) {
        this.minValue = minValue;
    }

}
C)MovedWindowAction.java

package com.remoa.section1.Q1.action;

import com.remoa.section1.Q1.service.MovedWindowService;

import java.util.List;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;

/**
 * 移动窗口程序入口
 * 测试样例:
 *      100
 *      3
 *      C:\Users\Administrator\Desktop
 * @author Remoa
 * @since 2017/12/20
 */
public class MovedWindowAction {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String flag = "0";
        String input = "";
        while (!flag.equals(input)){
            try {
                System.out.print("Please input the number for test:");
                String number = scanner.next();
                System.out.print("Please input the number of interval:");
                String divided = scanner.next();
                System.out.println("Please input the file output path:");
                String path = scanner.next();
                CountDownLatch countDownLatch = new CountDownLatch(2);
                List<Integer> list = MovedWindowService.initNumber(Integer.valueOf(number));
                new MovedWindowService(1, Integer.valueOf(divided), list, path, "method1", countDownLatch).start();
                new MovedWindowService(2, Integer.valueOf(divided), list, path, "method2", countDownLatch).start();
                countDownLatch.await();
                System.out.print("Input '0' to exit, input others will continue testing:");
                input = scanner.next();
            }catch (Exception e){
                System.out.println("input error, please input again!");
            }
        }
    }

}
运行结果:

图3.3 运行结果生成文件截图2

图3.4 运行结果控制台截图2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值