线性结构之最接近数及移动窗口
目录:
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