【精選】JAVA入門演算法題(四)
把敬業變成習慣。短期來看是為了僱主,長期來看是為了自己。
1.題目:全排列問題
這種問題在演算法題中應用很多,主要思路是使用遞迴來求,求n個數的全排列就是把第一個數固定後求n-1個數的全排列,不斷遞迴到只有一個數
private static void Method1() { disorder(array,0,array.length); } private static void disorder(int array[],int m,int n){ if (m==n){ for (int i=0;i<n;i++){ System.out.print(array[i]); } System.out.println(); return; }else { for (int i=m;i<n;i++){ swap(array,m,i); disorder(array,m+1,n); swap(array,m,i); } } } private static void swap(int[] array,int m,int n){ int temp=array[m]; array[m]=array[n]; array[n]=temp; }
2.題目:紙牌三角形
A,2,3,4,5,6,7,8,9 共9張紙牌排成一個正三角形(A按1計算)。要求每個邊的和相等。
下圖就是一種排法
A
9 6
4 8
3 7 5 2
這樣的排法可能會有很多。
如果考慮旋轉、映象後相同的算同一種,一共有多少種不同的排法呢?
請你計算並提交該數字。
這道題就是一個典型的全排列問題,但題目有要求說旋轉、映象算一種,那麼你就要在最後的結果除以6,因為正三角旋轉有3種情況,映象有兩種情況
static int[] first = new int[9]; static int[] s = new int[9]; static int sum = 0; private static void Method1() { seek(0); System.out.println(sum / 6);//6是因為有3種旋轉2種翻轉 } private static void seek(int code) { if (code == 9) { if (s[0] + s[1] + s[3] + s[5] == s[0] + s[2] + s[4] + s[8] && s[0] + s[1] + s[3] + s[5] == s[5] + s[6] + s[7] + s[8]) sum++; return; } for (int i = 0; i < 9; i++) { if (first[i] == 0) { first[i] = 1; s[code] = i + 1; seek(code + 1); first[i] = 0; } } }
static int num = 0; private static void Method2() { int a[] = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9}; f1(a, 0); System.out.println(num / 6); } public static void f1(int a[], int k) { int q, w, e; if (k == a.length - 1) { q = a[0] + a[1] + a[2] + a[3]; w = a[3] + a[4] + a[5] + a[6]; e = a[6] + a[7] + a[8] + a[0]; if (q == w && w == e) num++; } for (int i = k; i < a.length; i++) { System.out.println(i+" "+k); { int temp = a[i]; a[i] = a[k]; a[k] = temp; } f1(a, k + 1); { int temp = a[i]; a[i] = a[k]; a[k] = temp; } } }
3.題目:給定一個整數陣列 nums 和一個目標值 target,請你在該陣列中找出和為目標值的 兩個 整數。你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個陣列中同樣的元素。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
如果不可以組成目標數字則返回[-1,-1]
題目非常簡單,只要使用for迴圈就能搞定
private static int[] getSum(int[] nums, int target) {
for (int i=0;i<nums.length-1;i++){
for (int j=i+1;j<nums.length;j++){
if (nums[i]+nums[j]==target){
System.out.println(i+" "+j);
return new int[]{i,j};
}
}
}
return new int[]{-1,-1};
}
但是你會覺得兩層太麻煩了,一定有辦法一遍完事。這樣用到了hashmap,這種資料資料查詢十分快,是一種用控制元件換時間的資料結構,因為如果存在兩個滿足條件的數的話,當迴圈到第二個數的時候便能打印出來,這樣複雜度便降到了最低了。
public static int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
System.out.println(map.get(complement)+" "+i);
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
4.兩數之和(二叉樹)
/**
* 給定一個二叉搜尋樹和一個目標結果,如果 BST 中存在兩個元素且它們的和等於給定的目標結果,則返回 true。
* 輸入:
* 5
* / \
* 3 6
* / \ \
* 2 4 7
*
* Target = 9
*
* 輸出: True
*/
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
}
現在找尋兩個數字已經從陣列轉換成了二叉樹,但是你可以使用類似的辦法,將二叉樹遍歷新增到陣列中或者列表中,然後進行查詢
private static List<Integer> integers=new ArrayList<>();
private static boolean getSum(TreeNode root, int k) {
ergodic(root);
if(integers.size()==1){
if(integers.get(0)==k) return true;
else return false;
}
for (int i=0;i<integers.size()-1;i++){
for (int j=i+1;j<integers.size();j++){
if (integers.get(i)+integers.get(j)==k){
return true;
}
}
}
return false;
}
private static void ergodic(TreeNode root) {
if (root.left!=null){
ergodic(root.left);
}
integers.add(root.val);
if (root.right!=null){
ergodic(root.right);
}
}
5.數字反轉
/**
* 給出一個 32 位的有符號整數,你需要將這個整數中每位上的數字進行反轉。
* 示例:
* 輸入: 123
* 輸出: 321
*
* 輸入: -123
* 輸出: -321
*
* 輸入: 120
* 輸出: 21
*/
這道題有兩個點需要注意,一是要注意負數的反轉,二是要注意int型別的範圍問題,比如-2147483412
兩種解法,一種是將大數分割成兩個數進行轉換,因為Integer.parseInt()的範圍比較小,超出範圍便會出錯,另一種是藉助long型別
public static int reverse(int x) {
if (x>=0){
char[] chars=(x+"").toCharArray();
for (int i=0;i<chars.length/2;i++){
char c=chars[i];
chars[i]=chars[chars.length-i-1];
chars[chars.length-i-1]=c;
}
String s=new String(chars);
if (s.length()>=10){
String a=s.substring(0,s.length()/2);
String b=s.substring(s.length()/2);
double result=Integer.parseInt(a)*Math.pow(10,b.length())+Integer.parseInt(b);
int res=(int)result;
if (res==Integer.MAX_VALUE){
return 0;
}
return res;
}else {
return Integer.parseInt(s);
}
}else {
char[] chars=(x+"").toCharArray();
for (int i=1;i<=chars.length/2;i++){
char c=chars[i];
chars[i]=chars[chars.length-i];
chars[chars.length-i]=c;
}
String s=new String(chars);
if (s.length()>=10){
String a=s.substring(1,s.length()/2);
String b=s.substring(s.length()/2);
System.out.println(a+" "+b+" "+s.length()/2);
double result=Integer.parseInt(a)*Math.pow(10,b.length())+Integer.parseInt(b);
System.out.println(result);
int res=(int)-result;
if (res==Integer.MIN_VALUE){
return 0;
}
return res;
}else {
return Integer.parseInt(s);
}
}
public static int reverse(int x) {
long z = x;
String str = String.valueOf(Math.abs(z));
StringBuilder conStr = new StringBuilder();
int len = str.length();
while( len > 0){
conStr.append(str.charAt(len - 1));
len--;
}
Long l = Long.parseLong(conStr.toString());
if(l > Integer.MAX_VALUE){
return 0;
}
if(x >= 0)
return l.intValue();
else{
return -l.intValue();
}
}
但是你還是不滿意,你覺得完全沒必要這麼麻煩,只要迴圈取個位數然後拼接到另一個數的個位數上就可以了嘛,然後中間放一個判斷溢位的,怎麼做呢
pop = x % 10;//取出來個位數
x /= 10;//把十位數變成個位數
temp = rev * 10 + pop;//拼接到另一個數上
rev = temp;
//迴圈此操作
那怎麼判斷是否溢位呢?如果temp=rev*10+pop溢位,那麼rev>=IntMax/10,然後我們分情況進行處理
如果rev>IntMax/10,temp=rev*10+pop一定溢位
如果rev=IntMax/10,pop大於7才溢位
把邏輯寫成程式
public int reverse(int x) {
int rev = 0;
while (x != 0) {
int pop = x % 10;
x /= 10;
if (rev > Integer.MAX_VALUE/10 || (rev == Integer.MAX_VALUE / 10 && pop > 7)) return 0;
if (rev < Integer.MIN_VALUE/10 || (rev == Integer.MIN_VALUE / 10 && pop < -8)) return 0;
rev = rev * 10 + pop;
}
return rev;
}
漂亮!