劍指offer-第一週

劍指offer 第一週

acwing-13.找出數組中重複的數字

給定一個長度爲 nn 的整數數組 nums,數組中所有的數字都在0∼n−1 的範圍內。

數組中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。

請找出數組中任意一個重複的數字。

注意:如果某些數字不在0∼n−1 的範圍內,或數組中不包含重複數字,則返回 -1;

樣例

給定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。

利用Set存儲;

set 存儲時間複雜度O(1),所以此題的時間複雜度是O(n),空間複雜度是O(n);

class Solution {
    public int duplicateInArray(int[] nums) {
        Set<Integer> set = new HashSet<>();
        int flag = -1;
        for(int num : nums){
            if(num < 0 || num >= nums.length)return -1;
            if(!set.add(num))flag = num;
        }
        return flag != -1 ? flag : -1;
    }
}

利用原數組nums[] 求解

由於while()最多循環n次,故總時間複雜度爲O(3*n)= O(n),空間複雜度O(1);

class Solution {
    public int duplicateInArray(int[] nums) {
        int n = nums.length;
        for(int num : nums)
            if(num < 0 || num >= n)
                return -1;
                
        for(int i = 0; i < n; i ++){
            while(nums[i] != nums[nums[i]])swap(nums,i,nums[i]);
            if(i != nums[i])return nums[i];
        }
        return -1;
    }
    
    public void swap(int[] nums, int x,int y){
        int temp = nums[x];
        nums[x] = nums[y];
        nums[y] = temp;
    }
}

acwing-14.不修改數組找出重複的數字

給定一個長度爲 n+1 的數組nums,數組中所有的數均在 1∼n 的範圍內,其中n≥1。

請找出數組中任意一個重複的數,但不能修改輸入的數組。

樣例

給定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。

思考題:如果只能使用 O(1) 的額外空間,該怎麼做呢?

既然不能修改輸入的數組,也不可以額外開闢空間,故用二分尋找

class Solution {
    public int duplicateInArray(int[] nums) {
        int n = nums.length;
        for(int i = 0; i < n; i ++){
            if(binarySearch(nums, nums[i],i)) return nums[i];
        }
        return -1;
    }
    public boolean binarySearch(int[] nums, int target,int start){
        int l = start + 1, r = nums.length - 1;
        while(l < r){
            int mid = l + r >>> 1;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        return nums[r] == target;
    }
}

利用抽屜原理求解: 因爲每個數的範圍在1~n,總數有n+1個,故必定有個位置的數量大於1;

分治:判斷左半邊和右半邊,如果在l~mid的數的個數大於 mid 則代表左邊有重複的數,反之右邊

class Solution {
    public int duplicateInArray(int[] nums) {
        int n = nums.length;
        int l = 1, r = n - 1;
        while(l < r){
            int mid = l + r >>> 1, s = 0;
            for(int x : nums){
                if(x >= l && x <= mid)s ++;
            }
            if(s > mid - l + 1)r = mid;
            else l = mid + 1;
        }
        return r;
    }
}

acwing-15.二維數組中的查找

在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。

請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

樣例

輸入數組:

[
  [1,2,8,9],
  [2,4,9,12],
  [4,7,10,13],
  [6,8,11,15]
]

如果輸入查找數值爲7,則返回true,

如果輸入查找數值爲5,則返回false。

不斷循環,利用二分查找,時間複雜度O(nlogn),空間複雜度O(1);

class Solution {
    public boolean searchArray(int[][] array, int target) {
        for(int i = 0; i < array.length; i ++){
            int len = array[i].length;
            if(array[i][0] <= target && array[i][len - 1] >= target && binarySearch(array[i],target))return true;
        }
        return false;
    }
    public boolean binarySearch(int[] nums, int target){
        int l = 0, r = nums.length - 1;
        while(l < r){
            int mid = l + r >>> 1;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        return nums[r] == target;
    }
}

O(n+m) 做法

此題有個規律,就是當前數左邊的數都比 此數要小,下面的數都比次數要大。所以可以每次判斷下target比當前數大還是小.

大的話 往下走,小的話往左走。

class Solution {
    public boolean searchArray(int[][] array, int target) {
        if(array.length <= 0)return false;
        int len = array[0].length;
        int x = 0, y  = len - 1;
        while(x < len && y >= 0 && array[x][y] != target){
            if(target < array[x][y]) y--;
            else x ++;
        }
        if(x >= len || y < 0)return false;
        return array[x][y] == target;
    }
}

16.替換空格

請實現一個函數,把字符串中的每個空格替換成"%20"

你可以假定輸入字符串的長度最大是1000。
注意輸出字符串的長度可能大於1000。

樣例

輸入:"We are happy."

輸出:"We%20are%20happy."

java的庫函數實現 – 一行代碼

class Solution {
    public String replaceSpaces(StringBuffer str) {
        return str.toString().replaceAll(" ","%20");
    }
}

17.從頭到尾打印鏈表

輸入一個鏈表的頭結點,按照 從尾到頭 的順序返回節點的值。

返回的結果用數組存儲。

樣例

輸入:[2, 3, 5]
返回:[5, 3, 2]
class Solution {
    public int[] printListReversingly(ListNode head) {
        List<Integer> res = new ArrayList<>();
        while(head != null){
            res.add(head.val);
            head = head.next;
        }
        int n = res.size();
        int[] ans = new int[n];
        for(int i = res.size() - 1; i >= 0 ; i --){
            ans[n - i - 1] = res.get(i);
        }
        return ans;
    }
}

18.重建二叉樹

輸入一棵二叉樹前序遍歷和中序遍歷的結果,請重建該二叉樹。

注意:

  • 二叉樹中每個節點的值都互不相同;
  • 輸入的前序遍歷和中序遍歷一定合法;

樣例

給定:
前序遍歷是:[3, 9, 20, 15, 7]
中序遍歷是:[9, 3, 15, 20, 7]

返回:[3, 9, 20, null, null, 15, 7, null, null, null, null]
返回的二叉樹如下所示:
    3
   / \
  9  20
    /  \
   15   7

利用遞歸,前序的第一個點,肯定是根,然後在中序遍歷中找到該點k,則可以知道左子樹的節點個數爲 k - il(il 爲 中序遍歷數組的起點) 將數組分爲左右兩段。

class Solution {
    int[] p,i;
    Map<Integer,Integer> map;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        p = preorder;i = inorder;
        map = new HashMap<>();
        for(int i = 0 ; i < inorder.length; i ++){
            map.put(inorder[i],i);
        }
        return dfs(0,p.length - 1, 0, i.length - 1);
    }
    // pl pr 前序遍歷 的下標範圍,il ir 中序遍歷的下標範圍
    public TreeNode dfs(int pl,int pr, int il,int ir){
        if(pl > pr)return null;
        // 前序遍歷的第一個節點是根節點
        TreeNode root = new TreeNode(p[pl]);
        // 找到該根節點在 中序遍歷的位置
        int k = map.get(p[pl]);
        // 左子樹 的下標範圍 變成, pl + 1, pl + (k - il) --- 中序遍歷中得到左子樹的數量爲(k - il),
        root.left = dfs(pl + 1, pl + k - il, il, k - 1);
        // 同理
        root.right = dfs(pl + k - il + 1, pr,k + 1,ir);
        return root;
    }
}

19.二叉樹的下一個節點

給定一棵二叉樹的其中一個節點,請找出中序遍歷序列的下一個節點。

注意:

  • 如果給定的節點是中序遍歷序列的最後一個,則返回空節點;
  • 二叉樹一定不爲空,且給定的節點一定不是空節點;

樣例

假定二叉樹是:[2, 1, 3, null, null, null, null], 給出的是值等於2的節點。

則應返回值等於3的節點。

解釋:該二叉樹的結構如下,2的後繼節點是3。
  2
 / \
1   3
class Solution {
    public TreeNode inorderSuccessor(TreeNode p) {
        // 當有 右子樹的時候,返回右子樹的最左邊,如果沒有左子樹,即爲本身
        if(p.right != null){
            p = p.right;
            while(p.left != null)p = p.left;
            return p;
        }
        
        // 當沒有 右子樹的時候,返回其節點是父親的 左子樹節點的父親
        while(p.father != null && p == p.father.right)p = p.father;
        return p.father;
    }
}

20.用兩個棧實現隊列

請用棧實現一個隊列,支持如下四種操作:

  • push(x) – 將元素x插到隊尾;
  • pop() – 將隊首的元素彈出,並返回該元素;
  • peek() – 返回隊首元素;
  • empty() – 返回隊列是否爲空;

注意:

  • 你只能使用棧的標準操作:push to toppeek/pop from top, sizeis empty
  • 如果你選擇的編程語言沒有棧的標準庫,你可以使用list或者deque等模擬棧的操作;
  • 輸入數據保證合法,例如,在隊列爲空時,不會進行pop或者peek等操作;

樣例

MyQueue queue = new MyQueue();

queue.push(1);
queue.push(2);
queue.peek();  // returns 1
queue.pop();   // returns 1
queue.empty(); // returns false

棧的反方向就是隊列,當要pop的時候,將棧A的值,pop給棧B,此時棧B的top就是 隊首了。peek() 同理

class MyQueue {
    Stack<Integer> st1;
    Stack<Integer> st2;
    /** Initialize your data structure here. */
    public MyQueue() {
        st1 = new Stack<>();
        st2 = new Stack<>();
    }
    
    /** Push element x to the back of queue. */
    public void push(int x) {
        if(!st2.isEmpty()){
            while(!st2.isEmpty()){
                st1.push(st2.pop());
            }
        }
        st1.push(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
        while(!st1.isEmpty()){
            st2.push(st1.pop());
        }
        return st2.pop();
    }
    
    /** Get the front element. */
    public int peek() {
        if(!st2.isEmpty())return st2.peek();
        else{
            while(!st1.isEmpty()){
                st2.push(st1.pop());
            }
            return st2.peek();
        }
    }
    
    /** Returns whether the queue is empty. */
    public boolean empty() {
        if(st1.isEmpty() && st2.isEmpty())return true;
        return false;
    }
}

21.斐波那契數列

輸入一個整數 nn ,求斐波那契數列的第 nn 項。

假定從0開始,第0項爲0。(nn<=39)

樣例

輸入整數 n=5 

返回 5

cur0存儲前前個數,cur0存儲前一個數

class Solution {
    public int Fibonacci(int n) {
        if(n == 1)return 1;
        if(n == 0)return 0;
        int cur0 = 1, cur1= 1,ans = 0;
        for(int i = 2; i < n; i ++){
            ans = cur0 + cur1;
            cur0 = cur1;
            cur1 = ans;
        }
        return ans;
    }
}

22.旋轉數組的最小數字

把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。

輸入一個升序的數組的一個旋轉,輸出旋轉數組的最小元素。

例如數組{3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。

數組可能包含重複項。

注意:數組內所含元素非負,若數組大小爲0,請返回-1。

樣例

輸入:nums=[2,2,2,0,1]

輸出:0

題意:將一個升序數組,後半部分,放到前面來,求找出最小的元素;

題解: 先保證後部分的數都小於前部分,然後二分找。

class Solution {
    public int findMin(int[] nums) {
        int n = nums.length - 1;
        if(n == -1)return -1;
        while(nums[n] == nums[0])n--;
        if(nums[n] >= nums[0])return nums[0];
        int l = 0, r = n;
        
        while(l < r){
            int mid = l + r >>> 1;
            if(nums[mid] >= nums[0]) l = mid + 1;
            else r = mid;
        }
        return nums[l];
    }
}

23.矩陣中的路徑

請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。

路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。

如果一條路徑經過了矩陣中的某一個格子,則之後不能再次進入這個格子。

注意:

  • 輸入的路徑不爲空;
  • 所有出現的字符均爲大寫英文字母;

樣例

matrix=
[
  ["A","B","C","E"],
  ["S","F","C","S"],
  ["A","D","E","E"]
]

str="BCCE" , return "true" 

str="ASAE" , return "false"

dfs 尋找路徑,記得還原標記。

還原與不還原區別: 其他地方到此點時是否有區別,有區別就還原,沒區別,還原個屁

class Solution {
    int[] dx = {0,0,1,-1}, dy = {1,-1,0,0};
    public boolean dfs(int x,int y,int len, String str,char[][] matrix){
        if(str.charAt(len) != matrix[x][y])return false;
        if(len + 1 == str.length()) return true;
        char t = matrix[x][y];
        matrix[x][y] = '#';
        for(int i = 0; i < 4; i ++){
            int new_x = x + dx[i];
            int new_y = y + dy[i];
            if(new_x >= 0 && new_x < matrix.length && new_y >= 0 && new_y < matrix[0].length){
                if(dfs(new_x,new_y,len + 1,str,matrix))return true;
            }
        }
        matrix[x][y] = t;
        return false;
    }
    public boolean hasPath(char[][] matrix, String str) {
        for(int i = 0; i < matrix.length; i++)
            for(int j = 0 ; j < matrix[i].length; j++)
                if(dfs(i,j,0,str,matrix))return true;
                
        return false;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章