实验II: 检索算法的实现
本次实验拟解决生活中最常见的问题之一:检索问题(查找问题),该问题要求在一个列表中查找某个具体元素是否出现,若出现,返回具体元素在数组中的位置,否则返回-1。根据列表中元素的相对大小信息,有顺序检索、二分检索、三分检索等算法思路可供参考使用,本次实验需要学生根据所给列表的性质采取有效算法解决相关问题,并能分析各个算法所使用的算法设计技术和时间复杂度。下列基本要求必须完成:
- 设计一个交互界面(例如菜单)供用户选择,如果可能,最好是一个图形化用户界面;
- 能够人工输入或随机产生一个长度为n的整数数组,要求数组任意两个元素都互不相同;
- 设计一个算法判断要求2中产生的整数数组是否为或未排序(输出0)、升序(输出1)、降序(输出2)、先升后降(输出3)、或先降后升(输出4)状态;
- 给定某具体元素,使用顺序检索算法判断该具体元素是否出现在要求2中产生的数组中,并统计关键字比较的次数;
- 给定某具体元素,使用二分检索算法判断该具体元素是否出现在要求2中产生的升序或降序的数组中,并统计关键字比较的次数;
- 给定某具体元素,使用三分检索算法判断该具体元素是否出现在要求2中产生的升序或降序的数组中,并统计关键字比较的次数;
- 给定先升后降(或先降后升)数组,使用二分检索思路查找该数组的最大值(或最小值),并统计关键字比较的次数。
附加:给定某具体元素,使用二分检索算法判断该具体元素是否出现在要求2中产生的升序或降序的数组中,若出现,返回具体元素所在的位置,否则,返回小于最接近该具体元素的位置。
searchSuanfa.java
package com;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
public class searchSuanfa {
private static int compareCount=0;
//选择生成数组算法
public static int[] ChooseGenerateArray() {
int i = Integer.parseInt(JOptionPane.showInputDialog("选择生成数组方式(自动1 人工2 升序3 降序4"));
int[] result = null;
switch (i) {
case 1:
result = generateArray();
break;
case 2:
result = renGongArray();
break;
case 3:
result = generateRandomSortedArray(true);
break;
case 4:
result = generateRandomSortedArray(false);
break;
}
return result;
}
//生成数组
public static int[] generateArray() {
int n = Integer.parseInt(JOptionPane.showInputDialog("输入数组的长度:"));
int[] arr = new int[n];
Random rand = new Random();
Set<Integer> set = new HashSet<>();
for (int i = 0; i < n; i++) {
int num;
do {
num = rand.nextInt(2 * n); // 产生0到2n之间的随机数
} while (set.contains(num)); // 检查是否已经存在该数
arr[i] = num;
set.add(num);
}
System.out.println("生成数组:");
for (int value : arr) {
System.out.print(value + " ");
}
System.out.println("\n");
return arr;
}
//人工生成数组
public static int[] renGongArray() {
int length = Integer.parseInt(JOptionPane.showInputDialog("输入数组的长度:"));
JTextArea textArea = new JTextArea(length, 1);
JOptionPane.showMessageDialog(null, textArea, "请输入数组元素(每个元素一空格):", JOptionPane.INFORMATION_MESSAGE);
String[] lines = textArea.getText().split("\\s+");//使用split("\\s+")方法根据一个或多个连续的空格来拆分字符串
int[] array = new int[length];
System.out.println("生成数组:");
for (int i = 0; i < length; i++) {
array[i] = Integer.parseInt(lines[i].trim());
System.out.print(array[i]);
}
System.out.println("\n");
return array;
}
// true 为升序,false 为降序,n为数组长度
public static int[] generateRandomSortedArray(boolean ascending) {
int n=Integer.parseInt(JOptionPane.showInputDialog("输入数组的长度:"));
int[] array = new int[n];
// 随机生成未排序的数组
for (int i = 0; i < n; i++) {
array[i] = i + 1;
}
shuffleArray(array);
// 对数组进行排序
if (!ascending) {
Arrays.sort(array);
reverseArray(array);
} else {
Arrays.sort(array);
}
return array;
}
// 随机打乱数组
private static void shuffleArray(int[] array) {
Random random = new Random();
for (int i = array.length - 1; i > 0; i--) {
int index = random.nextInt(i + 1);
int temp = array[index];
array[index] = array[i];
array[i] = temp;
}
}
// 反转数组
private static void reverseArray(int[] array) {
for (int i = 0; i < array.length / 2; i++) {
int temp = array[i];
array[i] = array[array.length - 1 - i];
array[array.length - 1 - i] = temp;
}
}
//此函数首先调用countTurningPoints函数来计算数组中的转折点数量
//如果转折点数量为0,它会检查数组是否严格升序(第一个元素小于最后一个元素)或严格降序(第一个元素大于最后一个元素)
//如果转折点数量为1,它会找到这个转折点,然后检查数组是否先升后降或先降后升
//如果转折点数量大于1,或者没有符合先升后降或先降后升的情况,函数会返回0,表示数组未排序
public static int checkArrayStatus(int[] array) {
int turningPoints = countTurningPoints(array);
if (turningPoints == 0) {
if (array[0] < array[array.length - 1]) {
return 1; // 升序
} else if (array[0] > array[array.length - 1]) {
return 2; // 降序
}
} else if (turningPoints == 1) {//只有一次转折
int turningIndex = findTurningIndex(array);
if (turningIndex > 0 && turningIndex < array.length - 1) {
if (array[0] < array[turningIndex] && array[turningIndex] > array[array.length - 1]) {//判断峰值和初末值大小,判断先升后降
return 3; // 先升后降
} else if (array[0] > array[turningIndex] && array[turningIndex] < array[array.length - 1]) {//判断波谷值和初末值大小,判断先升后降
return 4; //先降后升
}
}
}
return 0; // 未排序
}
//这个函数通过遍历数组来计算转折点的数量
//转折点被定义为一个索引,其前一个元素与后一个元素的关系发生变化(例如,从升序变为降序或反之)
//函数返回转折点的数量
public static int countTurningPoints(int[] array) {
int turningPoints = 0;
for (int i = 1; i < array.length - 1; i++) {
if ((array[i-1] < array[i] && array[i] > array[i+1]) || (array[i-1] > array[i] && array[i] < array[i+1])) {//顶峰或波谷
turningPoints++;
}
}
return turningPoints;
}
//这个函数返回数组中的第一个转折点的索引
//转折点是一个索引,其前后元素的关系发生变化
//如果没有找到转折点,则返回-1
public static int findTurningIndex(int[] array) {
for (int i = 1; i < array.length - 1; i++) {
if ((array[i-1] < array[i] && array[i] > array[i+1]) || (array[i-1] > array[i] && array[i] < array[i+1])) {
return i;
}
}
return -1; //没有找到索引
}
//查找数
public static void searchElement(int[] arr ,int i) {
compareCount = 0;
int position;
int numComparisons;
boolean position1;
int[] comparisons = { 0 };
switch (i) {
case 1:
//顺序查找
String input = JOptionPane.showInputDialog("输入要查询的元素:");
int target = Integer.parseInt(input);
position = sequentialSearch(arr, target);
if (position == -1) {
JOptionPane.showMessageDialog(null, "元素没有找到!");
System.out.println("\n元素没有找到!");
} else {
JOptionPane.showMessageDialog(null, position, "找到元素的位置", JOptionPane.INFORMATION_MESSAGE);
System.out.println("找到元素的位置: " + position);
}
JOptionPane.showMessageDialog(null, compareCount, "关键字比较次数", JOptionPane.INFORMATION_MESSAGE);
System.out.println("关键字比较次数: " + compareCount);
break;
case 2:
//二分查找
// compareCount = 0;
input = JOptionPane.showInputDialog("输入要查询的元素:");
target = Integer.parseInt(input);
position = binarySearch(arr, target, 0, arr.length - 1);
if (position == -1) {
JOptionPane.showMessageDialog(null, "元素没有找到!");
System.out.println("\n元素没有找到!");
} else {
JOptionPane.showMessageDialog(null, position, "找到元素的位置", JOptionPane.INFORMATION_MESSAGE);
System.out.println("找到元素的位置: " + position);
}
JOptionPane.showMessageDialog(null, compareCount, "关键字比较次数", JOptionPane.INFORMATION_MESSAGE);
System.out.println("关键字比较次数: " + compareCount);
break;
case 3:
//三分查找
// compareCount = 0;
int[] comparisons1 = { 0 };
input = JOptionPane.showInputDialog("输入要查询的元素:");
target = Integer.parseInt(input);
position1 = ternarySearch(arr, target, comparisons1);
numComparisons = comparisons1[0];
if (position1==false) {
JOptionPane.showMessageDialog(null, "元素没有找到!");
System.out.println("\n元素没有找到!");
} else {
JOptionPane.showMessageDialog(null, "元素在数组中!");
System.out.println("元素在数组中!" );
}
JOptionPane.showMessageDialog(null, numComparisons, "关键字比较次数", JOptionPane.INFORMATION_MESSAGE);
System.out.println("关键字比较次数: " + numComparisons);
break;
case 4:
//二分查找最大值
boolean findMax = true;//最大值 findMax 的值设为 true 最小值 findMax 的值设为 false
position = findExtremeValue(arr, findMax, comparisons);
numComparisons = comparisons[0];
JOptionPane.showMessageDialog(null, position, "最大值:", JOptionPane.INFORMATION_MESSAGE);
System.out.println("最大值:" + position);
JOptionPane.showMessageDialog(null, numComparisons, "关键字比较次数", JOptionPane.INFORMATION_MESSAGE);
System.out.println("关键字比较次数:" + numComparisons);
break;
case 5:
//二分查找最小值
boolean findMin = false;//最大值 findMax 的值设为 true 最小值 findMax 的值设为 false
position = findExtremeValue(arr, findMin, comparisons);
numComparisons = comparisons[0];
JOptionPane.showMessageDialog(null, position, "最小值:", JOptionPane.INFORMATION_MESSAGE);
System.out.println("最小值:" + position);
JOptionPane.showMessageDialog(null, numComparisons, "关键字比较次数", JOptionPane.INFORMATION_MESSAGE);
System.out.println("关键字比较次数:" + numComparisons);
break;
case 6:
input = JOptionPane.showInputDialog("输入要查询的元素:");
target = Integer.parseInt(input);
position = binarySearch(arr, target);
if (position == -1) {
JOptionPane.showMessageDialog(null, position, "接近元素的位置", JOptionPane.INFORMATION_MESSAGE);
System.out.println("接近元素的位置: " + position);
} else {
JOptionPane.showMessageDialog(null, position, "找到元素的位置", JOptionPane.INFORMATION_MESSAGE);
System.out.println("找到元素的位置: " + position);
}
JOptionPane.showMessageDialog(null, compareCount, "关键字比较次数", JOptionPane.INFORMATION_MESSAGE);
System.out.println("关键字比较次数: " + compareCount);
break;
// default:
// System.out.println("其他");
}
}
//顺序查找算法
public static int sequentialSearch(int[] arr, int target) {
for (int i = 0; i < arr.length; i++) {
compareCount++;
if (arr[i] == target) {
return i;
}
}
return -1;
}
//二分查找算法
public static int binarySearch(int[] arr, int target, int left, int right) {
if (left > right) {
return -1;
}
int mid = left + (right - left) / 2;
compareCount++;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
return binarySearch(arr, target, mid + 1, right);//右界
} else {
return binarySearch(arr, target, left, mid - 1);//左界
}
}
//查找元素返回位置,否则返回接近该元素位置
public static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
int index = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) {
index = mid;
break;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
// 如果目标元素不存在于数组中,则返回小于最接近该元素的位置
if (index == -1) {
index = right;
}
return index;
}
//三分搜索算法
//使用一个 while 循环进行迭代,判断给定元素与数组中的中间元素进行比较
//在每次比较时,我们都会通过增加 comparisons 数组的第一个元素的值来记录关键字比较的次数
//如果找到匹配的元素,则返回 true 表示该元素出现在数组中
//如果左边界 left 大于右边界 right,则跳出循环,并返回 false 表示该元素未出现在数组中
public static boolean ternarySearch(int[] array, int target, int[] comparisons) {
int left = 0;
int right = array.length - 1;
while (left <= right) {
comparisons[0] += 2; // 每次循环会进行两次关键字比较
int mid1 = left + (right - left) / 3; //在1/3处
int mid2 = right - (right - left) / 3; //在2/3处
if (array[mid1] == target) {
return true; // 元素出现在数组中
}
if (array[mid2] == target) {
return true; // 元素出现在数组中
}
if (target < array[mid1]) { //在0-1/3范围内
right = mid1 - 1;
} else if (target > array[mid2]) { //在1/3-2/3范围内
left = mid2 + 1;
} else if (target < array[mid2]){ //在>2/3范围内
right = mid2 - 1;
left = mid1 + 1;
}
}
return false; // 元素未出现在数组中
}
//找最大值的情况:
//使用二分检索在先升后降(或先降后升)数组中找到一个满足数组中元素值严格大于两侧元素值的位置,即找到数组的峰值
//返回数组中的峰值作为最大值
//找最小值的情况:
//使用二分检索在先升后降(或先降后升)数组中找到一个满足数组中元素值严格小于两侧元素值的位置
//返回数组中的这个位置的元素作为最小值
public static int findExtremeValue(int[] array, boolean findMax, int[] comparisons) {
if (array == null || array.length == 0) {
throw new IllegalArgumentException("数组不为空!");
}
int left = 0;
int right = array.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
comparisons[0]++; // 记录比较次数
if (findMax) {//查找最大值true
if (array[mid] < array[mid + 1]) {
left = mid + 1; // 在右侧继续搜索
} else {
right = mid; // 在左侧继续搜索
}
} else {//查找最小值false
if (array[mid] > array[mid + 1]) {
left = mid + 1; // 在右侧继续搜索
} else {
right = mid; // 在左侧继续搜索
}
}
}
return array[left]; // 左指针指向最小值或最大值
}
}
UI.java
package com;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class UI{
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> ViewUI());
}
public static void ViewUI() {
// 创建用户界面
JFrame frame = new JFrame("UI界面");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 200);
frame.setLocation(500, 300);
frame.setLayout(new FlowLayout());
JMenuBar menuBar = new JMenuBar();
JMenu searchMenu = new JMenu("检索算法选择");
JMenu sequentialSearch = new JMenu("顺序检索");
JMenu binarySearch = new JMenu("二分检索");
JMenu threeSearch = new JMenu("三分检索");
JMenu shengchengArr = new JMenu("生成数组");
JMenuItem renGong = new JMenuItem("人工生成数组");
JMenuItem ziDong = new JMenuItem("自动生成数组");
JMenuItem searchShu1 = new JMenuItem("搜素元素");
JMenuItem maxSearch = new JMenuItem("查找最大值");
JMenuItem minSearch = new JMenuItem("查找最小值");
JMenuItem searchShu2 = new JMenuItem("搜素元素");
JMenuItem searchShu3 = new JMenuItem("搜素元素");
//菜单项添加
sequentialSearch.add(searchShu1);
binarySearch.add(searchShu2);
binarySearch.add(maxSearch);
binarySearch.add(minSearch);
threeSearch.add(searchShu3);
shengchengArr.add(renGong);
shengchengArr.add(ziDong);
//菜单添加
searchMenu.add(shengchengArr);
searchMenu.add(sequentialSearch);
searchMenu.add(binarySearch);
searchMenu.add(threeSearch);
menuBar.add(searchMenu);
frame.setJMenuBar(menuBar);
renGong.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] arr = searchSuanfa.renGongArray();
// 将数组转换为字符串
StringBuilder sb = new StringBuilder();
sb.append("生成的数组:");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i != arr.length - 1) {
sb.append(", ");
}
}
String arrString = sb.toString();
JOptionPane.showMessageDialog(frame, arrString, "生成数组", JOptionPane.INFORMATION_MESSAGE);
}
});
ziDong.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] arr = searchSuanfa.generateArray();
// 将数组转换为字符串
StringBuilder sb = new StringBuilder();
sb.append("生成的数组:");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i != arr.length - 1) {
sb.append(", ");
}
}
String arrString = sb.toString();
JOptionPane.showMessageDialog(frame, arrString, "生成数组", JOptionPane.INFORMATION_MESSAGE);
}
});
searchShu1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] arr = searchSuanfa.ChooseGenerateArray();
// 将数组转换为字符串
StringBuilder sb = new StringBuilder();
sb.append("生成的数组:");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i != arr.length - 1) {
sb.append(", ");
}
}
String arrString = sb.toString();
JOptionPane.showMessageDialog(frame, arrString, "生成数组", JOptionPane.INFORMATION_MESSAGE);
int catagory = searchSuanfa.checkArrayStatus(arr);
JOptionPane.showMessageDialog(frame, catagory,"显示数组状态(未排序0)、(升序1)、(降序2)、(先升后降3)、(先降后升4)", JOptionPane.INFORMATION_MESSAGE);
searchSuanfa.searchElement(arr,1);
}
});
searchShu2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] arr = searchSuanfa.ChooseGenerateArray();
// 将数组转换为字符串
StringBuilder sb = new StringBuilder();
sb.append("生成的数组:");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i != arr.length - 1) {
sb.append(", ");
}
}
String arrString = sb.toString();
JOptionPane.showMessageDialog(frame, arrString, "生成数组", JOptionPane.INFORMATION_MESSAGE);
int catagory = searchSuanfa.checkArrayStatus(arr);
JOptionPane.showMessageDialog(frame, catagory,"显示数组状态(未排序0)、(升序1)、(降序2)、(先升后降3)、(先降后升4)", JOptionPane.INFORMATION_MESSAGE);
searchSuanfa.searchElement(arr,2);//二分查找
}
});
maxSearch.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] arr = searchSuanfa.ChooseGenerateArray();
// 将数组转换为字符串
StringBuilder sb = new StringBuilder();
sb.append("生成的数组:");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i != arr.length - 1) {
sb.append(", ");
}
}
String arrString = sb.toString();
JOptionPane.showMessageDialog(frame, arrString, "生成数组", JOptionPane.INFORMATION_MESSAGE);
int catagory = searchSuanfa.checkArrayStatus(arr);
JOptionPane.showMessageDialog(null, catagory,"显示数组状态(未排序0)、(升序1)、(降序2)、(先升后降3)、(先降后升4)", JOptionPane.INFORMATION_MESSAGE);
searchSuanfa.searchElement(arr,4);
}
});
minSearch.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] arr = searchSuanfa.ChooseGenerateArray();
// 将数组转换为字符串
StringBuilder sb = new StringBuilder();
sb.append("生成的数组:");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i != arr.length - 1) {
sb.append(", ");
}
}
String arrString = sb.toString();
JOptionPane.showMessageDialog(frame, arrString, "生成数组", JOptionPane.INFORMATION_MESSAGE);
int catagory = searchSuanfa.checkArrayStatus(arr);
JOptionPane.showMessageDialog(null, catagory,"显示数组状态(未排序0)、(升序1)、(降序2)、(先升后降3)、(先降后升4)", JOptionPane.INFORMATION_MESSAGE);
searchSuanfa.searchElement(arr,5);
}
});
searchShu3.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] arr = searchSuanfa.ChooseGenerateArray();
// 将数组转换为字符串
StringBuilder sb = new StringBuilder();
sb.append("生成的数组:");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if (i != arr.length - 1) {
sb.append(", ");
}
}
String arrString = sb.toString();
JOptionPane.showMessageDialog(frame, arrString, "生成数组", JOptionPane.INFORMATION_MESSAGE);
int catagory = searchSuanfa.checkArrayStatus(arr);
JOptionPane.showMessageDialog(null, catagory,"显示数组状态(未排序0)、(升序1)、(降序2)、(先升后降3)、(先降后升4)", JOptionPane.INFORMATION_MESSAGE);
searchSuanfa.searchElement(arr,3);//三分查找
}
});
frame.setVisible(true);
}
}
以上内容是我的实验作业,仅供参考, 有问题请自行查找解决。