劍指offer題解

數組中重複的數字java

題目描述:在一個長度爲n的數組裏面的全部數字都在0~n-1的範圍內。數組中某些數字是重複的,可是不知道有幾個數字重複了,也不知道每一個數字重複了幾回,請找出數組中任意一個重複的數字。例如,若是輸入長度爲7的數組{2,3,1,0,2,5,3},那麼對應的輸出是重複的數字2或者3.node

解題思路:git

1.先把輸入的數組排序,從排序的數組中找出重複的數字是一件很容易的事,只需從頭至尾掃描排序後的數組就能夠了。排序一個長度爲n的數組須要O(nlogn)的時間。github

2.能夠利用哈希表來解決,從頭至尾按順序掃描數組的每一個數字,每掃描到一個數字的時候,均可以用O(1)的時間判斷哈希表裏面是否已經包含了該數字。若是哈希表裏面尚未這個數字,就把他加入哈希表,若是已經存在了該數字,就找到一個重複的數字,算法的時間複雜度是O(n),空間複雜度爲O(n),面試

3.假設重排這個數組,若是數組中沒有重複的數字,那麼當數組排序後,數字i將出如今下標爲i的位置。因爲數組中有重複的數字,那有些位置可能存在多個數字,有些位置可能沒有數字。算法

如今重排這個數組,從頭打到尾依次掃描這個數組中的每一個數字。當掃描到下標爲i的數字時,首先比較這個數字m是否是等於i,若是是,則接着掃描下一個數字;若是不是,那麼拿它和第m個數字進行比較。若是它和第m個數字相等,就找到了一個重複的數字;若是它和第m個數字不相等,就把第i個數字和第m個數字交換,把m放在屬於它的位置,接下來重複這個比較交換的過程,直到發現第一個重複數字。數組

 

以 (2, 3, 1, 0, 2, 5) 爲例:app

 

position-0 : (2,3,1,0,2,5) // 2 <-> 1 (1,3,2,0,2,5) // 1 <-> 3 (3,1,2,0,2,5) // 3 <-> 0 (0,1,2,3,2,5) // already in position position-1 : (0,1,2,3,2,5) // already in position position-2 : (0,1,2,3,2,5) // already in position position-3 : (0,1,2,3,2,5) // already in position position-4 : (0,1,2,3,2,5) // nums[i] == nums[nums[i]], exit

 

遍歷到位置 4 時,該位置上的數爲 2,可是第 2 個位置上已經有一個 2 的值了,所以能夠知道 2 重複。ide

例題:函數

題目描述
在一個長度爲n的數組裏的全部數字都在0到n-1的範圍內。 數組中某些數字是重複的,但不知道有幾個數字是重複的。也不知道每一個數字重複幾回。請找出數組中任意一個重複的數字。 例如,若是輸入長度爲7的數組{2,3,1,0,2,5,3},那麼對應的輸出是第一個重複的數字2。
public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    這裏要特別注意~返回任意重複的一個,賦值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        if (numbers == null || numbers.length == 0) {
            return false;
        }
        for (int i = 0; i < length; i++) {
            while (numbers[i] != i) {
                if (numbers[i] == numbers[numbers[i]]) {
                    duplication[0] = numbers[i];
                    return true;
                }
                swap(numbers, i, numbers[i]);
            }
        }
        return false;
    }
    public void swap(int[] numbers, int i, int j) {
        int temp = numbers[i];
        numbers[i] = numbers[j];
        numbers[j] = temp;
    }
}

 

-----------------------------------------------------------------------

不修改數組找出重複的數字

題目描述:在一個長度爲n+1的數組裏面的全部數字都在1~n的範圍內,因此數組中至少有一個數字是重複的,請找出數組當中任意一個重複的數字,但不能修改輸入的數組。例如輸入長度爲8的數組{2,3,5,4,3,2,6,7},那麼對應的輸出是重複的數字2或者3。

解題思路:

1.能夠建立一個長度爲n+1上午輔助數組,而後逐一把原數組的每一個數字複製到輔助數組。若是原數組中被複制的數字是m,則把它複製到輔助數組中下標爲m的位置,這樣很容易就能發現哪一個數字是重複的,這樣須要O(n)的輔助空間。

2.能夠把1~n的數字從中間的數字m分爲兩部分,前面一半爲1~m,後面一半爲m+1~n。若是1~m的數字的數目超過m,那麼這一版的區間裏面必定包含重複數字,不然另外一半區間裏面確定包含重複數字。咱們能夠繼續吧包含重複數字的區間一分爲二,直到找到一個重複的數字,相似二分法。

例題:

633. 尋找重複的數
給出一個數組 nums 包含 n + 1 個整數,每一個整數是從 1 到 n (包括邊界),保證至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。

樣例
給出 nums = [5,5,4,3,2,1],返回 5.
給出 nums = [5,4,4,3,2,1],返回 4.

注意事項
1.不能修改數組(假設數組只能讀)
2.只能用額外的O(1)的空間
3.時間複雜度小於O(n^2)
4.數組中只有一個重複的數,但可能重複超過一次

解法:

public class Solution {
    /**
     * @param nums: an array containing n + 1 integers which is between 1 and n
     * @return: the duplicate one
     */
    public int findDuplicate(int[] nums) {
        // write your code here
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 1;
        int end = nums.length - 1;
        while (end >= start) {
            int mid = ((end - start) >> 1) + start;
            int count = countRange(nums, start, mid);
            if (end == start) {
                if (count > 1) {
                    return start;
                } else {
                    break;
                }
            }
            //前半部分數字在數組中出現的次數大於前半部分數字的個數
            if (count > (mid - start + 1)) {
                end = mid;
            } else {
                start = mid + 1;
            }
        }
        return -1;
    }
    /**
     * 統計nums數組中某個範圍當中的數字出現的次數
     **/
    public int countRange(int[] nums, int start, int end) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] >= start && nums[i] <= end) {
                count++;
            }
        }
        return count;
    }
}

 

-------------------------------------------------------------------------------------

 二維數組中的查找

在一個二維數組中(每一個一維數組的長度相同),每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。
請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。
Consider the following matrix:
[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

Given target = 5, return true.
Given target = 20, return false.
public class Solution {
    /**
     * 從左下角開始查找,複雜度:O(M + N) + O(1)
     **/
    public boolean Find(int target, int [][] array) {
        if (array == null || array.length == 0 || array[0] == null || array[0].length == 0) {
            return false;
        }
        int row = array.length;
        int col = array[0].length;
        int i = row - 1;
        int j = 0;
        while (i >= 0 && j < col) {
            if (target == array[i][j]) {
                return true;
            } else if (target > array[i][j]) {
                j++;
            } else {
                i--;
            }
        }
        return false;

    }
}

 

-------------------------------------------

替換空格

題目描述
請實現一個函數,將一個字符串中的每一個空格替換成「%20」。例如,當字符串爲We Are Happy.則通過替換以後的字符串爲We%20Are%20Happy。
解題思路
在字符串尾部填充任意字符,使得字符串的長度等於替換以後的長度。由於一個空格要替換成三個字符(%20),所以當遍歷到一個空格時,須要在尾部填充兩個任意字符。

令 P1 指向字符串原來的末尾位置,P2 指向字符串如今的末尾位置。P1 和 P2從後向前遍歷,當 P1 遍歷到一個空格時,就須要令 P2 指向的位置依次填充 02%(注意是逆序的),不然就填充上 P1 指向字符的值。

從後向前遍是爲了在改變 P2 所指向的內容時,不會影響到 P1 遍歷原來字符串的內容。
public class Solution {
    public String replaceSpace(StringBuffer str) {
        int oldLen = str.length() - 1;
        for (int i = 0; i < oldLen + 1; i++) {
            if (str.charAt(i) == ' ') {
                str.append("  ");
            }
        }
        int newLen = str.length() - 1;
        while (oldLen >= 0 && newLen > oldLen) {
            char temp = str.charAt(oldLen--);
            if (temp == ' ') {
                str.setCharAt(newLen--, '0');
                str.setCharAt(newLen--, '2');
                str.setCharAt(newLen--, '%');
            } else {
                str.setCharAt(newLen--, temp);
            }
        }
        return str.toString();
    }
}

 

--------------------------------------------------------------------

從頭至尾打印鏈表

題目描述
輸入一個鏈表,按鏈表值從尾到頭的順序返回一個ArrayList。
/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
import java.util.Stack;
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> res = new ArrayList<>();
        while (!stack.isEmpty()) {
            res.add(stack.pop());
        }
        return res;
    }
}

 

