劍指offer 前篇

刷題啦刷題啦,劍指offer好像比較有名,所以就在牛客網(wǎng)上刷這個(gè)吧~
btw,刷了一些題發(fā)現(xiàn)編程之美的題好典型?。?!有不少都在筆試遇到過(guò)或者是特別有名之前做過(guò)的題。后悔沒(méi)早點(diǎn)刷T T

1.二維數(shù)組中的查找

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

思路就是從右上角開(kāi)始,比target大就向左移一位找小一點(diǎn)的,比target小就向下移找大一點(diǎn)的,直到找到為止。

public class Solution {
    
    public boolean Find(int target, int [][] array) {
        if(array.length == 0 || array[0].length == 0){
            return false;
        }
        
        int row = array.length;
        int col = array[0].length;
        
        for(int i = 0, j = col - 1; i < row && j >= 0;){
            
            if(target == array[i][j]){
                return true;
            }
            else if(target > array[i][j]){
                i++;
                continue;
            }
            else{
                j--;
                continue;
            }      
            
        }
        
        return false; 
    }   
}


2.替換空格

請(qǐng)實(shí)現(xiàn)一個(gè)函數(shù),將一個(gè)字符串中的空格替換成“%20”。例如,當(dāng)字符串為We Are Happy.則經(jīng)過(guò)替換之后的字符串為We%20Are%20Happy。

public class Solution {
    public String replaceSpace(StringBuffer str) {

        StringBuffer sb = new StringBuffer();
        for(int i = 0; i < str.length(); i++){
            if(' ' == str.charAt(i)){
                sb.append("%20");
            }else{
                sb.append(str.charAt(i));
            }
        }

        return sb.toString();

    }
}


3.從尾到頭打印鏈表

輸入一個(gè)鏈表,從尾到頭打印鏈表每個(gè)節(jié)點(diǎn)的值。

有幾種解法:
第一種是正序鏈表元素并存在list后反轉(zhuǎn)list;

public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        ArrayList<Integer> list = new ArrayList<Integer>();

        ListNode head = listNode;
        while(head != null){
            list.add(head.val);
            head = head.next;
        }

        int num = list.size();
        for(int i = 0; i < num/2; i++){
            int temp = list.get(i);
            list.set(i,list.get(num-1-i));
            list.set(num-1-i,temp);
        }

        return list;

    }
}

第二種是使用棧,入棧再出棧就會(huì)使原順序反向;

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

第三種方法是用遞歸。

public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        
        ArrayList<Integer> list = new ArrayList<Integer>();
    
        ListNode head = listNode;
        
        if(head != null){
            list = printListFromTailToHead(head.next);
            list.add(head.val);
        } 
   
        return list;
    }
}


4.重建二叉樹(shù)

輸入某二叉樹(shù)的前序遍歷和中序遍歷的結(jié)果,請(qǐng)重建出該二叉樹(shù)。假設(shè)輸入的前序遍歷和中序遍歷的結(jié)果中都不含重復(fù)的數(shù)字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹(shù)并返回。

之前分析二叉樹(shù)的帖子已經(jīng)寫(xiě)過(guò)一次了,這次再寫(xiě)發(fā)現(xiàn)root.right那一行遞歸中最后一個(gè)參數(shù)(表明此時(shí)前序中的root位置)很容易寫(xiě)錯(cuò)。index-inFrom是在中序中當(dāng)前左子樹(shù)的結(jié)點(diǎn)數(shù)目,preIndex是指從此時(shí)的前序root算起。

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(null == pre || pre.length == 0) return null;

        return construct(pre, in, 0, in.length - 1, 0);

    }



    public TreeNode construct(int [] pre, int [] in, int inFrom, int inTo, int preIndex){

        if(inFrom > inTo) return null; 

        TreeNode root = new TreeNode(pre[preIndex]);


        int index = inFrom;

        for(; index <= inTo; index++){
            if(in[index] == pre[preIndex]){
                break;
            }
        }


        root.left = construct(pre, in, inFrom, index - 1, preIndex + 1);
        root.right = construct(pre, in, index + 1, inTo, preIndex + index - inFrom + 1);

        return root;

    }
}


5.用兩個(gè)棧實(shí)現(xiàn)隊(duì)列

用兩個(gè)棧來(lái)實(shí)現(xiàn)一個(gè)隊(duì)列,完成隊(duì)列的Push和Pop操作。 隊(duì)列中的元素為int類型。

