编程经典案例(持续更新中,敬请期待):
一、购物问题
小明的女朋友最喜欢在网上买买买了,可是钱包里钞票有限,不能想买啥就买啥。面对琳琅满目的物品,她想买尽可能多的种类,每种只买一件,同时总价格还不能超过预算上限。于是她请小明写程序帮她找出应该买哪些物品,并算出这些物品的总价格。
输入规范:
每个输入包含两行。第一行是预算上限。第二行是用空格分隔的一组数字,代表每种物品的价格。所有数字都为正整数并且不会超过10000。
输出规范:
对每个输入,输出应买物品的总价格。
输入示例1:
100
50 50
输出示例1:
100
输入示例2:
188
50 42 9 15 105 63 14 30
输出示例2:
160
代码如下:
public class BuyTest {
public static void main(String[] args) {
//商品价格
Integer[] ints = {50, 42, 9, 15, 105, 63, 14, 30};
//预算上限
Integer sum = 188;
Integer num = calculate(ints, sum);
System.out.println(num);
}
public static Integer calculate(Integer[] ints, Integer sum) {
Integer count = 0;
Integer value;
sort(ints);
for (Integer integer : ints) {
value = count;
count += integer;
if (count > sum) {
return value;
}
}
return count;
}
public static void sort(Integer[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
}
二、信息加密(数组偏移)问题
李雷和韩梅梅坐前后排,上课想说话怕被老师发现,所以改为传小纸条。为了不被老师发现他们纸条上说的是啥,他们约定了如下方法传递信息:
将26个英文字母(全为大写),外加空格,一共27个字符分成3组,每组9个。也就是ABCDEFGHI是第一组,JKLMNOPQR是第二组,STUVWXYZ*是第三组(此处用*代表空格)。
然后根据传递纸条那天的日期,改变字母的位置。
先根据月份数m,以整个分组为单位进行循环左移,移动(m-1)次。
然后根据日期数d,对每个分组内的字符进行循环左移,移动(d-1)次。
以3月8日为例,首先移动分组,3月需要循环左移2次,变成:
STUVWXYZ*,ABCDEFGHI,JKLMNOPQR
然后每组内的字符,8日的话需要循环左移7次,最终的编码为:
Z*STUVWXY,HIABCDEFG,QRJKLMNOP
对于要传递信息中的每个字符,用组号和组内序号两个数字来表示。
如果在3月8日传递信息“HAPPY”,那么H位于第2组的第1个,A位于第2组第3个,P位于第3组第9个,Y位于第1组第9个,所以纸条上会写成:
21 23 39 39 19
现在给定日期和需要传递的信息,请输出应该写在纸条上的编码。
输入规范:
每个输入包含两行。第一行是用空格分隔的两个数字,第一个数字是月份,第二个数字是日子。输入保证是一个合法的日期。
第二行为需要编码的信息字符串,仅由A~Z和空格组成,长度不超过1024个字符。
输出规范:
对每个输入,打印对应的编码,数字之间用空格分隔,每个输出占一行。
输入示例1:
1 1
HI
输出示例1:
18 19
输入示例2:
3 8
HAPPY
输出示例2:
21 23 39 39 19
输入示例3:
2 14
I LOVE YOU
输出示例3:
35 25 18 12 29 31 25 23 12 28
代码如下:
package com.tobiasy.toolkit.test;
public class MessageTest {
public static void main(String[] args) {
//月份数
int month = 2;
//日
int day = 14;
//所需要转化的字符
String text = "I LOVE YOU";
start(month, day, text);
}
public static void start(int month, int day, String text) {
char[] chars = text.toCharArray();
for (char c : chars) {
String value = getChar(month, day, c);
System.out.print(value + ",");
}
}
public static String getChar(int month, int day, char c) {
month -= 1;
day -= 1;
char[][] chars = {
{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'},
{'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R'},
{'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' '}};
int monthFoward = month % chars.length;
int dayFoward = day % chars[0].length;
chars = sort(chars, monthFoward);
for (int i = 0; i < chars.length; i++) {
char[] arr = chars[i];
sort(arr, dayFoward);
}
return getCharLocation(chars, c);
}
public static String getCharLocation(char[][] chars, char c) {
String value = "";
sys:
for (int i = 0; i < chars.length; i++) {
for (int j = 0; j < chars[i].length; j++) {
char ch = chars[i][j];
if (ch == c) {
value = ++i + "" + ++j;
break sys;
}
}
}
return value;
}
public static char[] sort(char[] chars, int dayFoward) {
char[] arr = chars.clone();
for (int i = 0; i < chars.length; i++) {
int forward = i + dayFoward >= chars.length ? i + dayFoward - chars.length : i + dayFoward;
chars[i] = arr[forward];
}
return chars;
}
public static char[][] sort(char[][] chars, int monthFoward) {
char[][] arr = chars.clone();
for (int i = 0; i < chars.length; i++) {
int forward = i + monthFoward >= chars.length ? i + monthFoward - chars.length : i + monthFoward;
chars[i] = arr[forward];
}
return chars;
}
}
三、老鼠走迷宫
/**
* 老鼠走迷宫
* 说明:
* 老鼠走迷宫是递回求解的基本题型,我们在二维阵列中使用2表示迷宫墙壁,使用1表示老鼠行走的路径,试以程
* 式求出由入口至出口的路径。
*
* 解法:
* 老鼠的走法有上,下,左,右四个方向,在每前进一格之后就选一个方向前进,无法前进时退回选择下一个可前
* 进方向,如此在阵列中依序测试四个方向,直到走到出口为至,这是返回的基本题,请直接看程式应就可以理解
*/
static int[][] maze = {
{2, 2, 2, 2, 2, 2, 2, 2},
{2, 0, 0, 0, 0, 0, 0, 2},
{2, 0, 2, 0, 2, 0, 0, 2},
{2, 2, 0, 0, 0, 2, 0, 2},
{2, 2, 0, 2, 0, 0, 0, 2},
{2, 2, 0, 2, 2, 2, 2, 2},
{2, 0, 0, 0, 0, 0, 0, 2},
{2, 2, 2, 2, 2, 2, 2, 2}
};
static int startI = 1, startJ = 1;
static int endI = 6, endJ = 6;
static int success = 0;
public static int walk() {
int i, j;
int length = maze[0].length;
System.out.println("显示迷宫:\n");
for(i = 0; i < length; i++)
{
for(j = 0; j < length; j++)
{
if(maze[i][j] == 2) {
System.out.print("#");
} else {
System.out.print(" ");
}
}
System.out.println("\n");
}
if(visit(startI, startJ) == 0) {
System.out.println("\n没有找到出口!\n");
} else {
System.out.println("\n显示路径:\n");
for(i = 0; i < length; i++)
{
for(j = 0; j < length; j++)
{
if(maze[i][j] == 2) {
System.out.print("#");
} else if(maze[i][j] == 1) {
System.out.print("1");
} else {
System.out.print(" ");
}
}
System.out.println("\n");
}
}
return 0;
}
private static int visit(int i, int j) {
maze[i][j] = 1;
if(i == endI && j == endJ) {
success = 1;
}
if(success != 1 && maze[i][j+1] == 0) {
visit(i, j+1);
}
if(success != 1 && maze[i+1][j] == 0) {
visit(i+1, j);
}
if(success != 1 && maze[i][j-1] == 0) {
visit(i, j-1);
}
if(success != 1 && maze[i-1][j] == 0) {
visit(i-1, j);
}
if(success != 1) {
maze[i][j] = 0;
}
return success;
}
四、八皇后问题
说明:
西洋棋中的皇后可以直接前进,吃掉遇到的所有棋子,如果棋盘上有八个皇后,则这八个皇后如何相安无事的放置在棋盘上,
1970年与1971年,E.W.Dijkstra与N.Wirth曾经用这个问题来讲解程式设计之技巧。
解法:
关于棋盘的问题,可以用递回求解,然而如何减少递回的次数?在八个皇后额问题中,不必要所有的个子都检查过,例如若某列
检查过,该列的其他格子就不用再检查了,这个方法称为分支修剪。
package com.tobiasy.toolkit.algorithm;
/**
* @author tobiasy
* @date 2018/10/29
*/
public class EightQueen {
public static void main(String[] args) {
queen();
}
static final Integer N = 8;
/**
* 同栏是否有皇后,1表示有
*/
static int[] column = new int[N+1];
/**
* 右上至左下是否有皇后
*/
static int[] rup = new int[2*N+1];
/**
* 左上至右下是否有皇后
*/
static int[] lup = new int[2*N+1];
static int[] queen = new int[N+1];
/**
* 解答编号
*/
static int num;
private static int queen() {
int i;
num = 0;
for(i = 1; i <= N; i++)
{
column[i] = 1;
}
for(i = 1; i <= 2*N; i++)
{
rup[i] = lup[i] = 1;
}
backtrack(1);
return 0;
}
private static void showAnswer() {
int x, y;
System.out.print("\n解答 "+ ++num+"\n");
for(y = 1; y <= N; y++) {
for(x = 1; x <= N; x++)
{
if(queen[y] == x)
{
System.out.print("●");
}
else
{
System.out.print("◎");
}
}
System.out.print("\n");
}
}
private static void backtrack(int i) {
int j;
if(i > N)
{
showAnswer();
}
else
{
for(j = 1; j <= N; j++)
{
if(column[j] == 1 && rup[i+j] == 1 && lup[i-j+N] == 1)
{
queen[i] = j;
column[j] = rup[i+j] = lup[i-j+N] = 0;
backtrack(i+1);
column[j] = rup[i+j] = lup[i-j+N] = 1;
}
}
}
}
}
五、字符串最长对称子串
说明:
输入一段字符串,求其最长的对称子串并输出;例:
输入:qabccbaff,输出:abccba
出入:pop-upu, 输出:pop和upu
import java.util.ArrayList;
import java.util.List;
/**
* 找出最长对称字符串
*
* @author tobiasy
*/
public class MaxSymmetric {
public static void main(String[] args) {
// TODO 输出最长对称字符串:goog
String input1 = "google";
// TODO 输出最长对称字符串:aba
String input2 = "abada";
// TODO 输出2个最长对称字符串:pop/upu
String input3 = "pop-upu";
start("sdghjdgzzgdah");
}
/**
* 启动方法
* @param str
*/
private static void start(String str) {
List<String> list = maxSubSymmetric(str);
if (list.isEmpty()) {
System.err.println("没有找到对称字串!");
}
list.forEach(System.out::println);
}
/**
* 获取字符串中最长的对称字串
* @param str
* @return
*/
private static List<String> maxSubSymmetric(String str) {
List<String> result = new ArrayList<>();
List<String> list = allSubSymmetric(str);
Integer length = 0;
for (String s : list) {
if (s.length() > length) {
length = s.length();
}
}
for (String s : list) {
if (s.length() >= length) {
result.add(s);
}
}
return result;
}
/**
* 获取所有的对称字串
* @param str
* @return
*/
private static List<String> allSubSymmetric(String str) {
List<String> list = new ArrayList<>();
for (int i = 0; i <= str.length(); i++) {
for (int j = i; j <= str.length(); j++) {
String sub = str.substring(i, j);
if (check(sub)) {
list.add(sub);
}
}
}
return list;
}
/**
* 判断字符串是否对称
* @param str
* @return
*/
private static boolean check(String str) {
if (str == null || str.length() <= 1) {
return false;
}
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) != str.charAt(str.length() - i - 1)) {
return false;
}
}
return true;
}
}
六、模拟手机号注册
说明:
利用面向对象思路,设计和完成“手机号注册校验”业务逻辑。
package com.tobiasy.toolkit.algorithm;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 用户手机号注册校验
*
* @author tobiasy
*/
public class PhoneRegister {
/**
* 缓存地址
*/
private static final String FILE_PATH = "E://test/data";
public static void main(String[] args) {
String phoneNum1 = "138 1234 1234";
String phoneNum2 = "13812345abc";
String phoneNum3 = "13812345678";
String phoneNum4 = "1381234 5678";
String phoneNum5 = "98765432101";
start(phoneNum3);
}
/**
* 启动方法
* @param phone
*/
private static void start(String phone){
if (check(phone)) {
register(format(phone));
} else {
err("非法手机号码!");
}
out(String.format("已注册账号:%s", getRegisterPhoneNums()));
}
private static void err(Object o) {
System.err.println(o);
}
private static void out(Object o) {
System.out.println(o);
}
/**
* 创建文件
* @return
*/
private static boolean createFile() {
File file = new File(FILE_PATH);
if (!file.exists()) {
try {
File parentFile = file.getParentFile();
if (!parentFile.exists()) {
parentFile.mkdirs();
}
file.createNewFile();
} catch (IOException e) {
return false;
}
}
return true;
}
/**
* 反序列化
* @return
*/
private static Object getRegisterPhoneNums(){
return SerializableUtils.read(FILE_PATH);
}
/**
* 格式化手机号码
* @param phoneNum
* @return
*/
private static String format(String phoneNum){
return phoneNum.replaceAll("( )\\1*", "");
}
/**
* 注册
* @param phoneNum
*/
public static void register(String phoneNum) {
try {
if (!createFile()) {
err("缓存文件地址错误!");
} else {
List<String> data;
Object object = SerializableUtils.read(FILE_PATH);
if (object != null) {
data = (List<String>) object;
boolean contains = data.contains(phoneNum);
if (contains) {
err("该手机号已被注册!");
} else {
data.add(phoneNum);
out("注册成功!");
}
} else {
data = new ArrayList<>();
data.add(phoneNum);
out("注册成功!");
}
SerializableUtils.write(data, FILE_PATH);
}
} catch (Exception e) {
err("序列化异常!");
}
}
/**
* 校验手机号码是否正确
* @param phoneNum
* @return
*/
private static boolean check(String phoneNum) {
return phoneNum.matches("1(3[0-9]|5[189]|8[6789])( )?[0-9]{4}( )?[0-9]{4}");
}
}
工具类
package com.tobiasy.toolkit.algorithm;
import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * JDK自带序列化操作 * * @author tobiasy */ public class SerializableUtils { /** * 序列化 将对象存储到文件中 * * @param obj 序列化对象,必须实现可序列化接口 * @param path 序列化路径 * @return */ public static boolean write(Object obj, String path) { boolean f; File file = getFile(path); OutputStream out = null; ObjectOutputStream objout = null; try { out = new FileOutputStream(file); objout = new ObjectOutputStream(out); objout.writeObject(obj); f = true; } catch (IOException e) { f = false; e.printStackTrace(); } finally { close(objout); close(out); } return f; } /** * 反序列化 读取存入文件中的对象 * * @param pathname 反序列化路径 * @return */ public static Object read(String pathname) { Object obj = null; File file = new File(pathname); InputStream in = null; ObjectInputStream objin = null; try { in = new FileInputStream(file); objin = new ObjectInputStream(in); obj = objin.readObject(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (EOFException e) { } catch (IOException e) { e.printStackTrace(); } finally { close(objin); close(in); } return obj; } public static File getFile(String filePath) { File file = new File(filePath); if (!file.exists()) { try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } return file; } /** * 流的关闭操作 * * @param obj 流对象 */ public static void close(Object obj) { if (obj != null) { try { invoke(obj, "close"); } catch (RuntimeException e) { e.printStackTrace(); } } } /** * 执行一个无参方法 * * @param obj 操作对象 * @param methodName 属性名 * @return Object * @throws RuntimeException */ public static Object invoke(Object obj, String methodName) throws RuntimeException { if (obj == null || methodName == null) { return null; } Object value = null; try { Method getMethod = obj.getClass().getMethod(methodName); if (getMethod == null) { return null; } getMethod.setAccessible(true); value = getMethod.invoke(obj); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } return value; } }
Java自学教程推荐
