一、觀察表法
小虎去附近的商店買蘋果,奸詐的商販使用了捆綁交易,只提供6個每袋和8個 每袋的包裝包裝不可拆分。可是小虎現在只想購買恰好n個蘋果,小虎想購買盡 量少的袋數方便攜帶。如果不能購買恰好n個蘋果,小虎將不會購買。輸入一個 整數n,表示小虎想購買的個蘋果,返回最小使用多少袋子。如果無論如何都不 能正好裝下,返回-1
/**
* @Author: 郜宇博
* @Date: 2021/11/18 14:19
*/
public class Bag {
public static void main(String[] args) {
for (int i = 1 ; i < 100; i ++){
if (minBags(i) != minBag1(i)){
System.out.println(i+" no");
}
}
}
/**
* 袋子分為8,6兩種,想讓袋子數量最少,優先選擇8
* 所以先m/8得出用幾個8袋子,然後計算剩餘的蘋果數
* 檢視剩餘的蘋果樹是否可以被6整除,如果可以總袋數就等於bag8 + bag6
* 不可以就將8袋子所用數量-1,再計算剩餘蘋果樹然後判斷bag6,
* 當bag8減到0或者剩餘蘋果>24時,都不用繼續了(因為>24肯定優先考慮bag8)
*/
public static int minBags(int m){
if ((m & 1)!= 0){
return -1;
}
if (m == 6 || m == 8){
return 1;
}
int bag6 = -1;
int bag8 = m / 8;
int rest = m - 8 * bag8;
while (rest < 24 && bag8 >= 0){
bag6 = rest % 6 == 0 ? rest/6: -1;
if (bag6 != -1){
return bag8 + bag6;
}
bag8--;
rest = m - (bag8 * 8);
}
return -1;
}
/**
* 觀察表法
*/
public static int minBag1(int m){
if ( (m & 1) != 0){
return -1;
}
if (m < 18){
switch (m){
case 6:
case 8:
return 1;
case 12:
case 14:
case 16:
return 2;
default:
return -1;
}
}
return ((m - 18) / 8) + 3;
}
}
二、滑動視窗
給定一個有序陣列arr,代表數軸上從左到右有n個點arr[0]、arr[1]...arr[n-1], 給定一個正數L,代表一根長度為L的繩子,求繩子最多能覆蓋其中的幾個點。
/**
* 滑動視窗,讓left一直向右走
* 先讓繩子起始點處於ar[0],記為L,然後伸長繩子看能否到下一點arr[1],一直到不可以伸長為止
* 當不可以伸長的時候,更新max,L++,一直到arr末尾
*/
public static int maxCount(int []arr,int L){
int left = 0;
int count = 0;
int max = Integer.MIN_VALUE;
//int count = 0;
for (left = 0; left < arr.length; left++){
count = 0;
while (left+count < arr.length && arr[left+count] - arr[left] <= L){
count++;
}
//更新max
max = Math.max(count,max);
}
return max;
}
三、預處理法
牛牛有一些排成一行的正方形。每個正方形已經被染成紅色或者綠色。牛牛現在可 以選擇任意一個正方形然後用這兩種顏色的任意一種進行染色,這個正方形的顏色將 會被覆蓋。
牛牛的目標是在完成染色之後,每個紅色R都比每個綠色G距離最左側近。
牛牛想知道他最少需要塗染幾個正方形。
如樣例所示: s = RGRGR 我們塗染之後變成RRRGG滿足要求了,塗染的個數為2,沒有比這個更好的塗染方案
/**
* @Author: 郜宇博
* @Date: 2021/11/18 16:38
*/
public class Color {
public static void main(String[] args) {
System.out.println(minColorCount("RGRGR"));
}
/**
* 預處理法
* 將左側都變為R,右側都變為G
* 因此要逐個位置從左到右嘗試,如將0的左邊變為R,和[0,n-1]變為G
* 1的左邊變為R, [1,n-1]變為G
* 而要想求最小染色數,就需要知道左邊範圍有多少個G,右邊範圍有多少個R
* 也就是[0,i]多少個G,[i,n-1]有多少個R
* 有這兩個結果,就可以遍歷,不斷更新min = [0,i]+[i+1,n-1];
*/
public static int minColorCount(String str){
int i = 0;
char[] chars = str.toCharArray();
int [] leftG = new int[chars.length];
int [] rightR = new int[chars.length];
int min = Integer.MAX_VALUE;
leftG[0] = chars[0] == 'G'?1:0;
rightR[chars.length-1] = chars[chars.length-1] == 'R'?1:0;
//預處理
for ( i= 1; i < leftG.length; i++){
leftG[i] = leftG[i-1] + ((chars[i] == 'G')?1:0);
}
for (i = rightR.length-2; i >= 0; i--){
rightR[i] = rightR[i+1] + ((chars[i] == 'R')?1:0);
}
for (i = 0; i < chars.length; i++){
if (i == 0){
min = Math.min(min,leftG[chars.length-1]);
}
else if (i == chars.length-1){
min = Math.min(min,rightR[0]);
}
else {
min = Math.min(min,leftG[i]+rightR[i+1]);
}
}
return min;
}
}
四、等概率返回
給定一個函式f,可以1~5的數字等概率返回一個。請加工出1~7的數字等概率 返回一個的函式g
/**
* @Author: 郜宇博
* @Date: 2021/11/18 18:24
*/
public class RandomNum {
public static void main(String[] args) {
for (int i = 0; i < 10; i++){
System.out.println(getRandom7());
}
}
public static int getRandom5(){
return (int) (Math.random() * 5) + 1;
}
/**
1-7等概率相當於0-6等概率+1
二進位制表示的話,需要3位二進位制可以表示到0-7,
所以需要一個 方法可以等概率返回0和1,
使用三次該方法,然後拼接到一起形成十進位制的數,如果最後為7,那麼重新使用三次。
這樣可以得到0-6等概率,再+1就是1-7
*/
public static int getRandom7(){
int random7 = 0;
do {
int random1 = getRandom01();
int random2 = getRandom01();
int random3 = getRandom01();
random7 = random1 + (random2 << 1)+(random3<<2);
}while (random7 == 7);
return random7 + 1;
}
/**
* 等概率返回0 和 1
* 使用等概率返回1-5 :12345
* 大於3返回1 ,小於3返回0等於三重新使用
*/
public static int getRandom01(){
int random01 = 0;
do {
random01 = getRandom5();
}while (random01 == 3);
return random01 > 3?1:0;
}
}
五、括號匹配
題1
需要多少額外的括號,才能將字串的括號保持正確匹配
/**
* @Author: 郜宇博
* @Date: 2021/11/19 16:13
*/
public class NeedParentheses {
public static void main(String[] args) {
for (int i =0 ; i < 20; i++){
String s = randomParentheses(10);
if (needParentheses(s) != needParentheses1(s)){
System.out.println("no");
}
}
}
public static String randomParentheses(int m) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < m; i++) {
int i1 = new Random().nextInt(10);
//奇
if ((i1 & 1) != 0){
stringBuilder.append("(");
}
else {
stringBuilder.append(")");
}
}
return stringBuilder.toString();
}
public static int needParentheses1(String str) {
int leftRest = 0;
int needSolveRight = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '(') {
leftRest++;
} else {
if (leftRest == 0) {
needSolveRight++;
} else {
leftRest--;
}
}
}
return leftRest + needSolveRight;
}
/**
* 設定一個count變數
* 從左向右遍歷。遇到左括號++,右括號--
* 在過程中如果count <0 ,那麼說明單獨出現了右括號,此時需要一個左括號
* 遍歷結束後,如果count >0,說明左括號多了,需要count這麼多的右括號
*/
public static int needParentheses(String str){
char[] chars = str.toCharArray();
//當前狀態,<0代表缺'('
int count = 0;
//需要多少額外的括號字元
int res = 0;
for (int i = 0; i < chars.length; i++) {
if (chars[i] == '('){
count++;
}
//遇到右括號
else {
//count--;
//if (count < 0){
// //加一個左括號
// res++;
// count = 0;
//}
if (count == 0){
res++;
}else {
count--;
}
}
}
return res+ count;
}
}
題2
括號字串的最大深度
public static int deep(String s) {
char[] str = s.toCharArray();
int count = 0;
int max = 0;
for (int i = 0; i < str.length; i++) {
if (str[i] == '(') {
max = Math.max(max, ++count);
} else {
count=0;
}
}
return max;
}
六、magic集合移動
給一個包含n個整數元素的集合a,一個包含m個整數元素的集合b。 定義magic操作為,從一個集合中取出一個元素,放到另一個集合裡,且操作過 後每個集合的平均值都大於操作前。 注意以下兩點:
1)不可以把一個集合的元素取空,這樣就沒有平均值了
2)值為x的元素從集合b取出放入集合a,但集合a中已經有值為x的元素,則a的 平均值不變(因為集合元素不會重複),b的平均值可能會改變(因為x被取出 了)
問最多可以進行多少次magic操作?
/**
* @Author: 郜宇博
* @Date: 2021/11/21 20:57
*/
public class MagicOp {
public static void main(String[] args) {
int[] arr1 = { 1, 2, 5 };
int[] arr2 = { 2, 3, 4, 5, 6 };
System.out.println(magic(arr1, arr2));
}
/**
* 移動元素後,保證兩個集合的平均值增大
* 1.保證移動的元素,不在目標集合中
*/
public static int magic(int []arr1,int[]arr2){
double sum1=0,sum2 = 0;
int [] smallList = null;
int [] largeList = null;
double largeSum = 0;
double smallSum = 0;
for (int value : arr1) {
sum1 += value;
}
for (int value : arr2) {
sum2 += value;
}
//平均值
double avg1 = getAvg(sum1,arr1.length);
double avg2 = getAvg(sum2,arr2.length);
if ( avg1 == avg2){
return 0;
}
if (avg1 > avg2){
//為集合賦值
largeList = arr1;
smallList = arr2;
largeSum = sum1;
smallSum = sum2;
}
else {
//為集合賦值
largeList = arr2;
smallList = arr1;
largeSum = sum2;
smallSum = sum1;
}
int largeSize = largeList.length;
int smallSize = smallList.length;
int result = 0;
HashSet<Integer> smallSet = new HashSet();
for (int num: smallList){
smallSet.add(num);
}
for (int num: largeList){
double cur = num;
//移動元素在平均值中間,並且目標集合中不存在該元素
if (cur > getAvg(smallSum,smallSize) && cur < getAvg(largeSum,largeSize) && !smallSet.contains(cur)){
smallSet.add(num);
largeSize--;
largeSum-=num;
smallSum+=num;
smallSize++;
result++;
}
}
return result;
}
private static double getAvg(double sum, int size) {
if (size > 0 ){
return sum/ size;
}
return -1;
}
}
七、數字轉化
將給定的數轉換為字串,原則如下:1對應 a,2對應b,…..26對應z,
例如12258 可以轉換為"abbeh", "aveh", "abyh", "lbeh" and "lyh",個數為5,
編寫一個函式,給出可以轉換的不同字串的個數。
/**
* @Author: 郜宇博
* @Date: 2021/11/21 21:36
*/
public class NumberConvert {
public static void main(String[] args) {
int test = 111143311;
char[] arr = String.valueOf(test).toCharArray();
System.out.println(convertDP(arr));
}
public static int convert(char []arr,int index){
if (index == arr.length){
return 1;
}
if (arr[index] == '0'){
return 0;
}
//當前索引位作為一個字母
int res = convert(arr,index+1);
//兩個索引位組合為字母 規則0-26
if ( (index + 1) < arr.length
&&
((arr[index]-'0')*10 + (arr[index+1]-'0') < 27 )
){
res += convert(arr,index+2);
}
return res;
}
public static int convertDP(char[] arr){
int[] dp = new int[arr.length+1];
dp[arr.length] = 1;
dp[arr.length-1] = arr[arr.length-1] == '0'?0:1;
for (int i = arr.length-2;i >=0; i--){
if (arr[i] == '0'){
dp[i] = 0;
}
else {
dp[i] = dp[i+1];
if ((arr[i] -'0') *10 + (arr[i+1]-'0')<27 ){
dp[i] += dp[i+2];
}
}
}
return dp[0];
}
}
八、棧內排序
請編寫一個程式,對一個棧裡的整型資料,按升序進行排序(即排序前,棧裡 的資料是無序的,排序後最大元素位於棧頂),要求最多隻能使用一個額外的
棧存放臨時資料,但不得將元素複製到別的資料結構中。
public static void sortWithStack(Stack<Integer> stack){
Stack<Integer> help = new Stack<Integer>();
help.add(stack.pop());
//將棧內元素加入輔助棧中
while (! stack.isEmpty()){
int pop = stack.pop();
while (!help.isEmpty() && pop > help.peek()){
stack.add(help.pop());
}
//保證輔助棧的底部是大元素
help.add(pop);
}
//放回stack
while (!help.isEmpty()){
stack.add(help.pop());
}
while (!stack.isEmpty()){
System.out.print(stack.pop()+" ");
}
}
九、二叉樹權值
二叉樹每個結點都有一個int型權值,給定一棵二叉樹,要求計算出從根結點到 葉結點的所有路徑中,權值和最大的值為多少。
package day12;
/**
* @Author: 郜宇博
* @Date: 2021/11/21 22:17
*/
public class TreeSum {
public static void main(String[] args) {
Node head = new Node(4);
head.left = new Node(1);
head.left.right = new Node(5);
head.right = new Node(-7);
head.right.left = new Node(3);
System.out.println(getMax(head));
}
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int val) {
value = val;
}
}
public static int getMax(Node node){
if (node == null){
return 0;
}
//葉節點
if (node.left == node && node.right == null){
return node.value;
}
//左、右節點權值
int rightSum = getMax(node.right);
int leftSum = getMax(node.left);
return Math.max(rightSum,leftSum) + node.value;
}
}