棧那篇文章分析過(guò),記得挺簡(jiǎn)單的就不再寫(xiě)了。


6.旋轉(zhuǎn)數(shù)組的最小數(shù)字

把一個(gè)數(shù)組最開(kāi)始的若干個(gè)元素搬到數(shù)組的末尾,我們稱之為數(shù)組的旋轉(zhuǎn)。 輸入一個(gè)非遞減排序的數(shù)組的一個(gè)旋轉(zhuǎn),輸出旋轉(zhuǎn)數(shù)組的最小元素。 例如數(shù)組{3,4,5,1,2}為{1,2,3,4,5}的一個(gè)旋轉(zhuǎn),該數(shù)組的最小值為1。 NOTE:給出的所有元素都大于0,若數(shù)組大小為0,請(qǐng)返回0。

典型二分法,分幾種情況討論就行。要注意的是當(dāng)元素變?yōu)?個(gè)的時(shí)候是個(gè)特殊情況,如果不拿出來(lái)單獨(dú)討論就會(huì)陷在low=mid<high這個(gè)循環(huán)里了。

很奇怪的是,這段代碼在我自己給的幾個(gè)測(cè)試用例還有l(wèi)eetcode下都是對(duì)的,可是??途W(wǎng)上不對(duì),不知道為什么。

public class Solution {
    public int minNumberInRotateArray(int [] array) {

        if(array.length == 0) return 0;

        int low = 0;
        int high = array.length - 1;
        int mid;

        while(low < high){

            if(low == high - 1) return array[low] > array[high] ? array[high] : array[low];

            mid = (low + high) / 2;
            if(array[low] <= array[high]){
                return array[low];
            }
            else{
                if(array[low] <= array[mid]){
                    low = mid;
                }else{
                    high = mid;
                }

            }
        }

        return array[low];

    }
}


7.斐波那契數(shù)列

public int Fibonacci(int n) {

        if(n == 0) return 0;
        if(n == 1) return 1;

        return Fibonacci(n-2) + Fibonacci(n-1);

}


8.跳臺(tái)階

一只青蛙一次可以跳上1級(jí)臺(tái)階,也可以跳上2級(jí)。求該青蛙跳上一個(gè)n級(jí)的臺(tái)階總共有多少種跳法。

曾經(jīng)面試問(wèn)到過(guò)這題,我還很傻地說(shuō)我做過(guò)這題,然后就換了一道我不會(huì)的......
思路想明白了就很簡(jiǎn)單,要求跳n級(jí)臺(tái)階的跳法f(n),最后一步如果跳1級(jí),跳法是f(n-1);如果跳2級(jí),跳法是f(n-2),總跳法就是他們相加。

public int JumpFloor(int target) {

        if(target == 1) return 1;
        if(target == 2) return 2;

        return JumpFloor(target - 1) + JumpFloor(target - 2);
    }


9.變態(tài)跳臺(tái)階

一只青蛙一次可以跳上1級(jí)臺(tái)階,也可以跳上2級(jí)……它也可以跳上n級(jí)。求該青蛙跳上一個(gè)n級(jí)的臺(tái)階總共有多少種跳法。

按照上面一樣的思路,只是需要把所有情況累加起來(lái)。

    public int JumpFloorII(int target) {

        if(target == 0 || target == 1) return 1;

        int res = 0;
        for(int i = 0; i < target; i++){
            res += JumpFloorII(i);
        }

        return res;
    }


10.矩形覆蓋

我們可以用2*1的小矩形橫著或者豎著去覆蓋更大的矩形。請(qǐng)問(wèn)用n個(gè)2*1的小矩形無(wú)重疊地覆蓋一個(gè)2*n的大矩形,總共有多少種方法?

跟跳臺(tái)階相似,f(n) = f(n-1) + f(n-2)。f(n-1)情況是加一塊豎矩形,f(n-2)情況是加兩塊橫矩形。

public int RectCover(int target) {

        if(target == 0) return 0;
        if(target == 1) return 1;
        if(target == 2) return 2;

        return RectCover(target-1) + RectCover(target-2); 
    }


11.二進(jìn)制中1的個(gè)數(shù)

輸入一個(gè)整數(shù),輸出該數(shù)二進(jìn)制表示中1的個(gè)數(shù)。其中負(fù)數(shù)用補(bǔ)碼表示。

