java编程算法经典案例
编程经典案例(持续更新中,敬请期待):
一、购物问题
小明的女朋友最喜欢在网上买买买了,可是钱包里钞票有限,不能想买啥就买啥。面对琳琅满目的物品,她想买尽可能多的种类,每种只买一件,同时总价格还不能超过预算上限。于是她请小明写程序帮她找出应该买哪些物品,并算出这些物品的总价格。
输入规范:
每个输入包含两行。第一行是预算上限。第二行是用空格分隔的一组数字,代表每种物品的价格。所有数字都为正整数并且不会超过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; } }
六、字符串最长不重复子串(阿里笔试题)
说明:
给定字符串,找出其最长的并且最多只有一个重复字符的子串。
如:"abcdefgdchi", 符合条件的子串是"abcdefgd"、"defgdchi"
import java.util.*; /** * @author tobiasy * @date 2020/8/11 */ public class MaxLength { /** * 给定字符串,找出其最长的并且最多只有一个重复字符的子串。 如:"abcdefgdchi", 符合条件的子串是"abcdefgd"、"defgdchi" * * @param args */ public static void main(String[] args) { String str = "abcdefgdchi"; List<String> result = new ArrayList<>(); // 循环找到每一个子串 for (int i = 0; i < str.length(); i++) { for (int j = i + 1; j <= str.length(); j++) { // 子串 String substring = str.substring(i, j); // 判断子串是否有一个以上重复字符 boolean repeat = isRepeat(substring); if (!repeat) { // 将不重复的子串放入result中 result.add(substring); } } } // 过滤result中的不重复子串,找到最长的字符串,便是最终结果 List<String> maxString = getMaxString(result.toArray(new String[]{})); System.out.println("结果:" + maxString); } /** * 判断子串是否有一个以上重复字符(不含一个) * @param str * @return */ private static boolean isRepeat(String str) { Set<Character> set = new HashSet<>(); for (int i = 0; i < str.length(); i++) { set.add(str.charAt(i)); } // 刚好一个重复,不算真的重复 if (set.size() == str.length() - 1) { return false; } // 判断是否重复 return set.size() != str.length(); } /** * 找到最长的字符串 * @param arr * @return */ private static List<String> getMaxString(String[] arr) { List<String> result = new ArrayList<>(); Set<Integer> lengthSet = new HashSet<>(); // 将数组中的字符传长度存放到lengthSet中 for (String s : arr) { lengthSet.add(s.length()); } // 获取lengthSet中的字符串最大长度 Integer max = Collections.max(lengthSet); // 判断数组中满足最大长度条件的所有子串,放入result中 for (String s : arr) { if (s.length() == max) { result.add(s); } } return result; } }
Java自学教程推荐