-----------------------------------------------------------------------------------------

重建二叉樹

根據前序遍歷和中序遍歷重建二叉樹

 

題目描述
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。
例如輸入前序遍歷序列{
1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。
解題思路
前序遍歷的第一個值爲根節點的值,使用這個值將中序遍歷結果分紅兩部分,左部分爲樹的左子樹中序遍歷結果,右部分爲樹的右子樹中序遍歷的結果。
遞歸的調用,實現功能。
/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
import java.util.Map;
import java.util.HashMap;
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < in.length; i++) {
            map.put(in[i], i);
        }
        TreeNode root = helper(pre, 0, pre.length - 1, in, 0, in.length - 1, map);
        return root;
    }
    public TreeNode helper(int[] pre, int preStart, int preEnd, int[] in, int inStart, int inEnd, Map<Integer, Integer> inMap) {
        if (preStart > preEnd || inStart > inEnd) {
            return null;
        }
        TreeNode root = new TreeNode(pre[preStart]);
        int index = inMap.get(root.val);
        int leftNums = index - inStart;
        root.left = helper(pre, preStart + 1, preStart + leftNums, in, inStart, index - 1, inMap);
        root.right = helper(pre, preStart + leftNums + 1, preEnd, in, index + 1, inEnd, inMap);
        return root;
    }
}

 

根據後序遍歷和中序遍歷重建二叉樹

 

106. Construct Binary Tree from Inorder and Postorder Traversal


Given inorder and postorder traversal of a tree, construct the binary tree. Note: You may assume that duplicates
do not exist in the tree. For example, given inorder = [9,3,15,20,7] postorder = [9,15,7,20,3] Return the following binary tree: 3 / \ 9 20 / \ 15 7
解題思路
後序遍歷的最後一個值爲根節點的值,使用這個值將中序遍歷結果分紅兩部分,左部分爲樹的左子樹中序遍歷結果,右部分爲樹的右子樹中序遍歷的結果。
遞歸的調用,實現功能。
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        if (inorder == null || postorder == null || inorder.length != postorder.length) {
            return null;
        }
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < inorder.length; i++) {
            map.put(inorder[i], i);
        }
        TreeNode root = helper(inorder, 0, inorder.length - 1, postorder, 0, postorder.length - 1, map);
        return root;
    }
    public TreeNode helper(int[] inorder, int inStart, int inEnd, int[] postorder, int postStart, int postEnd, Map<Integer, Integer> map)     {
        if (inStart > inEnd || postStart > postEnd) {
            return null;
        }
        TreeNode root = new TreeNode(postorder[postEnd]);
        int index = map.get(root.val);
        int leftNums = index - inStart;
        root.left = helper(inorder, inStart, index - 1, postorder, postStart, postStart + leftNums - 1, map);
        root.right = helper(inorder, index + 1, inEnd, postorder, postStart + leftNums, postEnd - 1, map);
        return root;
    }
}

 

----------------------------------------------------------------

 二叉樹的下一個節點

題目描述
給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點而且返回。注意,樹中的結點不只包含左右子結點,同時包含指向父結點的指針。

解題思路

① 若是一個節點的右子樹不爲空,那麼該節點的下一個節點是右子樹的最左節點;

 