(n-1) & n 會(huì)讓從左往右第一個(gè)1變?yōu)?,一直操作直到整個(gè)數(shù)為0。

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


12.數(shù)值的整數(shù)次方

給定一個(gè)double類型的浮點(diǎn)數(shù)base和int類型的整數(shù)exponent。求base的exponent次方。

關(guān)鍵是考慮到exponent為負(fù)的情況。

public double Power(double base, int exponent) {
    
    if(exponent == 0) return 1;
    
    if(exponent < 0){
        return  1/base * Power(base,exponent + 1);
    }    
    
    return base * Power(base,exponent - 1);
}


13.調(diào)整數(shù)組順序使奇數(shù)位于偶數(shù)前面

輸入一個(gè)整數(shù)數(shù)組,實(shí)現(xiàn)一個(gè)函數(shù)來(lái)調(diào)整該數(shù)組中數(shù)字的順序,使得所有的奇數(shù)位于數(shù)組的前半部分,所有的偶數(shù)位于位于數(shù)組的后半部分,并保證奇數(shù)和奇數(shù),偶數(shù)和偶數(shù)之間的相對(duì)位置不變。

第一個(gè)做法是使用一個(gè)新數(shù)組,遍歷原數(shù)組按次序把奇數(shù)儲(chǔ)存在新數(shù)組中,再遍歷原數(shù)組把偶數(shù)儲(chǔ)存在后面,最后復(fù)制新數(shù)組給原數(shù)組。

    public void reOrderArray(int [] array) {

        int[] a = new int[array.length];

        int j = 0;
        for(int elem : array){
            if(elem % 2 != 0){
                a[j++] = elem;
            }
        }

        for(int elem : array){
            if(elem % 2 == 0){
                a[j++] = elem;
            }
        }

        for(int i = 0; i < array.length; i++){
            array[i] = a[i];
        }


    }

第二種方法是采用插入排序的思想。

    public void reOrderArray(int [] array) {

        for(int i = 1; i < array.length; i++){
            if(array[i] % 2 != 0){
                int odd = array[i];
                int j = i - 1;
                for(; j >= 0; j--){
                    if(array[j] % 2 == 0){
                        array[j+1] = array[j];
                    }else{
                        break;
                    }
                }
                array[j+1] = odd;
            }              
        }
    }

還可以采取冒泡排序的思路,不過(guò)差不多啦。


14.鏈表中倒數(shù)第k個(gè)結(jié)點(diǎn)

輸入一個(gè)鏈表,輸出該鏈表中倒數(shù)第k個(gè)結(jié)點(diǎn)。

可以用快慢結(jié)點(diǎn)的思想,讓快結(jié)點(diǎn)先走k步,然后快慢同時(shí)走,快結(jié)點(diǎn)到頭了慢結(jié)點(diǎn)也就到倒數(shù)第k了。要注意k可沒(méi)確保是正確的,可能比鏈表結(jié)點(diǎn)數(shù)要大,所以在先走k步時(shí)就要時(shí)刻判null了。

public ListNode FindKthToTail(ListNode head,int k) {

        ListNode fast = head;
        ListNode slow = head;

        for(int i = 0; i < k; i++){
            if(fast == null) return null;
            fast = fast.next;
        }

        while(fast != null){
            fast = fast.next;
            slow = slow.next;
        }

        return slow;

    }


15.反轉(zhuǎn)鏈表

輸入一個(gè)鏈表,反轉(zhuǎn)鏈表后,輸出鏈表的所有元素。

