劍指Offer(1-9Java語言描述)

很快秋招就來了,今天開始刷劍指,應該不會太慢吧,之前刷了一百道leetcode,按照tag刷的,想着這個月前把劍指刷一遍,第一波,記錄下:

順便附帶亮劍經典語錄 一:
碰到我們獨立團,就是碰到了一羣野狼,在咱們眼裏,任何對手,都是我們嘴裏的一塊肉。

不說雞湯了,上題

1. 二進制中 1 的個數

題目描述
輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼錶示。
解題思路:
這道題之前leetcode也有看到,也做過。
第一種可以直接用api Integer.bitCount(n)

public class Solution {
    public int NumberOf1(int n) {
        return Integer.bitCount(n);
    }
}

第二種可以用 & 運算的性質,n & (n - 1) 去除n的二進制最低位。

public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        while (n != 0) {
            n = n & (n - 1);
            count++;
        }
        return count;
    }
}

2. 數值的整數次方

題目描述
給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。
解題思路:
自己直接的遍歷循環解法,時間複雜度O(n)


public class Solution {
    public double Power(double base, int exponent) {
        if (base == 0.0) return 0;
        if (exponent == 0) return 1;
        double temp = base;
        for (int i = 1; i < Math.abs(exponent); i++) {
            base *= temp;
        }
        return exponent > 0 ? base : 1 / base;
  }
}

大神的解法思路:
有這樣的規律。。。

當n爲偶數,a^n =(a^n/2)*(a^n/2)
當n爲奇數,a^n = a^[(n-1)/2] * a^[(n-1)/2] * a

因此 (x*x)n/2 可以通過遞歸求解,並且每次遞歸 n 都減小一半,因此整個算法的時間複雜度爲 O(logN)。

public class Solution {
    public double Power(double base, int exponent) {
        if (base == 0) return 0;
        if (exponent == 0) return 1;
        if (exponent == 1) return base;
        
        double res = Power(base * base, Math.abs(exponent / 2));
        if (exponent % 2 != 0) {
            res *= base;
        }
        return exponent > 0 ? res : 1 / res;
  }
}

3.重複的數字

題目描述:
在一個長度爲n的數組裏的所有數字都在0到n-1的範圍內。 數組中某些數字是重複的,但不知道有幾個數字是重複的。也不知道每個數字重複幾次。請找出數組中任意一個重複的數字。 例如,如果輸入長度爲7的數組{2,3,1,0,2,5,3},那麼對應的輸出是第一個重複的數字2。
解題思路:
本題可以用排序,然後判斷即可,不過如果要去時間複雜度O(n),空間複雜度O(n)的話,就不能用排序了,因此注意題目特點,數組元素在 [0, n-1] 範圍內,可以將值爲 i 的元素調整(交換)到第 i 個位置上,然後遍歷的時候判斷對應位置是否已經存在值,進行求解。

public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        if (length == 0) return false;
        for (int i = 0; i < length; i++) {
            if (numbers[i] == i) continue;
            else{    
                if (numbers[i] == numbers[numbers[i]] ) {
                    duplication[0] = numbers[i];
                    return true;
                }
                swap(numbers,i,numbers[i]);
            }
        }
        return false;
        
    }
    private void swap(int [] arr,int a ,int b){
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

4.二維數組中的查找

題目描述:
在一個二維數組中(每個一維數組的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。
解題思路:
這道題剛開始考慮二分查找,結果發現不行,後來看數組的特點,從左到右遞增,從上到下遞減,因此右下角的數字肯定最大,左上角的數字肯定最小,可以考慮從右上角或者左下角判斷與target值 得大小,然後進行移動。
這裏我從右上角開始判斷。

public class Solution {
    public boolean Find(int target, int [][] array) {
        if (array.length == 0) return false;
        int row = array.length;
        int column = array[0].length;
        int r = 0, c = column - 1; 
        while (r < row && c >= 0) {
            if (target == array[r][c]) return true;
            else if (target > array[r][c]) r++;
            else c--;
        }
        return false;
    }
}

5.替換空格爲指定字符

題目描述:
請實現一個函數,將一個字符串中的每個空格替換成“%20”。例如,當字符串爲We Are Happy.則經過替換之後的字符串爲We%20Are%20Happy。
解題思路:

  1. 其實可以使用Java一行代碼即可實現,用replace 方法。。。皮了。

  2. 也可以用StringBuffer中的append來實現,這樣就需要再創建一個Strinbuffer對象。

  3. 這裏還是老老實實從後往前遍歷,首先先定義兩個長度,temp1 和 temp2 ,當遍歷到一個空格時,在尾部填充兩個任意字符。

public class Solution {
   public String replaceSpace(StringBuffer str) {
       int temp1 = str.length() - 1;
       for (int i = 0; i <= temp1; i++) {
           if (str.charAt(i) == ' '){
               str.append("  ");	//有多少空格就加多少 "  "
           }
       }
       int temp2 = str.length() - 1;
       while (temp1 >=0 && temp2 > temp1) {
           char c = str.charAt(temp1--);
           if (c == ' '){
               str.setCharAt(temp2--,'0');
               str.setCharAt(temp2--,'2');
               str.setCharAt(temp2--,'%');
           } else {
               str.setCharAt(temp2--,c);
           }
       }
       return str.toString();
       
   }
}

6.從尾到頭打印鏈表

題目描述:
輸入一個鏈表,按鏈表值從尾到頭的順序返回一個ArrayList。
Input : 1 -> 2 -> 3
Output : 3 -> 2 -> 1

解題思路:
題目剛看,感覺就是鏈表反轉,那麼如何進行鏈表反轉呢,其實可以使用 頭插法,這裏 head 就是頭,head不存儲數據,head.next存儲反轉後的鏈表。反轉後再遍歷就是了。

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ListNode head = new ListNode(-1);
        while (listNode != null) {
            ListNode temp = listNode.next;
            listNode.next = head.next;
            head.next = listNode;
            listNode = temp;
        }
        ArrayList<Integer> list = new ArrayList();
        head = head.next;
        while (head != null) {
            list.add(head.val);
            head = head.next;
        }
        return list;
    }
}

