劍指offer算法題彙總(java版)

1.二維數組中的查找

題目描述

在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

題目思路

讀取矩陣最右上角的元素,也就是第一行最後一列的元素開始遍歷判斷

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


2.替換空格

題目描述

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

題目思路

主要考點是不開闢新空間,先計算替換後的長度,然後在原stringbuffer上從後往前做替換就可以節省空間了。

    public static String replaceSpace(StringBuffer str) {
        int spaceNum=0;
        for(int i=0;i<str.length();i++){
            if(str.charAt(i)==' ')
                spaceNum++;
        }
        int indexOld=str.length()-1;
        int newLen=str.length()+2*spaceNum;
        int indexNew=newLen-1;
        str.setLength(newLen);
        for(;indexOld>=0&&indexOld<indexNew;--indexOld){
            if(str.charAt(indexOld)==' '){
                str.setCharAt(indexNew--,'0');
                str.setCharAt(indexNew--,'2');
                str.setCharAt(indexNew--,'%');
            }else {
                str.setCharAt(indexNew--,str.charAt(indexOld));
            }
        }
        return str.toString();
    }


3.從尾到頭打印鏈表

題目描述

輸入一個鏈表,從尾到頭打印鏈表每個節點的值。

題目思路

這題可以用遞歸或棧兩種方式,但其實遞歸本質就是棧。

    public static ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        if(listNode!=null){
            printListFromTailToHead(listNode.next);
            arrayList.add(listNode.val);
        }
        return arrayList;
    }

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


4.重建二叉樹

題目描述

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

題目思路

前序遍歷第一個元素是根節點,然後根據這個根節點把左子樹右子樹分開,然後分別對左子樹右子樹用同樣的方法,遞歸下去。

    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
         return build(pre,0,pre.length-1,in,0,in.length-1);
    }
    public TreeNode build(int[] pre,int pstart,int pend,int[] in,int istart,int iend){
        if(pstart>pend)	return null;
        int cur=pre[pstart];
        int find=istart;
        while(find<=iend){
            if(in[find]==cur) break;
            else find++;
        }
        int len=find-istart;
        TreeNode res=new TreeNode(cur);
        res.left=build(pre,pstart+1,pstart+len,in,istart,find-1);
        res.right=build(pre,pstart+len+1,pend,in,find+1,iend);
        return res;
    }


5.用兩個棧實現隊列

題目描述

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

題目思路

實現兩個棧,其中一個用來存放壓入的數據,另外一個用來出棧。

    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        stack1.push(node);
    }
    
    public int pop() {
    	if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }

6.旋轉數組的最小數字

題目描述

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

題目思路

基於二分搜索,如果情況特殊,則順序查找。比如看一組例子:{1,0,1,1,1} 和 {1,1,1,0,1} 都可以看成是遞增排序數組{0,1,1,1,1}的旋轉,這就是特殊情況。

    // http://blog.csdn.net/snow_7/article/details/51909764
    public int minNumberInRotateArray(int [] array) {
        if(array==null||array.length==0)
           return 0;
        int leftIndex=0;
        int rightIndex=array.length-1;
        int midIndex=leftIndex;
        while(array[leftIndex]>=array[rightIndex]){
            if(rightIndex-leftIndex<=1){
                midIndex=rightIndex;
                break;
            }
            midIndex = (leftIndex+rightIndex)/2;
            if(array[leftIndex]==array[rightIndex] && array[midIndex]==array[leftIndex]){
                return minInOrder(array,leftIndex,rightIndex); 
            }
            if(array[midIndex]>=array[leftIndex]){
                leftIndex=midIndex;
            }else if(array[midIndex]<=array[rightIndex]){
                rightIndex=midIndex;
            }
        }
           return array[midIndex];
    }
     public int minInOrder(int[] array,int leftIndex,int rightIndex){  
        int result = array[leftIndex];  
        for(int i = leftIndex +1;i<rightIndex;i++){  
            if(result> array[i]){  
                result = array[i];  
            }  
        }  
        return result;  
    } 

7.斐波那契數列

題目描述

大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項。

n<=39

題目思路

遞歸會有很多重複的計算,用迭代效率會高。

    public int Fibonacci(int n) {
        int[] result={0,1};
        if(n<2){
            return result[n];
        }
        int fibOne=0;
        int fibTwo=1;
        int fibN=0;
        for(int i=2;i<=n;i++){
            fibN=fibOne+fibTwo;
            fibOne=fibTwo;
            fibTwo=fibN;
        }
        return fibN;
    }


8.跳臺階

題目描述

一隻青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法。

題目思路

同上一題思路一樣。

    public int JumpFloor(int target) {
        if(target==1)	return 1;
        else if(target==2)	return 2;
        else return JumpFloor(target-1)+JumpFloor(target-2);
    }


9.變態跳臺階

題目描述

一隻青蛙一次可以跳上1級臺階,也可以跳上2級……它也可以跳上n級。求該青蛙跳上一個n級的臺階總共有多少種跳法。

題目思路

同可以得到f(n)=f(n-1)+f(n-2)+...+f(1)+1,而f(n-1)=f(n-2)+...+f(1)+1,兩個公式相減,得到f(n)=2f(n-1),

    public int JumpFloorII(int target) {
        // http://blog.csdn.net/ns_code/article/details/25367797
        if(target==0) return 0;
        else if(target==1) return 1;
        else return 2*JumpFloorII(target-1);
    }


10.矩形覆蓋

題目描述

我們可以用2*1的小矩形橫着或者豎着去覆蓋更大的矩形。請問用n個2*1的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?

題目思路

其實就是個斐波那契數列。

    public int RectCover(int target) {
        if (target <= 2) {
        return target;
        }
        int one = 1;
        int two = 2;
        int result = 0;
        for (int i = 3; i <= target; i++) {
            result = one + two;
            one = two;
            two = result;
        }
        return result;
    }

11.二進制中1的個數

題目描述

輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼錶示。

題目思路

把n與n-1做 與 運算,就是將n最右邊的1變成了0,得到的數賦值給n然後循環下去,每輪循環count+1,直到n=0時,退出循環。

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

12.數值的整數次方

題目描述

給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。

題目思路

1.全面考察指數的正負,底數爲0等情況。

2.寫出指數的二進制表達,例如13爲1101。

3.舉例:10^1101=10^0001*10^0100*10^1000。

4.通過&1和>>1來逐位讀取1101,爲1時將該位代表的乘數累乘到最終結果。(保留中間結果省去了很多操作)。

    public double Power(double base, int exponent) {
        int absExponent=exponent;
        if(exponent<0)
            absExponent=-exponent;
        if(exponent==0)
            return 1;
        if(exponent==1)
            return base;
        double result=Power(base,absExponent>>1);
        result*=result;
        if((exponent&0x1)==1)
            result*=base;
        if(exponent<0)
            return 1.0/result;
        return result;
  }

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

題目描述

輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有的奇數位於數組的前半部分,所有的偶數位於位於數組的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。

題目思路

新建一個數組,遍歷原數組,將奇數逐個放入新數組,然後再遍歷一遍原數組,將偶數從最後一個奇數所在的位置的下一個開始放。

    public void reOrderArray(int [] array) {
        int newIndex=0;
        int[] tempArray=new int[array.length];
        for(int i=0;i<array.length;i++){
            if((array[i]&1)!=0)	tempArray[newIndex++]=array[i];
        }
        for(int j=0;j<array.length;j++){
            if((array[j]&1)==0)	tempArray[newIndex++]=array[j];
        }
        for(int n=0;n<array.length;n++){
            array[n]=tempArray[n];
        }
    }