② 不然,向上找第一個左連接指向的樹包含該節點的祖先節點。

 
/*
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 node = pNode.right;
            while (node.left != null) {
                node = node.left;
            }
            return node;
        } else {
            while (pNode.next != null) {
                TreeLinkNode p = pNode.next;
                if (p.left == pNode) {
                    return p;
                }
                pNode = pNode.next;
            }
        }
        return null;
    }
}

 

----------------------------------------------------------------------------------------------------------

 兩個棧實現隊列

題目描述
用兩個棧來實現一個隊列,完成隊列的Push和Pop操做。 隊列中的元素爲int類型。
解題思路:
聲明兩個棧,stack1,stack2
進入隊列的時候,都push到stack1中
從隊列中出去的時候,從stack2中pop,若是stack2爲空,
那麼將stack1中的元素pop後壓入stack2中,若是stack2仍是爲空,
那麼拋出異常。

 

 
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() throws Exception{
        if (stack2.isEmpty()) {
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        if (stack2.isEmpty()) {
            throw new Exception("queue is empty");
        }
        return stack2.pop();
    
    }
}

-----------------------------------------------------------------------------

兩個隊列實現棧

 

import java.util.ArrayDeque;
import java.util.Queue;

public class Demo08 {
    Queue<Integer> queue1 = new ArrayDeque<>();
    Queue<Integer> queue2 = new ArrayDeque<>();

    public void push(int node) {
        //兩個棧都爲空時,優先考慮queue1
        if (queue1.isEmpty()&&queue2.isEmpty()) {
            queue1.add(node);
            return;
        }

        //若是queue1爲空,queue2有元素,直接放入queue2
        if (queue1.isEmpty()) {
            queue2.add(node);
            return;
        }

        if (queue2.isEmpty()) {
            queue1.add(node);
            return;
        }

    }

    public int pop() {
        //兩個棧都爲空時,沒有元素能夠彈出
        if (queue1.isEmpty()&&queue2.isEmpty()) {
            try {
                throw new Exception("stack is empty");
            } catch (Exception e) {
            }
        }
        //若是queue1爲空,queue2有元素, 將queue2的元素依次放入queue1中,直到最後一個元素,咱們彈出。
        if (queue1.isEmpty()) {
            while (queue2.size()>1) {
                queue1.add(queue2.poll());
            }
            return queue2.poll();
        }

        if (queue2.isEmpty()) {
            while (queue1.size()>1) {
                queue2.add(queue1.poll());
            }
            return queue1.poll();
        }

        return (Integer) null;
    }

    public static void main(String[] args) {
        Demo08 demo08 = new Demo08();
        demo08.push(1);
        demo08.push(2);
        demo08.push(3);
        demo08.push(4);
        System.out.println(demo08.pop());
        System.out.println(demo08.pop());
        demo08.push(5);
        System.out.println(demo08.pop());
        System.out.println(demo08.pop());
        System.out.println(demo08.pop());
    }
}

-----------------------------------------------------------------------------------

菲波那切數列

題目描述
你們都知道斐波那契數列,如今要求輸入一個整數n,請你輸出斐波那契數列的第n項(從0開始,第0項爲0)。
n<=39

遞歸解法:

public class Solution {
    public int Fibonacci(int n) {
        if (n <= 1) {
            return n;
        }
        int[] f = new int[n + 1];
        f[1] = 1;
        for (int i = 2; i <= n; i++) {
            f[i] = f[i - 1] + f[i - 2];
        }
        return f[n];

    }
}

非遞歸解法:

public class Solution {
    public int Fibonacci(int n) {
        if (n <= 1) {
            return n;
        }
        //考慮到第 i 項只與第 i-1 和第 i-2 項有關,
        //所以只須要存儲前兩項的值就能求解第 i 項,
        //從而將空間複雜度由 O(N) 下降爲 O(1)。
        int pre = 0;
        int p = 1;
        int res = 0;
        for (int i = 2; i <= n; i++) {
            res = pre + p;
            pre = p;
            p = res;
        }
        return res;

    }
}

-----------------------------------------------------------------------------

青蛙跳臺階

題目描述
一隻青蛙一次能夠跳上1級臺階,也能夠跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法(前後次序不一樣算不一樣的結果)。
解題思路:
首先考慮最簡答的狀況,若是隻有一級臺階,那麼只有一種跳法
若是有兩級臺階,那麼就有兩種跳法
咱們把n級臺階時的跳法當作n的函數,即爲f(n),
當n>2時,第一次跳的時候就有兩種不一樣的選擇:
第一種是第一次只跳一級,此時跳法數目等於後面剩下的n-1級臺階的跳法數目,即爲f(n-1)
第二種是第一次跳兩級,此時的跳法數目等於後面剩下的n-2級臺階的跳法數目,即爲f(n-2)
所以,n級臺階的不一樣跳法的總數爲f(n)=f(n-1)+f(n-2)。
public class Solution {
    public int JumpFloor(int target) {
        if (target <= 0) {
            return 0;
        }
        if (target >0 && target <= 2) {
            return target;
        }
        int pre = 1;
        int p = 2;
        int res = 0;
        for (int i = 3; i <= target; i++) {
            res = pre + p;
            pre = p;
            p = res;
        }
        return res;

    }
}

 

變態跳臺階

題目描述
一隻青蛙一次能夠跳上1級臺階,也能夠跳上2級……它也能夠跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。
這個能夠用數學來解釋
F(n) = F(n-1)+F(n-2)+...+F(1)
F(n-1) = F(n-2)+F(n-3)+...+F(1)
兩個式子相減,很容易得出F(n)=2F(n-1)
每一個臺階都有跳與不跳兩種狀況(除了最後一個臺階),最後一個臺階必須跳。因此共用2^(n-1)中狀況
public class Solution {
    public int JumpFloorII(int target) {
        if (target == 0) {
            return 0;
        }
        if (target == 1) {
            return 1;
        }
        return 2 * JumpFloorII(target - 1);
    }
}

--------------------------------------------------

矩形覆蓋

題目描述
咱們能夠用2*1的小矩形橫着或者豎着去覆蓋更大的矩形。請問用n個2*1的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?
解題思路:
假設2*n的矩形的覆蓋方法爲f(8)
用第一個2x1的小矩形區覆蓋大矩形的最左邊有兩種選擇,豎着放或者橫着放。當豎着放的時候,右邊還剩2*(n-1)的區域,覆蓋的方法有f(n-1)種。當橫着放的稅後,左下角也得橫着放一個小矩形,右邊還剩2*(n-2),覆蓋的方法還有f(n-2).即f(n)=f(n-1)+f(n-2)
public class Solution {
    public int RectCover(int target) {
        if (target <= 0) {
            return 0;
        }
        if (target > 0 && target <= 2) {
            return target;
        }
        int pre = 1;
        int p = 2;
        int res = 0;
        for (int i = 3; i <= target; i++) {
            res = p + pre;
            pre = p;
            p = res;
        }
        return res;

    }
}

 

---------------------------------------------------------------------------------

 旋轉數組的最小數字

題目描述
把一個數組最開始的若干個元素搬到數組的末尾,咱們稱之爲數組的旋轉。 輸入一個非減排序的數組的一個旋轉,輸出旋轉數組的最小元素。 
例如數組{
3,4,5,1,2}爲{1,2,3,4,5}的一個旋轉,該數組的最小值爲1。 NOTE:給出的全部元素都大於0,若數組大小爲0,請返回0。

這個題目分爲有重複元素和沒有重複元素兩種狀況,沒有重複元素時,利用二分法,這個時候首先要判斷nums[mid]>nums[high],若是是的話,最小值確定在在mid和high之間,令low=mid+1;不然的話領high=mid,代碼以下:

public class Solution {
    public int findMin(int[] nums) {
        if (nums.length == 0 || nums == null) {
            return -1;
        }
        int low = 0;
        int high = nums.length - 1;
        if (nums[low] < nums[high]) {
            return nums[low];
        }
        int mid;
        while (low < high) {
            mid = low + ((high - low) >> 1);
            if (nums[mid] > nums[high]) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        return nums[low];
    }
}

那麼判斷nums[mid]>nums[high],若是是的話,那麼最小值確定在mid和high中間,而後判斷nums[mid]<nums[low],若是是的話最小值確定在low和mid中間;不然的話都有可能,這個時候只須要令high--,代碼以下:

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

------------------------------------------------------------------------

搜索旋轉排序數組

描述
假設有一個排序的按未知的旋轉軸旋轉的數組(好比,0 1 2 4 5 6 7 可能成爲4 5 6 7 0 1 2)。給定一個目標值進行搜索,若是在數組中找到目標值返回數組中的索引位置,不然返回-1。

你能夠假設數組中不存在重複的元素。

您在真實的面試中是否遇到過這個題?  
樣例
給出[4, 5, 1, 2, 3]和target=1,返回 2

給出[4, 5, 1, 2, 3]和target=0,返回 -1

挑戰
O(logN) time
解題思路
這個題目解得時候,首先查找mid下標元素的值,判斷nums[mid]是否等於target,
若是是,返回1;若是不是的話就與low位置的值相比較,判斷nums[low]
<nums[mid],
若是是,那麼這個範圍內的數字是單調遞增的,若是不是,那麼這個範圍內的數字不是單調的。
若是是單調遞增的,那麼判斷這個nums[low]<=target<=nums[mid],是的話那麼讓high=mid,不然的話low=mid+1,;
若是不是單調遞增的話,那麼判斷nums[mid]=<target<=nums[high],若是是的話,令low=mid,不然的話讓high=mid-1。
因爲區間是low+1<high,因此最後要對結果進行驗證,判斷low和high哪個符合要求,具體代碼以下:
public class Solution {
    public int search(int[] nums, int target) {
        if (nums.length == 0 || nums == null) {
            return -1;
        }
        int low = 0;
        int high = nums.length - 1;
        while(low + 1 < high) {
            int mid = low + ((high - low) >> 1);
            if (nums[mid] == target) {
                return mid;
            }
            if (nums[mid] > nums[low]) {//前半部分是升序
                if (target >= nums[low] && target <= nums[mid]) {//待查找的元素再升序子序列中
                    high = mid;
                } else {
                    low = mid + 1;
                }
            } else if (nums[mid] < nums[low]){//前半部分不是升序
                if (target >= nums[mid] && target <= nums[high]) {
                    low = mid;
                } else {
                    high = mid - 1;
                }
            }
        }
        if (nums[low] == target) {
            return low;
        }
        if (nums[high] == target) {
            return high;
        }
        return -1;
    }
}

 

 另外一種狀況是旋轉數組中存在重複元素的時候,這個時候與上面基本類似,就是加一個判斷若是nums[mid]=nums[low]的話,就是讓low++,具體代碼以下:

public class Solution {
    public boolean search(int[] nums, int target) {
        if (nums.length == 0 || nums == null) {
            return false;
        }
        int low = 0;
        int high = nums.length - 1;
        while(low + 1 < high) {
            int mid = low + ((high - low) >> 1);
            if (nums[mid] == target) {
                return true;
            }
            if (nums[mid] > nums[low]) {//前半部分是升序
                if (target >= nums[low] && target <= nums[mid]) {//待查找的元素再升序子序列中
                    high = mid;
                } else {
                    low = mid + 1;
                }
            } else if (nums[mid] < nums[low]){//前半部分不是升序
                if (target >= nums[mid] && target <= nums[high]) {
                    low = mid;
                } else {
                    high = mid - 1;
                }
            } else {
                low++;
            }
        }
        if (nums[low] == target) {
            return true;
        }
        if (nums[high] == target) {
            return true;
        }
        return false;
    }
}

--------------------------------------------------------------------------------------------------------------

 二進制中1的個數

題目描述
輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼錶示。
解題思路:
n&(n-1) 該位運算去除 n 的位級表示中最低的1那一位。 n : 10110100 n-1 : 10110011 n&(n-1) : 10110000 時間複雜度:O(M),其中 M 表示 1 的個數。
public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        while (n != 0) {
            count++;
            n &= n - 1;
        }
        return count++;

    }
}

 

相關題目:
1.判斷一個整數是否是2的整數次方。若是一個整數是2的整數次方,那麼它的二進制表示中只有一位是1,
因此把這個整數減去1以後再和它本身作與運算,這個整數中惟一的1就會變成0.
2.輸入兩個整數m,n,計算須要改變m的二進制表示中的多少位才能獲得n。好比10的二進制爲1010,13的二進制爲1101,
須要改變1010中3爲才能獲得1101.分爲兩步解決,第一步求這連個數的異或;第二步統計異或結果中1的位數。

 

-----------------------------------------------------------------------------------------

數值的整數次方

題目描述
給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。
思路分析:
就是求解一個數的冪級數並返回,這道題的一個思路就是利用二分法,判斷n的值,
若是n
=0,直接返回1,若是n=1,返回x,不然的話判斷n的奇偶性,
若是n是偶數,那麼x的n次方就能夠分解成兩個x的n/2次方相乘,而後繼續分解;
若是是奇數,那麼直接分解成兩個x的n/2次方相乘再乘以x,而後遞歸的調用分解函數就行,具體代碼以下;
public class Solution {
    public double Power(double base, int exponent) {
        if (exponent == 0) {
            return 1;
        }
        if (exponent == 1) {
            return base;
        }
        if (exponent > 0) {
            return pow(base, exponent);
        } else {
            return 1 / pow(base, -exponent);
        }
  }
    public double pow(double x, int n) {
        if (n == 1) {
            return x;
        }
        double half = pow(x, n >>> 1);
        if (n % 2 == 0) {
            return half * half;
        } else {
            return half * half * x;
        }
    }
}

 

------------------------------------------------------------------------

調整數組順序使奇數位於偶數前面

題目描述
輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得全部的奇數位於數組的前半部分,全部的偶數位於數組的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。
public class Solution {
    public void reOrderArray(int [] array) {
        if (array == null || array.length == 0) {
            return;
        }
        int oddCnt = 0;
        for (int i = 0; i < array.length; i++) {
            if (array[i] % 2 == 1) {
                oddCnt++;
            }
        }
        int[] copy = array.clone();
        int i = 0, j = oddCnt;
        for (int num : copy) {
            if (num % 2 == 1) {
                array[i++] = num;
            } else {
                array[j++] = num;
            }
        }
    }
}

 

----------------------------------------------------------------------------------

 鏈表中倒數第k個結點

題目描述
輸入一個鏈表,輸出該鏈表中倒數第k個結點。
解題思路
聲明兩個指針,第一個前後移k,而後兩個指針同時後移,直到第一個到達最後。
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if (head == null || k <= 0) {
            return null;
        }
        ListNode p = head;
        ListNode pre = head;
        while (p != null && k != 0) {
            p = p.next;
            k--;
        }
        if (k > 0) {
            return null;
        }
        while (p != null) {
            p = p.next;
            pre = pre.next;
        }
        return pre;

    }
}

 

---------------------------------------------------------------------------

反轉鏈表

題目描述
輸入一個鏈表,反轉鏈表後,輸出新鏈表的表頭。
有遞歸和非遞歸兩種實現方式,對於非遞歸方式,
首先要定要三個指針,pre表示前驅節點,p表示當前節點,next表示下一個節點,
非遞歸的時候有很是固定的模式,
next=p.next,p.next=pre,pre=p,p=next;
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode pre = head;
        ListNode p = head.next;
        ListNode next = null;
        while (p != null) {
            next = p.next;
            p.next = pre;
            pre =  p;
            p = next;
        }
        head.next = null;
        return pre;

    }
}
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode ReverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode next = head.next;
        head.next = null;
        ListNode newHead = ReverseList(next);
        next.next = head;
        return newHead;

    }
}

 

-------------------------------------------------------------------------------------

合併兩個排序的鏈表

題目描述
輸入兩個單調遞增的鏈表,輸出兩個鏈表合成後的鏈表,固然咱們須要合成後的鏈表知足單調不減規則。
將兩個有序鏈表合併成一個有序鏈表,首先判斷一下是否有一個爲空,若是是的話返回另一個,而後從頭結點開始判斷哪個鏈表的節點值小,
將小的一個節點插入到新建的鏈表中,同時指針向後移動一個,最後知道有一個鏈表爲空結束。最後還要判斷哪個鏈表沒有結束,直接將新鏈表的next指向沒有結束的鏈表便可.
另外還有一種遞歸的算法,直接比較兩個鏈表的頭結點大小,將小的一個做爲新的頭結點,而後遞歸的調用函數.
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if (list1 == null) {
            return list2;
        }
        if (list2 == null) {
            return list1;
        }
        ListNode dummy = new ListNode(0);
        ListNode p = dummy;
        while (list1 != null && list2 != null) {
            if (list1.val < list2.val) {
                p.next = list1;
                list1 = list1.next;
            } else {
                p.next = list2;
                list2 = list2.next;
            }
            p = p.next;
        }
        p.next = (list1 == null) ? list2 : list1;
        return dummy.next;
    }
}
/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        if (list1 == null) {
            return list2;
        }
        if (list2 == null) {
            return list1;
        }
        if (list1.val < list2.val) {
            list1.next = Merge(list1.next, list2);
            return list1;
        } else {
            list2.next = Merge(list1, list2.next);
            return list2;
        }
    }
}

 

-------------------------------------------------------------------------------

刪除鏈表中重複的結點

題目描述
在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留,返回鏈表頭指針。 例如,鏈表1->2->3->3->4->4->5 處理後爲 1->2->5
把全部重複的元素都要刪除,由於頭結點可能也是重複元素,因此要聲明新的頭結點,一樣此次要聲明三個指針pre,p,next;

pre初始化指向新生命的頭結點,p初始化爲頭結點,要判斷p和next的值是否相等,因此這二者都不能爲空。直接判斷p.val是否等於next.val,若是相等

將next後移直至不相等,而後pre.next = next,p = next,這樣就將重複的元素都刪除了,若是不想等的話直接將pre = p;p = p.next;而後繼續執行循環,
/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public ListNode deleteDuplication(ListNode pHead)
    {
        if (pHead == null || pHead.next == null) {
            return pHead;
        }
        ListNode dummy = new ListNode(0);
        dummy.next = pHead;
        ListNode pre = dummy;
        ListNode p = pHead;
        ListNode next = null;
        while (p != null && p.next != null) {
            next = p.next;
            if (p.val == next.val) {
                while (next != null && next.val == p.val) {
                    next = next.next;
                }
                pre.next = next;
                p = next;
            } else {
                pre = p;
                p = p.next;
            }
        }
        return dummy.next;

    }
}

 

----------------------------------------------------------------

鏈表中環的入口結點

題目描述
給一個鏈表,若其中包含環,請找出該鏈表的環的入口結點,不然,輸出null。

利用快慢指針作,判斷時主要有如下依據:

 

 

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if (pHead == null) {
            return null;
        }
        ListNode fast = pHead;
        ListNode slow = pHead;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (slow == fast) {
                slow = pHead;
                while (slow != fast) {
                    slow = slow.next;
                    fast = fast.next;
                }
                return fast;
            }
        }
        return null;
    }
}

 

----------------------------------------------------------------------------------------

相關文章
相關標籤/搜索