public ListNode ReverseList(ListNode head) {
    
    if(head == null || head.next == null) return head;
    
    ListNode prev = head;
    ListNode curr = head.next;
    
    prev.next = null;
    
    while(curr != null){
        ListNode next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    
    ListNode p = prev;
    while(p != null){
        System.out.println(p.val);
        p = p.next;
    }
    
    return prev;
}


16.合并兩個(gè)排序的鏈表

輸入兩個(gè)單調(diào)遞增的鏈表,輸出兩個(gè)鏈表合成后的鏈表,當(dāng)然我們需要合成后的鏈表滿足單調(diào)不減規(guī)則。

之前寫(xiě)過(guò)iterative版本的,比較冗長(zhǎng),用recursive版本更短更好理解。

public ListNode Merge(ListNode list1,ListNode list2) {

    if(list1 == null) return list2;
    if(list2 == null) return list1;
    
    ListNode head = null;
    if(list1.val < list2.val){
        head = list1;
        head.next = Merge(list1.next, list2);
    }else{
        head = list2;
        head.next = Merge(list1,list2.next);
    }
    
    return head;  
}


17.樹(shù)的子結(jié)構(gòu)

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

??途W(wǎng)的定義不是很清楚,我覺(jué)得應(yīng)該再加上兩點(diǎn):1.結(jié)構(gòu)相同且對(duì)應(yīng)位置的值也相同;2.樹(shù)[1,2]是樹(shù)[1,2,3]的子結(jié)構(gòu)。
如果參考leetcode上判subtree那道題:

Given two non-empty binary trees s and t, check whether tree t has exactly the same structure and node values with a subtree of s. A subtree of s is a tree consists of a node in s and all of this node's descendants. The tree s could also be considered as a subtree of itself.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public boolean isSubtree(TreeNode root1, TreeNode root2) {
        if(root2 == null || root1 == null) return false;
        if(sameTree(root1, root2)) return true;

        return isSubtree(root1.left, root2) || isSubtree(root1.right, root2);
    }

    public boolean sameTree(TreeNode root1, TreeNode root2){
        if(root1 == null && root2 == null) return true;
        if(root1 != null && root2 != null && root1.val == root2.val) return sameTree(root1.left, root2.left) && sameTree(root1.right, root2.right);
        return false;  
    }
}

如果是牛客網(wǎng)上的思路,應(yīng)該把判相同的函數(shù)改為判子結(jié)構(gòu)函數(shù),當(dāng)對(duì)應(yīng)位置子結(jié)構(gòu)沒(méi)了而復(fù)結(jié)構(gòu)不一定沒(méi)了,仍然為真。

public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {

        if(root2 == null || root1 == 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) return false;
        if(root1.val == root2.val) {
            return isSubTree(root1.left, root2.left) && isSubTree(root1.right, root2.right);
        }

        return false;    
    }
}


18.二叉樹(shù)的鏡像

二叉樹(shù)里寫(xiě)過(guò)不說(shuō)了。


19.順時(shí)針打印矩陣

構(gòu)造一個(gè)數(shù)組step來(lái)記錄上下左右的單位偏移量,用對(duì)應(yīng)的multiple來(lái)記錄偏移量數(shù)目,count%4作為數(shù)組索引。這樣讓上下左右在代碼中都能一視同仁??墒遣粚?duì),也沒(méi)調(diào)出來(lái)結(jié)果,先貼在這里吧。

public static ArrayList<Integer> printMatrix(int [][] matrix) {
    
    ArrayList<Integer> list = new ArrayList<Integer>();
    if(matrix.length == 0 || matrix[0].length == 0) return list;
    
    int[] step = {1,1,-1,-1}; //right, down, left, up
    int[] limit = {matrix[0].length, matrix.length, 0, 0}; //jHigh, iHigh, jLow, iLow
    int count = 0;
    int i = 0, j = 0;
    
    while(limit[2] < limit[0] || limit[3] < limit[1]){
   
        int[] multiple = {0,0,0,0};
        multiple[count]++;
        
        while(i >= limit[3] && i < limit[1] && j >= limit[2] && j < limit[0]){
            //System.out.println("i = " + i + ", j = " + j);
            list.add(matrix[i][j]);
            
            i += step[1]*multiple[1] + step[3]*multiple[3];
            j += step[0]*multiple[0] + step[2]*multiple[2];         
        }            
       
        limit[count%4] -= step[count%4];
        
        count = (count+1) % 4;
  }
    
    return list;     
}



20.包含Min函數(shù)的棧
棧的帖子中寫(xiě)過(guò),筆試題也遇到過(guò)。

public class Solution {

    Stack<Integer> stack = new Stack<Integer>();
    Stack<Integer> minStack = new Stack<Integer>();

    public void push(int node) {
        stack.push(node);
        if(minStack.empty() || node < minStack.peek()) minStack.push(node);
        else minStack.push(minStack.peek());
    }

    public void pop() {
        stack.pop();
        minStack.pop();
    }

    public int top() {
        return minStack.peek();
    }

    public int min() {
        return minStack.peek();

    }
}