java也可以這麼解
    public void reOrderArray(int [] array) {
        IntStream even= Arrays.stream(array).filter(x->(x&0x1)==0);
        IntStream odd=Arrays.stream(array).filter(x->(x&0x1)==1);
        int []tmp= IntStream.concat(odd,even).toArray();
        for(int i=0;i<array.length;i++){
            array[i]=tmp[i];
        }
    }


14.鏈表中倒數第k個結點

題目描述

輸入一個鏈表,輸出該鏈表中倒數第k個結點。

題目思路

聲明兩個結點等於頭結點,先讓第一個結點走k步,然後兩個結點同時走,當第一個結點走到終點時,第二個節點剛好走到倒數第k個結點。

    public ListNode FindKthToTail(ListNode head,int k) {
		if(head == null || k <= 0){
			return null;
		}
		ListNode ANode = head;
		ListNode BNode = null;
		for(int i = 0;i<k-1;i++){
			if(ANode.next != null)
				ANode = ANode.next;
			else
				return null;
		}
		BNode = head;
		while(ANode.next != null){
			ANode = ANode.next;
			BNode = BNode.next;
		}
		return BNode;
    }


15.反轉鏈表

題目描述

輸入一個鏈表,反轉鏈表後,輸出鏈表的所有元素。

題目思路

非遞歸:

    public ListNode ReverseList(ListNode head) {
        if (head == null||head.next == null)
            return head;
        ListNode p = head;
        ListNode q = head.next;
        head.next = null;
        while (q != null) {
            ListNode temp = q.next;
            q.next = p;
            p = q;
            q = temp;
        }
        return p;
    }

遞歸

    public ListNode ReverseList(ListNode head) {
        if (head == null||head.next == null)
            return head;
        ListNode node = ReverseList(head.next);
        head.next.next = head;
        head.next=null;
        return node;
    }


16.合併兩個排序的鏈表

題目描述

輸入兩個單調遞增的鏈表,輸出兩個鏈表合成後的鏈表,當然我們需要合成後的鏈表滿足單調不減規則。

題目思路

沒特殊的技巧,就是先判斷哪個是頭結點,然後分別遍歷對比下去,直到遍歷完某個鏈表,然後將當前結點的next賦值爲另一個鏈表的當前結點。

    public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null)	return list2;
        if(list2==null)	return list1;
        ListNode mergeHead=null;
        ListNode current=null;
        while(list1!=null&&list2!=null){
            if(list1.val<=list2.val){
                if(mergeHead==null){
                    mergeHead=current=list1;
                }else{
                    current.next=list1;
                    current=current.next;
                }
                list1=list1.next;
            }else{
                if(mergeHead==null){
                    mergeHead=current=list2;
                }else{
                    current.next=list2;
                    current=current.next;
                }
                list2=list2.next;
            }
        }
        if(list1==null){
            current.next=list2;
        }else{
            current.next=list1;
        }
        return mergeHead;
    }

17.樹的子結構

題目描述

輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)

題目思路

分三種情況:

1.根結點一樣,A樹的根結點和B樹的根結點遞歸判斷下去

2.如果前一個結果爲false,A樹的左孩子和B樹的根結點判斷下去

3.如果還是false,A樹的右孩子和B樹的根結點判斷下去

用||省去了if判斷

    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        if(root1==null||root2==null) return false;
        return isSubTree(root1,root2)||HasSubtree(root1.left,root2)||HasSubtree(root1.right,root2);
    }

    public boolean isSubTree(TreeNode root1,TreeNode root2){
        if(root2==null)
            return true;
        if(root1==null) //root2不等於null但root1等於null
            return false;
        if(root1.val==root2.val){
            return isSubTree(root1.left,root2.left)&&isSubTree(root1.right,root2.right);
        }
        return false;
    }

18.二叉樹的鏡像

題目描述

操作給定的二叉樹,將其變換爲源二叉樹的鏡像。

題目思路

遞歸:

    public void Mirror(TreeNode root) {
        if(root==null)    return;
        Mirror(root.left);
        Mirror(root.right);
        TreeNode temp=root.left;
        root.left=root.right;
        root.right=temp;
    }

非遞歸:(層序遍歷)

    public void Mirror(TreeNode root) {
        if (root == null) return;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode node=queue.poll();
            swap(node);
            if(node.right!=null)
                queue.offer(node.right);
            if(node.left!=null)
                queue.offer(node.left);
        }
    }
    public static void swap(TreeNode root)
    {
        TreeNode temp;
        temp=root.right;
        root.right=root.left;
        root.left=temp;
    }


19.順時針打印矩陣

題目描述

輸入一個矩陣,按照從外向裏以順時針的順序依次打印出每一個數字,例如,如果輸入如下矩陣: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 則依次打印出數字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

題目思路

從左到右,從上到下,從右到左,從下到上,這四個步驟走一遍。坑點在於要考慮只有一行或一列的時候,不能像之前遍歷那樣,不然會死循環。所以必須在第三個、第四個步驟做判斷。

    public ArrayList<Integer> printMatrix(int [][] matrix) {
        int row=matrix.length;
        int col=matrix[0].length;
        ArrayList<Integer> list=new ArrayList<>();
        //if(row==0||col==0)    return list;
        int top=0,bottom=row-1,left=0,right=col-1;
        while(top<=bottom && left<=right){
            for(int i=left;i<=right;i++)    list.add(matrix[top][i]);
            for(int i=top+1;i<=bottom;i++)    list.add(matrix[i][right]);
            if (top != bottom)//不加此條件對於只有一行的數組來說,會死循環
            for(int i=right-1;i>=left;i--)    list.add(matrix[bottom][i]);
            if (left != right)//不加此條件對於只有一列的數組來說,會死循環
            for(int i=bottom-1;i>top;i--)    list.add(matrix[i][left]);
            left++;top++;right--;bottom--;
        }
        return list;
    }


20.包含min函數的棧

題目描述

定義棧的數據結構,請在該類型中實現一個能夠得到棧最小元素的min函數。

題目思路

思路很巧妙。聲明兩個stack,第一個正常存儲數據,第二個stack判斷當前min,然後壓入這個min,這個min有可能是之前的數,有可能是新的數。出棧的時候,兩個棧同時彈出。

    Stack<Integer> stack1=new Stack<>();
    Stack<Integer> stack2=new Stack<>();
    public void push(int node) {
        stack1.push(node);
        if(stack2.isEmpty()){
            stack2.push(node);
        }else if(node<=stack2.peek()){
            stack2.push(node);
        }else{
            stack2.push(stack2.peek());
        }
    }
    
    public void pop() {
        stack1.pop();
        stack2.pop();
    }
    
    public int top() {
        return stack1.peek();
    }
    
    public int min() {
        return stack2.peek();
    }

還有用一個棧就可以實現的思路。

    Stack<Integer> stack=new Stack<>();
    int minNum=Integer.MAX_VALUE;
    public void push(int node) {
        if(node<=minNum){
            stack.push(minNum);
            stack.push(node);
            minNum=node;
        }else{
            stack.push(node);
        }
    }
    
    public void pop() {
        if(stack.pop()==minNum)
            minNum=stack.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int min() {
        return minNum;
    }

21.棧的壓入彈出序列

題目描述

輸入兩個整數序列,第一個序列表示棧的壓入順序,請判斷第二個序列是否爲該棧的彈出順序。假設壓入棧的所有數字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對應的一個彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注意:這兩個序列的長度是相等的)