本題也可以說用棧來實現,利用棧的特點,後進先出。

import java.util.*;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
       Stack<Integer> stack = new Stack();
        while (listNode != null) {
            stack.push(listNode.val);
            listNode = listNode.next;
        }
        ArrayList<Integer> list = new ArrayList();
        while (!stack.isEmpty() ) {
            list.add(stack.pop());
        }
        return list;
    }
}

7.重建二叉樹

題目描述:
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。

解題思路:注意二叉樹性質,如果涉及到二叉樹,應當考慮使用遞歸來解決,首先根據前序遍歷第一個值就是根節點,然後根據根結點在中序序列中的位置分割出左右兩個子序列,最後遞歸地對左子樹和右子樹分別遞歸使用同樣的方法繼續分解。
這裏注意Arrays.copyOfRange()的用法,(左閉右開)附上API:
在這裏插入圖片描述


import java.util.Arrays;
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if (pre.length == 0 || in.length == 0) return null;
        int value = pre[0];
        TreeNode root = new TreeNode(value);
        for (int i = 0; i < in.length; i++){
            if (in[i] == value) {
                root.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i));
                root.right =reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,in.length),Arrays.copyOfRange(in,i+1,in.length));
                break;
            }
        }
        return root;
    }
}

8.二叉樹的下一個結點

題目描述:
給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點並且返回。注意,樹中的結點不僅包含左右子結點,同時包含指向父結點的指針。
解題思路:由於每個節點都有指向父節點的指針,因此先求的根節點,然後再利用根節點和用鏈表存儲中序遍歷的值,然後判斷返回鏈表下一個即可,需注意是否是最後一個。

/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;
 
    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
import java.util.*;
public class Solution {
    static List<TreeLinkNode> list = new ArrayList();
    public TreeLinkNode GetNext(TreeLinkNode pNode){
        if (pNode == null) return null;
        TreeLinkNode p = pNode;
        while (pNode.next != null) {
            pNode = pNode.next;
        }
        getMidTravel(pNode);
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i) == p) {
                return (i == list.size() - 1) ? null : list.get(i+1);
            }
        }
        return null;
    }
    //中序遍歷
    private static void getMidTravel(TreeLinkNode node) {
        if (node != null) {
            getMidTravel(node.left);
            list.add(node);
            getMidTravel(node.right);
        }
         
    }
}

上述方法時間複雜度O(N),空間複雜度:O(n),下面這種可以做到空間複雜度O(n)
可以把中序下一結點歸爲幾種類型:

  • 有右子樹,下一結點是右子樹中的最左結點

  • 無右子樹,且結點是該結點父結點的左子樹,則下一結點是該結點的父結點

  • 無右子樹,且結點是該結點父結點的右子樹,則我們一直沿着父結點追朔,直到找到某個結點是其父結點的左子樹,如果存在這樣的結點,那麼這個結點的父結點就是我們要找的下一結點。

/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode){
        if (pNode == null) return null;
        if (pNode.right != null) {
            TreeLinkNode temp = pNode.right;
            if (temp.left != null) {
                return temp.left;
            }
            return temp;
        }else {
            while (pNode.next != null) {
                TreeLinkNode temp = pNode.next;
                if (temp.left == pNode) return temp;
                pNode = pNode.next;
            }
        }
        return null;
        
    }
}

9. 用兩個棧實現隊列

題目描述
用兩個棧來實現一個隊列,完成隊列的Push和Pop操作。 隊列中的元素爲int類型。

解題思路:之前在LeetCode刷過類似題目,當時還寫了一篇文章LeetCode中的棧和隊列的問題,這裏使用兩個棧,利用棧後進先出特點和隊列先進先出特點進行解決即可。需要注意的就是當第二個棧stack2爲空時才往裏面push。

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        stack1.push(node);
	    
    }   
    public int pop() {
    //當第二個棧stack2爲空時才往裏面push。
        while (stack2.empty()){
            while (!stack1.empty()){
		        stack2.push(stack1.pop());
	        }
        }     
        return stack2.pop();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章