21.棧的壓入、彈出序列

輸入兩個(gè)整數(shù)序列,第一個(gè)序列表示棧的壓入順序,請(qǐng)判斷第二個(gè)序列是否為該棧的彈出順序。假設(shè)壓入棧的所有數(shù)字均不相等。例如序列1,2,3,4,5是某棧的壓入順序,序列4,5,3,2,1是該壓棧序列對(duì)應(yīng)的一個(gè)彈出序列,但4,3,5,1,2就不可能是該壓棧序列的彈出序列。(注意:這兩個(gè)序列的長(zhǎng)度是相等的)

又是在棧文章里面寫(xiě)過(guò)的題,重新寫(xiě)了一遍,簡(jiǎn)化了一個(gè)步驟:可以不用先把popA里面元素Push進(jìn)去再與popA比較,而是直接比較當(dāng)前的popA和pushA元素,相等就直接跳過(guò)。

public boolean IsPopOrder(int [] pushA,int [] popA) {
    
    Stack<Integer> stack = new Stack<Integer>();
    
    int j = 0;
    for(int i = 0; i < pushA.length;i++){
        if(popA[j] == pushA[i]){
           j++;
        }else{
            stack.push(pushA[i]);
        }
    }
    
    while(j < popA.length){
        if(stack.pop() != popA[j++]) return false;
    }
    
    return true;
}


22.從上往下打印二叉樹(shù)

層序遍歷(廣度優(yōu)先),二叉樹(shù)里寫(xiě)過(guò)的,這次兩分鐘一次通過(guò)!

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.peek() != null){
        int len = queue.size();
        for(int i = 0; i < len; i++){
            TreeNode temp = queue.poll();
            list.add(temp.val);
            
            if(temp.left != null) queue.offer(temp.left);
            if(temp.right != null) queue.offer(temp.right);
        }
        
    }
    
    return list;         
}


23.二叉搜索樹(shù)的后續(xù)遍歷

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

因?yàn)槭呛笮虮闅v,所以最后一個(gè)數(shù)是根節(jié)點(diǎn)。根節(jié)點(diǎn)比左子樹(shù)所有結(jié)點(diǎn)大,比右子樹(shù)所有結(jié)點(diǎn)小,所以先找到第一個(gè)比根結(jié)點(diǎn)大的結(jié)點(diǎn),以此位置區(qū)分左子樹(shù)和右子樹(shù),左子樹(shù)已經(jīng)保證了都比根小了,再一個(gè)個(gè)判斷右邊是不是都比根結(jié)點(diǎn)大,如果不是,肯定是false。再遞歸對(duì)左子樹(shù)和右子樹(shù)判斷。

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence.length == 0) return false;
        return isPostOrder(sequence, 0, sequence.length - 1);
    }

    //end inclusive
    public boolean isPostOrder(int[] postOrder, int start, int end){

        if(start < end){

            int root = postOrder[end];
            int index = start;
            for(; index < end; index++){
                if(root < postOrder[index]){
                    break;
                }
            }

            for(int i = index + 1; i < end; i++){
                if(root > postOrder[i]) return false;
            }

            return isPostOrder(postOrder, start, index - 1) && isPostOrder(postOrder, index, end - 1);

        }
        return true;
    }
}


24.二叉樹(shù)中和為某一值的路徑

輸入一顆二叉樹(shù)和一個(gè)整數(shù),打印出二叉樹(shù)中結(jié)點(diǎn)值的和為輸入整數(shù)的所有路徑。路徑定義為從樹(shù)的根結(jié)點(diǎn)開(kāi)始往下一直到葉結(jié)點(diǎn)所經(jīng)過(guò)的結(jié)點(diǎn)形成一條路徑。

用遞歸解決,臨界條件是葉節(jié)點(diǎn)剛好等于某值,說(shuō)明這是一條有效路徑,所以開(kāi)一個(gè)新list添加進(jìn)結(jié)果;遞歸時(shí)不斷把路徑上的點(diǎn)加到list最前面(combine函數(shù))。