題目思路

用一個輔助棧去壓入元素,然後依靠邏輯遍歷壓入數組,最後判斷該棧是否爲空,空的話說明一一匹配上了。

    public boolean IsPopOrder(int [] pushA,int [] popA) {

        if(pushA.length == 0 || popA.length == 0)
            return false;
        Stack<Integer> s = new Stack<Integer>();
        //用於標識彈出序列的位置
        int popIndex = 0;
        for(int i = 0; i< pushA.length;i++){
            s.push(pushA[i]);
            //如果棧不爲空,且棧頂元素等於彈出序列
            while(!s.empty() &&s.peek() == popA[popIndex]){
                //出棧
                s.pop();
                //彈出序列向後一位
                popIndex++;
            }
        }
        return s.empty();
    }


22.從上往下打印二叉樹

題目描述

從上往下打印出二叉樹的每個節點,同層節點從左至右打印。

題目思路

層序遍歷,隊列實現。

    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if(root==null){
            return list;
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            TreeNode treeNode = queue.poll();
            if (treeNode.left != null) {
                queue.offer(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.offer(treeNode.right);
            }
            list.add(treeNode.val);
        }
        return list;
    }

23.二叉搜素樹的後序遍歷序列

題目描述

輸入一個整數數組,判斷該數組是不是某二叉搜索樹的後序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的數組的任意兩個數字都互不相同。

題目思路

二叉搜索樹的後序遍歷,根結點爲最後一個元素。數組前面的數小於根結點,數組後面的數大於根結點。從後往前找到第一個比根結點小的數,該數前面的數是否都小於根結點。然後分別遞歸前面數組和後面數組。都爲true,結果才爲true。

    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence.length==0)
            return false;
        if(sequence.length==1)
            return true;
        return judge(sequence,0,sequence.length-1);
    }
    public boolean judge(int[] sequence,int start,int end){
        if(start>=end)
            return true;
        int i=end;
        while(i>start&&sequence[i-1]>sequence[end])
            i--;
        for(int j=start;j<i-1;j++){
            if(sequence[j]>sequence[end])
                return false;
        }
        return judge(sequence,start,i-1)&&judge(sequence,i,end-1);
    }

24.二叉樹中和爲某一值的路徑

題目描述

輸入一顆二叉樹和一個整數,打印出二叉樹中結點值的和爲輸入整數的所有路徑。路徑定義爲從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。

題目思路

遞歸,且需要回退。

    private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
    private ArrayList<Integer> list = new ArrayList<Integer>();
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        if(root==null)    return listAll;
        list.add(root.val);
        target-=root.val;
        if(target==0&&root.left==null&&root.right==null)
            listAll.add(new ArrayList<Integer>(list));
        FindPath(root.left,target);
        FindPath(root.right,target);
        target+=root.val;//要不要都可以,因爲是值傳遞
        list.remove(list.size()-1);
        return listAll;
    }

25.複雜鏈表的複製

題目描述

輸入一個複雜鏈表(每個節點中有節點值,以及兩個指針,一個指向下一個節點,另一個特殊指針指向任意一個節點),返回結果爲複製後複雜鏈表的head。(注意,輸出結果中請不要返回參數中的節點引用,否則判題程序會直接返回空)

題目思路

思路也很巧妙。分三步

第一步:在每個結點後面新建結點,新結點原來結點的next結點

第二步:新建的結點的隨機指向是原來結點隨機指向結點的下一個結點

第三步:根據結點的序號是奇數還是偶數進行拆分。

    public RandomListNode Clone(RandomListNode pHead)
    {
        if(pHead==null)
            return null;
        RandomListNode pCur=pHead;
        while(pCur!=null){
            RandomListNode node=new RandomListNode(pCur.label);
            node.next=pCur.next;
            pCur.next=node;
            pCur=node.next;
        }
        pCur=pHead;
        while(pCur!=null){
            if(pCur.random!=null)
                pCur.next.random=pCur.random.next;
            pCur=pCur.next.next;
        }
        RandomListNode head=pHead.next;
        RandomListNode cur=head;
        pCur=pHead;
        while(pCur!=null){
            pCur.next = pCur.next.next;
            if(cur.next!=null)
                cur.next = cur.next.next;
            cur = cur.next;
            pCur = pCur.next;
        }
        return head;
    }

26.二叉搜索樹與雙向鏈表

題目描述

輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只能調整樹中結點指針的指向。

題目思路

本質上是個中序遍歷。改編一下,用一個boolean類型判斷是否是彈出的第一個結點來記錄雙向鏈表的根結點,還需記錄前一個彈出的結點。

    public TreeNode Convert(TreeNode pRootOfTree) {
        Stack<TreeNode> stack=new Stack<>();
        boolean isFirst=true;
        TreeNode pre=null;
        TreeNode root=null;
        while(pRootOfTree!=null||!stack.isEmpty()){
            if(pRootOfTree!=null){
                stack.push(pRootOfTree);
                pRootOfTree=pRootOfTree.left;
            }else{
                pRootOfTree=stack.pop();
                if(isFirst){
                    root=pRootOfTree;
                    isFirst=false;
                    pre=root;
                }else{
                    pre.right=pRootOfTree;
                    pRootOfTree.left=pre;
                    pre=pRootOfTree;
                }
                pRootOfTree=pRootOfTree.right;
            }
        }
        return root;
    }

27.字符串的排列

題目描述

輸入一個字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串abc,則打印出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba。

題目思路

全排列。把字符串看作是第一個字符與後面的字符這兩個部分。然後依次將第一個字符與後面的字符逐個交換位置,遞歸後,再回退。遞歸的思路是把後面的部分也看做是這樣的兩個部分。

    public ArrayList<String> Permutation(String str) {
        ArrayList list=new ArrayList();
        if(str!=null&&str.length()>0){
            PermutationHelper(str.toCharArray(),0,list);
            Collections.sort(list);
        }
        return list;
    }
    public void PermutationHelper(char[] res,int i,ArrayList list){
        if(i==res.length-1){
            String str=String.valueOf(res);
            if(!list.contains(str))
                list.add(str);
        }else{
            for(int j=i;j<res.length;j++){
                swap(res,i,j);
                PermutationHelper(res,i+1,list);
                swap(res,i,j);
            }
        }
    }
    public void swap(char[] res,int i,int j){
        char temp=res[i];
        res[i]=res[j];
        res[j]=temp;
    }

28.數組中出現次數超過一半的數字

題目描述

數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度爲9的數組{1,2,3,2,2,2,5,4,2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。

題目思路

num記錄當前數字,count記錄變化,下一個數字相同則加1,不相同則減1.最後還需要驗證找出來的這個數是否真的出現次數超過一半。

    public int MoreThanHalfNum_Solution(int [] array) {
        if(array==null||array.length==0)
            return 0;
        int num=array[0];int count=1;
        for(int i=1;i<array.length;i++){
            if(count==0){
                num=array[i];
                count=1;
            }else if(array[i]==num){
                count++;
            }else{
                count--;
            }
        }
        count=0;
        for(int i=0;i<array.length;i++){
            if(array[i]==num)	count++;
        }
        return (count>array.length/2)?num:0;
    }

29.最小的k個數

題目描述

輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。

題目思路

java中優先隊列用小頂堆實現的。先將元素都添加,然後直接按順序輸出前k個數就行了。

    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> res=new ArrayList<>();
        if(input==null||input.length==0||k>input.length)
            return res;
        PriorityQueue<Integer> queue=new PriorityQueue<>();
        for(int i=0;i<input.length;i++){
            queue.offer(input[i]);
        }
        for(int i=0;i<k;i++){
            res.add(queue.poll());
        }
        return res;
    }