public class Solution {
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {

        ArrayList<ArrayList<Integer>> l = new ArrayList<ArrayList<Integer>>();

        if(root == null) return l;
        if(root.val == target && root.left == null && root.right == null){
            ArrayList<Integer> temp = new ArrayList<Integer>();
            temp.add(root.val);
            l.add(temp);
            return l;
        }

        ArrayList<ArrayList<Integer>> left = combine(root.val, FindPath(root.left, target - root.val));
        ArrayList<ArrayList<Integer>> right = combine(root.val, FindPath(root.right, target - root.val));

        l.addAll(left);
        l.addAll(right);

        return l;
    }

    public ArrayList<ArrayList<Integer>> combine(int first, ArrayList<ArrayList<Integer>> l){
        for(int i = 0; i < l.size(); i++){
            l.get(i).add(0, first);
        }

        return l;
    }

}



25.復(fù)雜鏈表的復(fù)制

輸入一個(gè)復(fù)雜鏈表(每個(gè)節(jié)點(diǎn)中有節(jié)點(diǎn)值,以及兩個(gè)指針,一個(gè)指向下一個(gè)節(jié)點(diǎn),另一個(gè)特殊指針指向任意一個(gè)節(jié)點(diǎn)),返回結(jié)果為復(fù)制后復(fù)雜鏈表的head。(注意,輸出結(jié)果中請(qǐng)不要返回參數(shù)中的節(jié)點(diǎn)引用,否則判題程序會(huì)直接返回空)

關(guān)鍵是random指向任意一個(gè)節(jié)點(diǎn),所以不能按原鏈表節(jié)點(diǎn)的random值新建它。先使用HashMap建立原鏈表主鏈(不含random)節(jié)點(diǎn)和對(duì)應(yīng)復(fù)制的鏈表結(jié)點(diǎn)之間的映射,再遍歷鏈表通過(guò)HashMap來(lái)確定各節(jié)點(diǎn)的random。

/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/

public RandomListNode Clone(RandomListNode pHead){
    
    if(pHead == null) return null;
    HashMap<RandomListNode,RandomListNode> map = new HashMap<RandomListNode,RandomListNode>();
    
    RandomListNode newHead = new RandomListNode(pHead.label);
    RandomListNode r = newHead;
    RandomListNode p = pHead;
    map.put(p,r);
    
    while(p.next != null){
        r.next = new RandomListNode(p.next.label);
        p = p.next;
        r = r.next;     
        map.put(p,r);
    }
    
    p = pHead;
    r = newHead;
    while(p != null){
        r.random = map.get(p.random);
        p = p.next;
        r = r.next;
    }
    
    return newHead;
}



26.二叉搜索樹(shù)與雙向鏈表

輸入一棵二叉搜索樹(shù),將該二叉搜索樹(shù)轉(zhuǎn)換成一個(gè)排序的雙向鏈表。要求不能創(chuàng)建任何新的結(jié)點(diǎn),只能調(diào)整樹(shù)中結(jié)點(diǎn)指針的指向。

函數(shù)返回的是鏈表頭結(jié)點(diǎn)(最小值),根據(jù)二叉搜索樹(shù)的特點(diǎn),遞歸得出右子樹(shù)的最小值應(yīng)緊跟在在根結(jié)點(diǎn)后。左子樹(shù)有些不一樣,遞歸得出的是左子樹(shù)的最小值,而我們應(yīng)找到左子樹(shù)最大值,調(diào)整指針讓它在根結(jié)點(diǎn)前。所以再用一個(gè)函數(shù)求最大值結(jié)點(diǎn)。
要注意是先convert后調(diào)整指針指向,否則convert的對(duì)象是一棵已被破壞的樹(shù)。

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {

        TreeNode res = pRootOfTree;

        if(pRootOfTree == null || (pRootOfTree.left == null && pRootOfTree.right == null)) return res;

        if(pRootOfTree.left != null){
            res = Convert(pRootOfTree.left);
            TreeNode big = biggest(pRootOfTree.left);
            big.right = pRootOfTree;
            pRootOfTree.left = big;             
        }

        if(pRootOfTree.right != null){
            TreeNode small = Convert(pRootOfTree.right);
            pRootOfTree.right = small;
            small.left = pRootOfTree; 
        }      
        return res;
    }


    public TreeNode biggest(TreeNode root){
        if(root.right == null) return root;
        return biggest(root.right);
    }
}


27.字符串的排列

輸入一個(gè)字符串,按字典序打印出該字符串中字符的所有排列。例如輸入字符串a(chǎn)bc,則打印出由字符a,b,c所能排列出來(lái)的所有字符串a(chǎn)bc,acb,bac,bca,cab和cba。