30.連續子數組的最大和

題目描述

HZ偶爾會拿些專業問題來忽悠那些非計算機專業的同學。今天測試組開完會後,他又發話了:在古老的一維模式識別中,常常需要計算連續子向量的最大和,當向量全爲正數的時候,問題很好解決。但是,如果向量中包含負數,是否應該包含某個負數,並期望旁邊的正數會彌補它呢?例如:{6,-3,-2,7,-15,1,2,2},連續子向量的最大和爲8(從第0個開始,到第3個爲止)。你會不會被他忽悠住?(子向量的長度至少是1)

題目思路

沒有特殊技巧。

    public int FindGreatestSumOfSubArray(int[] array) {
        if(array==null||array.length==0)    return 0;
        int sum=array[0],tempsum=array[0];
        for(int i=1;i<array.length;i++){
            tempsum=(tempsum<0)?array[i]:tempsum+array[i];
            sum=(tempsum>sum)?tempsum:sum;
        }
        return sum;
    }

31.整數中1出現的次數(從1到n整數中1出現的次數)

題目描述

求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?爲此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數。

題目思路

有難度。計算每一位出現1的次數,求和。每一位出現1的次數與高位的數和低位的數有關,研究規律,歸納爲統一的計算方式

    public int NumberOf1Between1AndN_Solution(int n) {
    if(n<1)
        return 0;
    int count = 0;
    int base = 1;
    int round = n;
    while(round>0){
        int weight = round%10;
        round/=10;
        count += round*base;
        int former=n%base;
        if(weight==1)
            count+=former+1;
        else if(weight>1)
            count+=base;
        base*=10;
    }
    return count;
    }

32.把數組排成最小的數

題目描述

輸入一個正整數數組,把數組裏所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。例如輸入數組{3,32,321},則打印出這三個數字能排成的最小數字爲321323。

題目思路

實現比較器

    public String PrintMinNumber(int [] numbers) {
        int n;
        String s="";
        ArrayList<Integer> list=new ArrayList<Integer>();
        n=numbers.length;
        for(int i=0;i<n;i++){
            list.add(numbers[i]);
        }
        Collections.sort(list,new Comparator<Integer>(){
            public int compare(Integer str1,Integer str2){
                String s1=str1+""+str2;
                String s2=str2+""+str1;
                return s1.compareTo(s2);
            }
        });
        for(int j:list){
            s+=j;
        }
        return s;
    }

33.醜數

題目描述

把只包含因子2、3和5的數稱作醜數(Ugly Number)。例如6、8都是醜數,但14不是,因爲它包含因子7。 習慣上我們把1當做是第一個醜數。求按從小到大的順序的第N個醜數。

題目思路

有時候會不好理解。首先醜數本質上是由這三個數相互乘構成的。所以可以構建一個數組來計算前面的醜數,用三個int變量記錄三個因子使用的次數。按照變量代表的數組下標表示的元素,乘以對應的因子取最小的數。

    public int GetUglyNumber_Solution(int index) {
        if(index<7)    return index;
        int[] res=new int[index];
        res[0]=1;
        int t2=0,t3=0,t5=0,i;
        for(i=1;i<index;i++){
            res[i]=Math.min(res[t2]*2,Math.min(res[t3]*3,res[t5]*5));
            if(res[i]==res[t2]*2) t2++;
            if(res[i]==res[t3]*3) t3++;
            if(res[i]==res[t5]*5) t5++;
        }
        return res[index-1];
    }

34.第一個只出現一次的字符

題目描述

在一個字符串(1<=字符串長度<=10000,全部由字母組成)中找到第一個只出現一次的字符,並返回它的位置

題目思路

這題容易拿到手的時候想太多,但其實就是簡單的構造一個hashmap或者256數組。

    public int FirstNotRepeatingChar(String str)
    {
        if(str == null || str.equals(""))
            return -1;
        int []counts = new int[256];
        char ch;
        int index;
        for(int i=0; i < str.length();i++)
        {
            ch = str.charAt(i);
            index = (int)ch;
            counts[index]++;
        }
        for(int i =0; i< str.length(); i++)
        {
            ch = str.charAt(i);
            index = (int)ch;
            if(counts[index] == 1)
                return i;
        }
        return 0;
    }

35.數組中的逆序對

題目描述

在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007

題目思路

歸併排序的應用

    static final int P=1000000007;


    public int InversePairs(int [] array){
        if(array==null||array.length<2) return 0;
        int []tmp= Arrays.copyOf(array,array.length);
        return rec(0,tmp.length-1,tmp);
    }

    public int rec(int start,int end,int []array){
        if(start<end){
            int count=0;
            int mid=(start+end)/2;
            count+=rec(start,mid,array);
            count+=rec(mid+1,end,array); //若mid不加1會無限循環
            count+=merge(start,mid,end,array);
            return count%P;
        }
        return 0;
    }

    public int merge(int start,int mid,int end,int []array){
        int []newArray=new int[end-start+1];
        int i=start;
        int j=mid+1;
        int k=0,count=0;
        while(i<=mid&&j<=end){
            if(array[i]<=array[j])
                newArray[k++]=array[i++];
            else{
                count += mid - i + 1;
                newArray[k++]=array[j++];
                if(count>=P) {
                    count%=P;
                }
            }
        }
        while(i<=mid)
            newArray[k++]=array[i++];
        while (j<=end)
            newArray[k++]=array[j++];
        for(i=0;i<k;i++)
            array[start+i]=newArray[i];
        return count%P;
    }

36.兩個鏈表的第一個公共結點

題目描述

輸入兩個鏈表,找出它們的第一個公共結點。

題目思路

兩個鏈表看作一個環。第一個鏈表走完了從第二個鏈表的頭結點開始走,碰到後就是第一個公共結點。

    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode node1=pHead1,node2=pHead2;
        while(node1!=node2){
            node1=(node1==null?pHead2:node1.next);
            node2=(node2==null?pHead1:node2.next);
        }
        return node1;
    }

37.數字在排序數組中出現的次數

題目描述

統計一個數字在排序數組中出現的次數。

題目思路

二分查找。找到這個數字第一次出現和最後一次出現的位置。

    public int GetNumberOfK(int [] array , int k) {
        int start=GetLower(array,k);
        int end=GetUpper(array,k);
        if(start!=-1&&end!=-1)
            return end-start+1;
        else
            return 0;
    }
    public int GetLower(int [] array,int k){
        int start=0,end=array.length-1;
        while(start<=end){
            int mid=(end+start)>>1;
            if(array[mid]<k){
                start=mid+1;
            }else if(array[mid]>k){
                end=mid-1;
            }else if(mid-1>=0&&array[mid-1]==k){
                end=mid-1;
            }else{
                return mid;//如果mid=0了或者array[mid-1]!=k了就可以直接返回mid。
            }
        }
        return -1;
    }
    public int GetUpper(int [] array,int k){
        int start=0,end=array.length-1;
        while(start<=end){
            int mid=(end+start)>>1;
            if(array[mid]<k){
                start=mid+1;
            }else if(array[mid]>k){
                end=mid-1;
            }else if(mid+1<array.length&&array[mid+1]==k){
                start=mid+1;
            }else{
                return mid;
            }
        }
        return -1;
    }


38.二叉樹的深度

題目描述

輸入一棵二叉樹,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度爲樹的深度。

題目思路

遞歸:

    public int TreeDepth(TreeNode root) {
        if(root==null)
            return 0;
        else 
            return Math.max(TreeDepth(root.left),TreeDepth(root.right))+1;
    }

非遞歸:

    public int TreeDepth(TreeNode root) {
        if(root==null)
            return 0;
        int left = TreeDepth(root.left);
        int right = TreeDepth(root.right);
        return Math.max(left, right) + 1;
    }
    public int TreeDepth2(TreeNode root) {
        if(root==null)  return 0;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        int count=0,depth=0,nextCount=1;
        while(!queue.isEmpty()){
            TreeNode node=queue.poll();
            count++;//記錄這一層遍歷了幾個結點了
            if(node.left!=null)
                queue.offer(node.left);
            if(node.right!=null)
                queue.offer(node.right);
            if(count==nextCount){
                count=0;
                depth++;
                nextCount=queue.size();
            }
        }
        return depth;
    }

39.平衡二叉樹

題目描述

輸入一棵二叉樹,判斷該二叉樹是否是平衡二叉樹。

題目思路

聲明一個boolean類型變量,遞歸的過程中一旦高度差大於1,直接返回false

    private boolean isBalanced=true;
    public boolean IsBalanced_Solution(TreeNode root) {
        getDepth(root);
        return isBalanced;
    }
    public int getDepth(TreeNode root){
        if(root==null)
            return 0;
        int left=getDepth(root.left);
        int right=getDepth(root.right);
        if(Math.abs(left-right)>1){
            isBalanced=false;
            return 0;
        }
        //更大的樹才能代表樹的深度
        return (left>right)?left+1:right+1;
    }

40.數組中只出現一次的數字

題目描述

一個整型數組裏除了兩個數字之外,其他的數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。

題目思路

先將所有數異或得到的結果就是這兩個數異或的結果。然後用  (~x+1)&x  的方法求得的值只有x從右到左第一次出現1的位數爲1,其他位數都爲0,然後再用該數與逐個數做&操作作爲判斷條件,相同的數肯定分在一起,再做^操作,相同的數又抵消了,得到結果。

    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int tmp=0;
        for(int i:array)
            tmp^=i;
        int tmp2=(~tmp+1)&tmp; //tmp2只有第一個出現1的地方爲1
        for(int i:array){
            if((i&tmp2)==0)
                num1[0]^=i;
            else 
                num2[0]^=i;
        }
    }

41.和爲S的連續正數序列

題目描述

小明很喜歡數學,有一天他在做數學作業時,要求計算出9~16的和,他馬上就寫出了正確答案是100。但是他並不滿足於此,他在想究竟有多少種連續的正數序列的和爲100(至少包括兩個數)。沒多久,他就得到另一組連續正數和爲100的序列:18,19,20,21,22。現在把問題交給你,你能不能也很快的找出所有和爲S的連續正數序列? Good Luck!

題目思路

聲明兩個變量start爲1,end爲2,當start<(sum+1)/2時進入循環,current記錄當前區間的和,調整statt和end的位置得到滿足條件的區間。

    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> lists=new ArrayList<ArrayList<Integer>>();
        if(sum<3)
            return lists;
        int start=1;
        int end=2;
        int middle=(sum+1)>>1;
        int curSum=3;
        while(start<middle){
            while(curSum<sum){
                end++;
                curSum+=end;
            }
            if(curSum==sum){
                ArrayList<Integer> list=new ArrayList<Integer>();
                for(int i=start;i<=end;i++){
                    list.add(i);
                }
                lists.add(list);
            }
            curSum-=start;
            start++;
        }
        return lists;
    }

42.和爲S的兩個數字

題目描述

輸入一個遞增排序的數組和一個數字S,在數組中查找兩個數,是的他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。

題目思路

數組滿足遞增,設兩個頭尾兩個指針向中間滑動直到頭指針位置>=尾指針位置

    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> res=new ArrayList<Integer>();
        int minMul=Integer.MAX_VALUE;
        int start=0;
        int end=array.length-1;
        while(start<end){
            int tempSum=array[start]+array[end];
            if(tempSum<sum){
                start++;
            }else if(tempSum==sum){
                int tempMul=array[start]*array[end];
                if(minMul>tempMul){
                    minMul=tempMul;
                    res.add(array[start]);
                    res.add(array[end]);
                }
                start++;
                end--;
            }else{
                end--;
            }
        }
        return res;
    }

43.左旋轉字符串

題目描述

彙編語言中有一種移位指令叫做循環左移(ROL),現在有個簡單的任務,就是用字符串模擬這個指令的運算結果。對於一個給定的字符序列S,請你把其循環左移K位後的序列輸出。例如,字符序列S=”abcXYZdef”,要求輸出循環左移3位後的結果,即“XYZdefabc”。是不是很簡單?OK,搞定它

題目思路

先旋轉字符串前面的部分,再旋轉後面的部分,然後全部旋轉。

    public String LeftRotateString(String str,int n) {
        if(str==null||str.length()<0||str.length()<=n){
            return str;
        }
        char[] charArray=str.toCharArray();
        //翻轉前n個  
        reverse(charArray,0,n-1);  
        //翻轉後面的  
        reverse(charArray,n,charArray.length-1);  
        //整體翻轉  
        reverse(charArray,0,charArray.length-1);  
        return String.valueOf(charArray);  
    }
    public void reverse(char[] array ,int start,int end){
        char temp=' ';
        while(start<end){
            temp=array[start];
            array[start++]=array[end];
            array[end--]=temp;
        }
    }
如果可以用庫函數(連接兩個字符串後再截取)
    public static String LeftRotateString(String str,int n) {
        if (str==null)  return null;
        int length=str.length();
        if(length==0) return "";
        n%=str.length();
        str+=str;
        return str.substring(n,n+length);
    }

44.翻轉單詞順序列

題目描述

牛客最近來了一個新員工Fish,每天早晨總是會拿着一本英文雜誌,寫些句子在本子上。同事Cat對Fish寫的內容頗感興趣,有一天他向Fish借來翻看,但卻讀不懂它的意思。例如,“student. a am I”。後來才意識到,這傢伙原來把句子單詞的順序翻轉了,正確的句子應該是“I am a student.”。Cat對一一的翻轉這些單詞順序可不在行,你能幫助他麼?

題目思路

先將字符串全部旋轉,然後再將每兩個空格間的字符串依次旋轉。注意最左邊的字符串和最右邊的字符串。

    public String ReverseSentence(String str) {
        if(str==null||str.length()==0){
            return str;
        }
        char[] array=str.toCharArray();
        reverse(array,0,str.length()-1);
        int blank=-1;
        for(int i=0;i<str.length();i++){
            if(array[i]==' '){
                int nextBlank=i;
                reverse(array,blank+1,nextBlank-1);
                blank=nextBlank;
            }
        }
        reverse(array,blank+1,str.length()-1);
        return new String(array);
    }
    public void reverse(char[] array,int start,int end){
        char temp=' ';
        while(start<end){
            temp=array[start];
            array[start++]=array[end];
            array[end--]=temp;
        }
    }
用庫函數
    public String ReverseSentence(String str) {
        if(str==null) return null;
        if(str.length()==0) return "";
        if(str.equals(" ")) return " ";
        String [] ss=str.split(" ");
        String res="";
        for(String s:ss){
            res=s+" "+res;
        }
        return res.substring(0,res.length()-1);
    }

45.撲克牌順子

題目描述