又是全排列.......注意這次要字典順序而且結(jié)果不重復(fù)。

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Collections;

public class Solution {
    public ArrayList<String> Permutation(String str) {
       if(str == null || str.length() == 0) return new ArrayList<String>();
       ArrayList<String> res = new ArrayList(new HashSet(helper(str.toCharArray(),0)));
       Collections.sort(res);
       return res; 

    }

    public void swap(char[] array, int i, int j){
        char temp = array[i];
        array[i] = array[j];
        array[j] = temp;

    }

    public ArrayList<String> helper(char[] array, int start){   

        ArrayList<String> list = new ArrayList<String>();

        if(start == array.length - 1){
            list.add(String.valueOf(array[start]));
        }

        for(int i = start; i < array.length; i++){
            swap(array,start,i);
            ArrayList<String> temp = helper(array, start + 1);
            list.addAll(combine(array[start],temp));  
            swap(array,start,i);
        }

        return list;
    }

    public ArrayList<String> combine(char ch, ArrayList<String> list){
        for(int i = 0; i < list.size(); i++){
            list.set(i, String.valueOf(ch) + list.get(i));
        }
        return list;
    }
}


28.數(shù)組中出現(xiàn)次數(shù)超過(guò)一半的數(shù)字

數(shù)組中有一個(gè)數(shù)字出現(xiàn)的次數(shù)超過(guò)數(shù)組長(zhǎng)度的一半,請(qǐng)找出這個(gè)數(shù)字。例如輸入一個(gè)長(zhǎng)度為9的數(shù)組{1,2,3,2,2,2,5,4,2}。由于數(shù)字2在數(shù)組中出現(xiàn)了5次,超過(guò)數(shù)組長(zhǎng)度的一半,因此輸出2。如果不存在則輸出0。

import java.util.HashMap;
import java.util.Map;

public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {

        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();

        for(int elem: array){
            if(map.containsKey(elem)){
                int count = map.get(elem);
                map.put(elem,map.get(elem) + 1);
            }else{
                map.put(elem,1);
            }
        }

        for(Map.Entry<Integer,Integer> entry: map.entrySet()){
            if(entry.getValue() > array.length / 2) return entry.getKey();
        }

        return 0;
    }
}


29.最小的k個(gè)數(shù)

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

冒泡排序的前k次排序即可。算法復(fù)雜度O(kn)。
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {

    ArrayList<Integer> list = new ArrayList<Integer>();
    
    if(k > input.length) return list;
    
    for(int i = 0; i < k; i++){
        for(int j = input.length - 1; j > i; j--){
            if(input[j] < input[j - 1]){
                int temp = input[j];
                input[j] = input[j - 1];
                input[j - 1] = temp;
            }
        }
        list.add(input[i]);
    }  
    return list;
}

31.連續(xù)子數(shù)組的最大和

HZ偶爾會(huì)拿些專業(yè)問(wèn)題來(lái)忽悠那些非計(jì)算機(jī)專業(yè)的同學(xué)。今天測(cè)試組開(kāi)完會(huì)后,他又發(fā)話了:在古老的一維模式識(shí)別中,常常需要計(jì)算連續(xù)子向量的最大和,當(dāng)向量全為正數(shù)的時(shí)候,問(wèn)題很好解決。但是,如果向量中包含負(fù)數(shù),是否應(yīng)該包含某個(gè)負(fù)數(shù),并期望旁邊的正數(shù)會(huì)彌補(bǔ)它呢?例如:{6,-3,-2,7,-15,1,2,2},連續(xù)子向量的最大和為8(從第0個(gè)開(kāi)始,到第3個(gè)為止)。你會(huì)不會(huì)被他忽悠住?(子向量的長(zhǎng)度至少是1)



32.整數(shù)中1出現(xiàn)的次數(shù)

求出113的整數(shù)中1出現(xiàn)的次數(shù),并算出1001300的整數(shù)中1出現(xiàn)的次數(shù)?為此他特別數(shù)了一下1~13中包含1的數(shù)字有1、10、11、12、13因此共出現(xiàn)6次,但是對(duì)于后面問(wèn)題他就沒(méi)轍了。ACMer希望你們幫幫他,并把問(wèn)題更加普遍化,可以很快的求出任意非負(fù)整數(shù)區(qū)間中1出現(xiàn)的次數(shù)。