牛客最近來了一個新員工Fish,每天早晨總是會拿着一本英文雜誌,寫些句子在本子上。同事Cat對Fish寫的內容頗感興趣,有一天他向Fish借來翻看,但卻讀不懂它的意思。例如,“student. a am I”。後來才意識到,這傢伙原來把句子單詞的順序翻轉了,正確的句子應該是“I am a student.”。Cat對一一的翻轉這些單詞順序可不在行,你能幫助他麼?

題目思路

循環的時候判斷最大值和最小值的差是否大於或者等於5,且判斷是否有相同的值(多種方法判斷,用移位的方式記錄也可以,數組也可以),這些條件都構不成順子,直接返回false。

    public boolean isContinuous(int [] numbers) {
        if(numbers.length!=5)    return false;
        int min=14;
        int max=-1;
        int flag=0;
        for(int i=0;i<numbers.length;i++){
            int number=numbers[i];
            if(number<0||number>13)    return false;
            if(number==0)    continue;
            if(((flag>>number)&1)==1)    return false;
            flag |=1<<number;
            if(number>max)    max=number;
            if(number<min)    min=number;
            if(max-min>=5)    return false;
        }
        return true;
    }

46.孩子們的遊戲(圓圈中最後剩下的數)

題目描述

每年六一兒童節,牛客都會準備一些小禮物去看望孤兒院的小朋友,今年亦是如此。HF作爲牛客的資深元老,自然也準備了一些小遊戲。其中,有個遊戲是這樣的:首先,讓小朋友們圍成一個大圈。然後,他隨機指定一個數m,讓編號爲0的小朋友開始報數。每次喊到m-1的那個小朋友要出列唱首歌,然後可以在禮品箱中任意的挑選禮物,並且不再回到圈中,從他的下一個小朋友開始,繼續0...m-1報數....這樣下去....直到剩下最後一個小朋友,可以不用表演,並且拿到牛客名貴的“名偵探柯南”典藏版(名額有限哦!!^_^)。請你試着想下,哪個小朋友會得到這份禮品呢?(注:小朋友的編號是從0到n-1)

題目思路

本質上是個約瑟夫環。用歸納法是最簡單的。參考:https://blog.csdn.net/MapReduce/article/details/1549494

    public int LastRemaining_Solution(int n,int m) {
        if(n==0) return -1;
         
       int s=0;
       for(int i=2;i<=n;i++){
           s=(s+m)%i;
       }
       return s;
    }
用arraylist模擬環來做也可以
    public int LastRemaining_Solution(int n, int m) {
        if(n==0||m==0){
            return -1;
        }
        ArrayList<Integer> array=new ArrayList<Integer>();
        for(int i=0;i<n;i++){
            array.add(i);
        }
        int index=-1;
        while(array.size()>1){
            index=(index+m)%array.size();
            array.remove(index);
            index--;
        }
        return array.get(0);
    }

47.求1+2+3+...+n

題目描述

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。

題目思路

需要利用邏輯與來使遞歸終止

    public int Sum_Solution(int n) {
        int ans=n;
        //&&兩側的表達式結果必須爲boolean型,所有&&右側要用0判斷是否與result相等,讓右側的表達式返回boolean型。 
        //不管返回的是true還是false,我們的目的僅僅是讓&&右側的表達式執行。
        boolean value=(n>0)&&((ans+=Sum_Solution(n-1))!=0);
        return ans;
    }

48.不用加減乘除做加法

題目描述

寫一個函數,求兩個整數之和,要求在函數體內不得使用+、-、*、/四則運算符號。

題目思路

用邏輯運算計算二進制相加。異或相當於各位相加,沒計算進位的值。與操作並且左移1位相當於計算進位值了,循環,直到進位值爲0

    public int Add(int num1,int num2) {
        while(num2!=0){
            int temp=num1^num2;
            num2=(num1&num2)<<1;
            num1=temp;
        }
        return num1;
    }

49.把字符串轉換成整數

題目描述

將一個字符串轉換成一個整數,要求不能使用字符串轉換整數的庫函數。 數值爲0或者字符串不是一個合法的數值則返回0

題目思路

主要查考各種邊界值判斷條件

    public int StrToInt(String str) {
        if(str==null||str.length()==0)    return 0;
        int sign=1,base=0,i=0,n=str.length();
        while (i < n && str.charAt(i) == ' ') ++i;
        if(str.charAt(i) == '+' || str.charAt(i) == '-'){
            sign = (str.charAt(i++) == '+') ? 1 : -1;
        }
        while (i < n && str.charAt(i) >= '0' && str.charAt(i) <= '9'){
            int digit=str.charAt(i) - '0';
            if(base > Integer.MAX_VALUE / 10 || (base == Integer.MAX_VALUE / 10 && digit > Integer.MAX_VALUE%10)) {
                return (sign == 1) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            }
            base = 10 * base + digit;
            i++;
        }
        if(i<n)    return 0;
        return base * sign;
    }

50.數組中重複的數字

題目描述

在一個長度爲n的數組裏的所有數字都在0到n-1的範圍內。 數組中某些數字是重複的,但不知道有幾個數字是重複的。也不知道每個數字重複幾次。請找出數組中任意一個重複的數字。 例如,如果輸入長度爲7的數組{2,3,1,0,2,5,3},那麼對應的輸出是第一個重複的數字2。

題目思路

第一反應是用輔助數組,但是有更好的辦法。再原數組上進行操作,不需要額外的空間,利用了數字在0到n-1範圍的特性。遍歷時,如果以該數字爲索引的值如果小於length,就加上length。如果大於,就說明之前數字在這裏標記過了,將其減去length,也就是還原了,然後判斷這個數字爲索引對應的值是否大於length,如果大於說明找到了重複值。

    public boolean duplicate(int numbers[],int length,int [] duplication) {
        for(int i=0;i<length;i++){
            int index=numbers[i];
            if(length<=index){
                index-=length;
            }
            if(length<=numbers[index]){
                duplication[0]=index;
                return true;
            }
            numbers[index]+=length;
        }
        return false;
    }

51.構建乘積數組

題目描述

給定一個數組A[0,1,...,n-1],請構建一個數組B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

題目思路

不能用除法。畫出兩個數組的矩陣,找規律,可以發現對角線上都爲0,先迭代求左下部分元素,由上至下填充到數組,然後再求右上部分,由下至上迭代乘以數組中的值。

    public int[] multiply(int[] A) {
        int length=A.length;
        int[] B=new int[length];
        if(length!=0){
            B[0]=1;
            for(int i=1;i<length;i++){
                B[i]=B[i-1]*A[i-1];
            }
            int temp=1;
            for(int j=length-2;j>=0;j--){
                temp*=A[j+1];
                B[j]*=temp;
            }
        }
        return B;
    }

52.正則表達式匹配

題目描述

請實現一個函數用來匹配包括'.'和'*'的正則表達式。模式中的字符'.'表示任意一個字符,而'*'表示它前面的字符可以出現任意次(包含0次)。 在本題中,匹配是指字符串的所有字符匹配整個模式。例如,字符串"aaa"與模式"a.a"和"ab*ac*a"匹配,但是與"aa.a"和"ab*a"均不匹配

題目思路

請實現一個函數用當模式中的第二個字符不是“*”時:

1.如果字符串第一個字符和模式中的第一個字符相匹配,那麼字符串和模式都後移一個字符,然後匹配剩餘的。

2.如果字符串第一個字符和模式中的第一個字符不匹配,直接返回false;

而當模式中的第二個字符是”*“時:

如果字符串第一個字符跟模式第一個字符不匹配,則模式後移2個字符,繼續匹配。如果字符串第一個字符跟模式第一個字符匹配,可以有3種匹配方式:

1.模式後移2個字符,相當於x*被忽略

2.字符串後移1個字符,模式後移2個字符

3.字符串後移1個字符,模式不變,即繼續匹配字符下一位,因爲*可以匹配多位

    public boolean match(char[] str, char[] pattern)
    {
        if(str==null||pattern==null)
            return false;
        int strIndex=0;
        int patternIndex=0;
        return matchCore(str,strIndex,pattern,patternIndex);
    }
    public boolean matchCore(char[] str,int strIndex,char[] pattern,int patternIndex){
        //有效性檢驗:str到尾,pattern到尾,匹配成功
        if(strIndex==str.length&&patternIndex==pattern.length){
            return true;
        }
        //有效性檢驗:str沒到尾,pattern到尾,匹配失敗
        if(strIndex!=str.length&&patternIndex==pattern.length){
            return false;
        }
    //模式第2個是*,且字符串第1個跟模式第1個匹配,分3種匹配模式;如不匹配,模式後移2位
    if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
        if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
            return matchCore(str, strIndex, pattern, patternIndex + 2)//模式後移2,視爲x*匹配0個字符
                    || matchCore(str, strIndex + 1, pattern, patternIndex + 2)//視爲模式匹配1個字符
                    || matchCore(str, strIndex + 1, pattern, patternIndex);//*匹配1個,再匹配str中的下一個
        } else {
            return matchCore(str, strIndex, pattern, patternIndex + 2);
        }
    }
    //模式第2個不是*,且字符串第1個跟模式第1個匹配,則都後移1位,否則直接返回false
    if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
        return matchCore(str, strIndex + 1, pattern, patternIndex + 1);
    }
    return false;
    }


53.表示數值的字符串

題目描述

請實現一個函數用來判斷字符串是否表示數值(包括整數和小數)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示數值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。

題目思路

懂一點正則表達式的寫法

    public boolean isNumeric(char[] str) {
        String string=String.valueOf(str);
        return string.matches("[\\+-]?[0-9]*(\\.[0-9]*)?([eE][\\+-]?[0-9]+)?");
    }

用庫函數

    public boolean isNumeric(char[] str) {
        try {
            double re = Double.parseDouble(new String(str));
        } catch (NumberFormatException e) {
            return false;
        }
        return true;
    }


54.字符流中第一個不重複的字符

題目描述

請實現一個函數用來找出字符流中第一個只出現一次的字符。例如,當從字符流中只讀出前兩個字符"go"時,第一個只出現一次的字符是"g"。當從該字符流中讀出前六個字符“google"時,第一個只出現一次的字符是"l"。

題目思路

長度爲256的數組來記錄字符出現的次數。arraylist記錄出現次數爲1的數,在插入的過程中如果超過1次,就刪除。

    int[] countArr=new int[256];
    ArrayList<Character> charList=new ArrayList<Character>();
    public void Insert(char ch)
    {
        countArr[ch]++;
        if(countArr[ch]==1){
            charList.add(ch);
        }else{
            charList.remove((Character)ch);
        }
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        if(charList.size()==0){
            return '#';
        }else{
            return charList.get(0);
        }
    }


55.鏈表種環的入口地址

題目描述

一個鏈表中包含環,請找出該鏈表的環的入口結點。

題目思路

用快慢指針,相遇點肯定在環內,畫圖可以分析出,起點到環起點的距離爲環長度的整數倍加上倆指針相遇的結點到環的距離,故讓其中一個指針回到起點重新開始,和另外一個指針同步走直到相遇,相遇點一定是環入口點。

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


56.刪除鏈表中重複的結點

題目描述

在一個排序的鏈表中,存在重複的結點,請刪除該鏈表中重複的結點,重複的結點不保留,返回鏈表頭指針。 例如,鏈表1->2->3->3->4->4->5 處理後爲 1->2->5

題目思路

preNode記錄前一個結點,初始化爲null,curNode記錄當前結點。根據curNode是否爲null來遍歷

    public ListNode deleteDuplication(ListNode pHead)
    {
        if(pHead==null)
            return null;
        ListNode preNode=null;
        ListNode curNode=pHead;
        while(curNode!=null){
            if(curNode.next!=null&&curNode.val==curNode.next.val){
                int value=curNode.val;
                while(curNode.next!=null&&curNode.next.val==value){
                    curNode=curNode.next;
                }
                if(preNode==null){
                    pHead=curNode.next;
                }else{
                    preNode.next=curNode.next;
                }
            }else{
                preNode=curNode;
            }
            curNode=curNode.next;
        }
        return pHead;
    }

57.二叉樹的下一個結點

題目描述

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

題目思路

沒特殊技巧,就是根據邏輯來寫代碼。如果有右子樹就找右子樹的最左結點;如果沒有右子樹,就找第一個當前結點是其父結點左孩子的結點

    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode==null) return null;
        if(pNode.right!=null){
            TreeLinkNode tmp=pNode.right;
            while(tmp.left!=null)
                tmp=tmp.left;
            return tmp;
        }else{
            TreeLinkNode parent=pNode.next;
            while(parent!=null&&parent.left!=pNode){
                pNode=parent;
                parent=pNode.next;
            }
            return parent;
        }
    }

58.對稱的二叉樹

題目描述

請實現一個函數,用來判斷一顆二叉樹是不是對稱的。注意,如果一個二叉樹同此二叉樹的鏡像是同樣的,定義其爲對稱的。

題目思路

層序遍歷。遞歸

    boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot==null)
            return true;
        return comRoot(pRoot.left,pRoot.right);
    }
    boolean comRoot(TreeNode left,TreeNode right){
        if(left==null)    return right==null;
        if(right==null)    return false;
        if(left.val!=right.val)    return false;
        return comRoot(left.right,right.left)&&comRoot(left.left,right.right);
    }
非遞歸,兩個隊列來實現,出列的時候判斷
    boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot==null) return false;
        Queue<TreeNode> queue1=new LinkedList<>();
        Queue<TreeNode> queue2=new LinkedList<>();
        TreeNode left,right;
        queue1.offer(pRoot.left);
        queue2.offer(pRoot.right);
        while (!queue1.isEmpty()&&!queue2.isEmpty()){
            left=queue1.poll();
            right=queue2.poll();
            if(left==null&&right==null)
                continue;
            if(left==null||right==null)
                return false;
            if(left.val!=right.val)
                return false;
            queue1.offer(left.left);
            queue1.offer(left.right);
            queue2.offer(right.right);
            queue2.offer(right.left);
        }
        return true;
    }

59.按之字形順序打印二叉樹

題目描述

請實現一個函數按照之字形打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右至左的順序打印,第三行按照從左到右的順序打印,其他行以此類推。

題目思路

用到descendingIterator函數。層序遍歷,一個變量記錄是奇數行還是偶數行,一個變量記錄當前層的節點數。

    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>();
        if(pRoot==null)    return res;
        LinkedList<TreeNode> q=new LinkedList<>();
        q.offer(pRoot);
        int row=0;
        while(!q.isEmpty()){
            int size=q.size();
            Iterator<TreeNode> iter=null;
            ArrayList<Integer> tmp=new ArrayList<>();
            if((row&1)>0){
                iter=q.descendingIterator();
            }else{
                iter=q.iterator();
            }
            while(iter.hasNext()){
                tmp.add(iter.next().val);
            }
            for(int i=0;i<size;i++){
                TreeNode node1=q.poll();
                if(node1.left!=null)
                    q.offer(node1.left);
                if(node1.right!=null)
                    q.offer(node1.right);
            }
            row++;
            res.add(tmp);
        }
        return res;
    }