腦袋都要想破了,有點(diǎn)點(diǎn)麻煩的一道題。
思路是先統(tǒng)計(jì)個(gè)位數(shù)出現(xiàn)1的次數(shù),再統(tǒng)計(jì)十位數(shù)出現(xiàn)1的次數(shù)......
while循環(huán)里每次對(duì)一個(gè)位數(shù)統(tǒng)計(jì),要注意的是quotient2%10計(jì)算的是當(dāng)位數(shù)的數(shù)字,如果大于1,要加一個(gè)位數(shù)的滿輪次;如果等于1,不滿一個(gè)位數(shù)但有剩余,要單獨(dú)算出這個(gè)extra。

public int NumberOf1Between1AndN_Solution(int n) {
        
    int num = 0;
    int quotient1 = -1;
    int count = 0;
    while(quotient1 != 0){

        int dividend2 = (int)Math.pow(10.0,(double)count++);
        int dividend1 = 10 * dividend2;
        quotient1 = n/dividend1;
        int quotient2 = n/dividend2;
        
        int extra = 0;
        int extra1 = 0;
       
        if(quotient2%10 == 1){
            extra = n % dividend2 + 1;
        }else if(quotient2%10 != 0){
            extra1 = 1;
        }
       
        num += (quotient1 + extra1) * dividend2 + extra;
              
    }
    
    return num;
}



33.把數(shù)組排成最小的數(shù)

輸入一個(gè)正整數(shù)數(shù)組,把數(shù)組里所有數(shù)字拼接起來(lái)排成一個(gè)數(shù),打印能拼接出的所有數(shù)字中最小的一個(gè)。例如輸入數(shù)組{3,32,321},則打印出這三個(gè)數(shù)字能排成的最小數(shù)字為321323。

就排前排后而言,這些數(shù)之間是有相對(duì)大小的。對(duì)兩個(gè)數(shù)而言,把他們拼接起來(lái),拼在前面更小說(shuō)明這個(gè)數(shù)更“小”。Arrays有一個(gè)
sort(T[] a, Comparator<? super T> c) 函數(shù),可以通過(guò)自定義compare方法來(lái)排序a。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

public class Solution {
    public String PrintMinNumber(int [] numbers) {
        String s = "";
        if(numbers == null || numbers.length == 0) return s;

        String[] sArray = new String[numbers.length];
        for(int i = 0; i < numbers.length; i++){
            sArray[i] = String.valueOf(numbers[i]);
        }

        Arrays.sort(sArray, new Comparator<String>(){
            public int compare(String s1, String s2){
                return (s1+s2).compareTo(s2+s1);
            }
        });

        for(String elem: sArray){
            s += elem;
        }

        return s;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 說(shuō)明: 本文中出現(xiàn)的所有算法題皆來(lái)自牛客網(wǎng)-劍指Offer在線編程題,在此只是作為轉(zhuǎn)載和記錄,用于本人學(xué)習(xí)使用,不...
    秋意思寒閱讀 1,216評(píng)論 1 1
  • 劍指 offer 在一個(gè)二維數(shù)組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請(qǐng)完成...
    faremax閱讀 2,313評(píng)論 0 7
  • 第一章 緒論 什么是數(shù)據(jù)結(jié)構(gòu)? 數(shù)據(jù)結(jié)構(gòu)的定義:數(shù)據(jù)結(jié)構(gòu)是相互之間存在一種或多種特定關(guān)系的數(shù)據(jù)元素的集合。 第二章...
    SeanCheney閱讀 6,002評(píng)論 0 19
  • 1.把二元查找樹(shù)轉(zhuǎn)變成排序的雙向鏈表 題目: 輸入一棵二元查找樹(shù),將該二元查找樹(shù)轉(zhuǎn)換成一個(gè)排序的雙向鏈表。 要求不...
    曲終人散Li閱讀 3,495評(píng)論 0 19
  • 總結(jié) 想清楚再編碼 分析方法:舉例子、畫(huà)圖 第1節(jié):畫(huà)圖分析方法 對(duì)于二叉樹(shù)、二維數(shù)組、鏈表等問(wèn)題,都可以采用畫(huà)圖...
    M_巴拉巴拉閱讀 1,293評(píng)論 0 7

友情鏈接更多精彩內(nèi)容