60.把二叉樹打印成多行

題目描述

從上到下按層打印二叉樹,同一層結點從左至右輸出。每一層輸出一行。

題目思路

就是個層序遍歷,記錄當前層的結點數就行了

    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>();
        if(pRoot==null)    return res;
        LinkedList<TreeNode> q=new LinkedList<>();
        q.offer(pRoot);
        int row=0;
        while(!q.isEmpty()){
            int size=q.size();
            ArrayList<Integer> tmp=new ArrayList<>();
            for(int i=0;i<size;i++){
                TreeNode node1=q.poll();
                tmp.add(node1.val);
                if(node1.left!=null)
                    q.offer(node1.left);
                if(node1.right!=null)
                    q.offer(node1.right);
            }
            row++;
            res.add(tmp);
        }
        return res;
    }

61.序列化二叉樹

題目描述

請實現兩個函數,分別用來序列化和反序列化二叉樹

題目思路

前序遍歷,遞歸

    public int index = -1;
    String Serialize(TreeNode root) {
        StringBuffer sb = new StringBuffer();
        if(root == null){
            sb.append("#,");
            return sb.toString();
        }
        sb.append(root.val + ",");
        sb.append(Serialize(root.left));
        sb.append(Serialize(root.right));
        return sb.toString();
  }
    TreeNode Deserialize(String str) {
       index++;
       int len = str.length();
        if(index >= len){
            return null;
        }
        String[] strr = str.split(",");
        TreeNode node = null;
        if(!strr[index].equals("#")){
            node = new TreeNode(Integer.valueOf(strr[index]));
            node.left = Deserialize(str);
            node.right = Deserialize(str);
        }
         
        return node;
  }

62.二叉搜索樹的第k個結點

題目描述

給定一顆二叉搜索樹,請找出其中的第k大的結點。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按結點數值大小順序第三個結點的值爲4。

題目思路

中序遍歷

    TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(k<=0)    return null;
        int count=0;
        Stack<TreeNode> stack=new Stack<>();
        while(pRoot!=null||!stack.isEmpty()){
            if(pRoot!=null){
                stack.push(pRoot);
                pRoot=pRoot.left;
            }else{
                pRoot=stack.pop();
                count++;
                if(count==k)
                    return pRoot;
                pRoot=pRoot.right;
            }
        }
        return null;
    }

63.數據流中的中位數

題目描述

如何得到一個數據流中的中位數?如果從數據流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從數據流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。

題目思路

方法很巧妙,構造一個小頂堆和大頂堆,cout爲偶數時,將較大的數通過大頂堆過濾後添加到小頂堆,count爲奇數時,較小的數通過小頂堆過濾後加入到大頂堆

    private int count=0;
    private PriorityQueue<Integer> minHeap=new PriorityQueue<Integer>();
    private PriorityQueue<Integer> maxHeap=new PriorityQueue<Integer>(15,new Comparator<Integer>(){
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
    }
    });
    public void Insert(Integer num) {
        if(count%2==0){
            maxHeap.offer(num);
            int filteredMaxNum = maxHeap.poll();
            minHeap.offer(filteredMaxNum);
        }else{
            minHeap.offer(num);
            int filteredMinNum = minHeap.poll();
            maxHeap.offer(filteredMinNum);
        }
        count++;
    }

    public Double GetMedian() {
        if (count %2 == 0) {
            return new Double((minHeap.peek() + maxHeap.peek())) / 2;
        } else {
            return new Double(minHeap.peek());
        }
    }

64.滑動窗口的最大值

題目描述

給定一個數組和滑動窗口的大小,找出所有滑動窗口裏數值的最大值。例如,如果輸入數組{2,3,4,2,6,2,5,1}及滑動窗口的大小3,那麼一共存在6個滑動窗口,他們的最大值分別爲{4,4,6,6,6,5}; 針對數組{2,3,4,2,6,2,5,1}的滑動窗口有以下6個: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

題目思路

雙端隊列的頭部記錄當前窗口的最大值,每進來一個新數,從尾部添加,把比該數小的數都刪除。還要記得判斷最大值是否過期。

    public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> res=new ArrayList<>();
        if(size==0)    return res;
        int begin;
        ArrayDeque<Integer> q=new ArrayDeque<>();
        for(int i=0;i<num.length;i++){
            begin=i-size+1;
            if(q.isEmpty())
                q.offer(i);
            else if(begin>q.peekFirst())
                q.pollFirst();
            while((!q.isEmpty())&&num[q.peekLast()]<=num[i])
                q.pollLast();
            q.offer(i);
            if(begin>=0)
                res.add(num[q.peekFirst()]);
        }
        return res;
    }

65.矩陣中的路徑

題目描述

請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。如果一條路徑經過了矩陣中的某一個格子,則該路徑不能再進入該格子。 例如 a b c e s f c s a d e e 矩陣中包含一條字符串"bcced"的路徑,但是矩陣中不包含"abcb"路徑,因爲字符串的第一個字符b佔據了矩陣中的第一行第二個格子之後,路徑不能再次進入該格子。

題目思路

構造一個flag數組來記錄是否已經經過該結點了,然後遞歸。

    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {
        //用來記錄是否被訪問到了
        int  flag[]=new int[matrix.length];
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                if(helper(matrix,rows,cols,i,j,str,0,flag))
                    return true;
            }
        }
        return false;
    }

    public boolean helper(char[] matrix,int rows,int cols,int i,int j,char[] str,int len,int[] flag){
        int index=i*cols+j;
        if(i<0||i>=rows||j<0||j>=cols||matrix[index]!=str[len]||flag[index]==1)
            return false;
        if(len==str.length-1)
            return true;
        flag[index]=1;
        if(helper(matrix,rows,cols,i-1,j,str,len+1,flag)
          ||helper(matrix,rows,cols,i+1,j,str,len+1,flag)
          ||helper(matrix,rows,cols,i,j-1,str,len+1,flag)
          ||helper(matrix,rows,cols,i,j+1,str,len+1,flag))
            return true;
        flag[index]=0;
        return false;
    }

66.機器人的運動範圍

題目描述

地上有一個m行和n列的方格。一個機器人從座標0,0的格子開始移動,每一次只能向左,右,上,下四個方向移動一格,但是不能進入行座標和列座標的數位之和大於k的格子。 例如,當k爲18時,機器人能夠進入方格(35,37),因爲3+5+3+7 = 18。但是,它不能進入方格(35,38),因爲3+5+3+8 = 19。請問該機器人能夠達到多少個格子?

題目思路

    public int movingCount(int threshold, int rows, int cols) {
        int flag[][] = new int[rows][cols]; //記錄是否已經走過
        return helper(0, 0, rows, cols, flag, threshold);
    }
 
    private int helper(int i, int j, int rows, int cols, int[][] flag, int threshold) {
        if (i < 0 || i >= rows || j < 0 || j >= cols || numSum(i) + numSum(j)  > threshold || flag[i][j] == 1) return 0;    
        flag[i][j] = 1;
        return helper(i - 1, j, rows, cols, flag, threshold)
            + helper(i + 1, j, rows, cols, flag, threshold)
            + helper(i, j - 1, rows, cols, flag, threshold)
            + helper(i, j + 1, rows, cols, flag, threshold)
            + 1;
    }
 
    private int numSum(int i) {
        int sum = 0;
        do{
            sum += i%10;
        }while((i = i/10) > 0);
        return sum;
    }











發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章