劍指offer刷題記錄(二)

 

 

1.

我的解法:將樹中節點以遞歸的方式放入ArrayList這個容器中,然後調用Collection.sort()方法將其排序,然後輸出第k大的節點。

這種方法相當於沒有利用好二叉查找樹的特點,導致速度較慢。

6ms

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    ArrayList<Integer> arrays = new ArrayList<>();
    public int kthLargest(TreeNode root, int k) {
        //將全部的節點放入一個容器
        TreeNode node = root;
        Append(node);
        Collections.sort(arrays);
        int Klarge = arrays.get(arrays.size()-k);
        return Klarge;  
    }
    public void Append(TreeNode root){
        if(root != null){
            arrays.add(root.val);
        }
        
        if(root.left != null){
            Append(root.left);
        }
        if(root.right != null){
            Append(root.right);
        }
    }
}

看了大佬的兩個解法

法一:1ms

使用棧來儲存各個節點,先把root節點和右邊這一路往下的節點,依次儲存。然後pop出最右邊的節點,它肯定是最大的那個,第二大的是哪個?如果它擁有左節點的話,那第二大的並不是它的父節點。所以pop出一個節點,我們就得檢測出它是否有左節點,如果有壓棧,因爲左節點肯定比棧裏其他的節點都大。如此循環後,很明顯,被第幾個pop出來的,就是第幾大,用一個count計數就行。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    Deque<TreeNode> Stack = new LinkedList<>(); 
    public int kthLargest(TreeNode root, int k) {
        int count = 1;
        while(root != null || !Stack.isEmpty()){
            while(root != null){
            Stack.push(root);
            root = root.right;
        }
        TreeNode pop = Stack.pop();
        if(count == k){
            return pop.val;
        }
        count++;
        root = pop.left;
        }
        //循環了半天,沒找到
        return 0;
    }
}

法二:

實際上這個思路是基於中序遍歷上修改的,中序遍歷是 左子樹,根結點,右子樹,這樣它本身就是從小到大的順序。附上一個中序遍歷的程序:

這裏有個細節就是打印t.element一定要放在t.left和t.right遞歸調用之間,倘若我們需要從大到小的順序,只要調換t.left和t.right就可以了。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int count;//記錄次數
    int result = -1; //要返回的數值
    public int kthLargest(TreeNode root, int k) {
        count = k;
        Kth(root);
        return result;
    }
    public void Kth(TreeNode root){
        if(root != null){
            Kth(root.right);
            if(count == 1){
                result = root.val;
                count--;
                return;//找到了,就返回吧
            }
            count--;
            Kth(root.left);
        }
        
    }
}

2.

我的解法:思路使用隊列的層次遍歷法,一層一層輸入進List中,這裏要判斷何時換行(new),這裏採用設置兩個變量parent,current。當隊列poll出元素時,parent--;這樣當parent歸0時,相當於這一行結束了,準備換行了。當添加一個元素的左右節點時(子節點),current++;明確了下一行有幾個元素,這樣在一個行結束時,就會將current的值賦給parent。

這裏還遇到問題,這裏 List<Integer> list2 = new ArrayList<>();list2後續再換行時,我需要清空它,有三種方法。

1.list2.clear()方法,但這裏不行,會把寫入List1裏面的值也清空

2.list2 =null;這裏也不行,在下一次循環時會出現空指針異常

3.這裏採用最後一種方法。用new ArrayList()來清空list

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    Deque <TreeNode> queue = new LinkedList<>();
    int parent = 1;
    int current = 0;
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list1 = new ArrayList<>();
        //List<Integer> tmp = new ArrayList<>();
        if(root ==null)
            return list1;
        queue.add(root);
        List<Integer> list2 = new ArrayList<>();
        while(!queue.isEmpty()){
            
            TreeNode node = queue.poll();
            parent--;
            list2.add(node.val);
            
            if(node.left != null){
                queue.add(node.left);
                current++;                  
            }
            if(node.right != null){
                queue.add(node.right);
                current++;   
            }
            if(parent == 0){//這一層是否走完
                
                list1.add(list2);
                list2 = new ArrayList<>(); ;//清空
                parent = current;
                current = 0;
            }
            
        }
        return list1;
    }
}

還看到也是層次遍歷法,但它判斷換行用的是隊列的size,我覺得比我這個稍好理解。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> results = new ArrayList<>();
        if (root == null) {
            return results;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            int size = queue.size();
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                TreeNode treeNode = queue.poll();
                list.add(treeNode.val);
                if (treeNode.left != null) {
                    queue.add(treeNode.left);
                }
                if (treeNode.right != null) {
                    queue.add(treeNode.right);
                }
            }
            results.add(list);
        }
        return results;
    }
}

法二:使用遞歸,轉一個大佬的答案

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    private List<List<Integer>> list =new ArrayList<>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        helper(root,0);
        return list;
    }
    private void helper(TreeNode node,int index){//index記錄樹的深度
        if (node==null)
            return;
        if (index==list.size()) {//如果深度超出了list的size,就說明到了最新的深度
            List<Integer> mid = new ArrayList<>();
            mid.add(node.val);
            list.add(mid);
        }
        else{//若沒超出,則添加到已有的深度
            List<Integer> mid = list.get(index);
            mid.add(node.val);
            list.set(index,mid);
        }
        helper(node.left,index+1);//繼續遞歸
        helper(node.right,index+1);
    }
}

 

3.

解法一:移窗法

思路如下:

代碼如下:復現這個思路的時候,其實裏面細節很多。出了很多bug,調試了1小時才排完。

class Solution {
    public int[][] findContinuousSequence(int target) {
        int i = 1;//窗頭
        int j = 1;//窗尾
        int sum =1;
        ArrayList <int[]> array = new ArrayList<>();
        while(i <= target/2){
            if(sum < target){
                sum += ++j;
            }else if(sum > target){
                sum -= i++;
            }else{//相等
                int[] nums = new int[j-i+1];
                for(int k=i;k<=j;k++){
                    nums[k-i] = k;
                }
                sum -= i++;
                array.add(nums);
            }
        }
        return array.toArray(new int[array.size()][]);
    }
}

方法二:削尖法 這個方法非常巧妙

舉個例子,比如9,如果它能被2個連續正整數a,b。a+b=9 b =a+1,很明顯9-1=2a,所以9-1一定能被2整除。

同理如果它能被3個連續正整數表示,a+b+c=9,那3a=9-1-2,所以9-1-2一定能被3整除....

class Solution {
    public int[][] findContinuousSequence(int target) {
        int i=2;//代表的是能成功分成連續正整數時,正整數的個數,最小2個
        ArrayList <int[]> array = new ArrayList<>();
        int origal = target;
        while(i<origal/2){
            target = target-i+1;
            if(target <=0) break;
            if(target % i == 0){
                //可以組成i個連續正整數
                int[] nums = new int[i];
                int start = target / i;
                for(int k =0;k<i;k++){
                    nums[k] = start++;
                }
                array.add(nums);
                i++;
            }else{
                i++;
            }
        }
        Collections.reverse(array);
        return array.toArray(new int[array.size()][]);
    }
}

4.

我的解法: 排序,找相同3 ms

class Solution {
    public int findRepeatNumber(int[] nums) {
        Arrays.sort(nums);
        int result = -1;
        for(int i=0;i<nums.length-1;i++){
            if(nums[i] == nums[i+1]){
                result = nums[i];
                break;
            }

        }
        return result;
    }
}

解法二:哈希表 HashSet  6 ms

class Solution {
    public int findRepeatNumber(int[] nums) {
        Set<Integer> set = new HashSet<Integer>();
        int repeat = -1;
        for (int num : nums) {
            if (!set.add(num)) {
                repeat = num;
                break;
            }
        }
        return repeat;
    }
}

解法三:原地置換 時間複雜最低  0ms

如果沒有重複數字,那麼正常排序後,數字i應該在下標爲i的位置,所以思路是重頭掃描數組,遇到下標爲i的數字如果不是i的話,(假設爲m),那麼我們就拿與下標m的數字交換。在交換過程中,如果有重複的數字發生,那麼終止返回ture。

這道題能原地置換的關鍵點在於n個長度數組,並且它的範圍又是0到n-1,一個數對應一個位置。

class Solution {
    public int findRepeatNumber(int[] nums) {
        int temp;
        for(int i=0;i<nums.length;i++){
            while (nums[i]!=i){
                if(nums[i]==nums[nums[i]]){
                    return nums[i];
                }
                temp=nums[i];
                nums[i]=nums[temp];
                nums[temp]=temp;
            }
        }
        return -1;
    }
}

5.

解法一:遞歸  6ms

因爲二叉搜索樹的特點,這裏要分析幾種情況,就拿上面的樹距離。

1. root == null,那就返回root就好

2.p或者q等於root,那不用說,公共祖先一定是root,返回root。

3.p,q的值一個比root小,一個比root大,公共祖先也一定是root,返回root。

4.p,q的值都比root小,那公共祖先就往root的左節點走了。

5.p,q的值都比root大,那公共祖先就往root的右節點走了。

這裏,其實if只要寫4和5,其餘都算else,就行。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(p.val > root.val && q.val > root.val){
            return lowestCommonAncestor(root.right,p,q);
        }else if(p.val < root.val && q.val < root.val){
            return lowestCommonAncestor(root.left,p,q);
        }else{
            return root;
        }
    }
}

 解法二:非遞歸 6ms

一個思路,不多說

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        TreeNode tmp = root;
        while(true){
            if(p.val > root.val && q.val > root.val){
                root = root.right;
            }else if(p.val < root.val && q.val < root.val){
                root = root.left;
            }else{
                return root;
            }
        }    
    }
}

6.

這一題和第5題,都是找公共祖先,區別在於第5題更加簡單一點。因爲第5題是二叉搜索樹,但是他們都是判定情況是一樣的。

1.p,q如果分別在root的兩側,那就返回root。

2.p,q如果都在root的左側,那就返回root.left

3.p,q如果都在root的右側,那就返回root.right

因爲這裏無法通過p,q和root的值直接進行判別p,q分佈的位置,所以這裏的邊界條件是不同了:

1.向左側探,如果左節點爲null,說明p,q都在右節點

2.向右側探,如果右節點爲null,說明p,q都在左節點

3.不是以上的情況,那就說明正好分佈於兩側,返回當前root

8MS

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null || root==p || root==q)
            return root;
        
        TreeNode leftNode=lowestCommonAncestor(root.left,p,q);
        TreeNode rightNode=lowestCommonAncestor(root.right,p,q);

        if(leftNode==null)
            return rightNode;
        if(rightNode==null)
            return leftNode;

        return root;
    }
}

7.

我的解法:17ms 時間複雜度高了

運用了哈希圖這個容器,一個key對應一個value,所以我將數組裏每一個數,都存入圖中,遍歷一遍數組,發現圖中沒有,就加入,有就相應地value值+1。思路很簡單  其實我們並沒有返回超過數組長度一般的數字,而是返回了出現次數最多的數字,因爲題目中說了該數組只有一個數字超過了數組長度的一半,所以他就是次數最多的數字。

class Solution {
    public int majorityElement(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); 
        for(int i=0;i<nums.length;i++){
            if(map.containsKey(nums[i])){
                map.put(nums[i],map.get(nums[i])+1);
            }else{
                map.put(nums[i],1);
            }
        }
        int maxNumber = 0;
        int maxCount = Collections.max(map.values());
        for(Map.Entry<Integer, Integer> entry:map.entrySet()){
            if(maxCount == entry.getValue()){
                maxNumber = entry.getKey();
            }
        }
        return maxNumber;
    }
}

看看別人的思路,列舉兩個:

方法一:正常思路可以先排序,再取中間值,中間值就是數組中出現次數超過一半的數字。

2ms

class Solution {
    public int majorityElement(int[] nums) {
        if(nums.length == 0){
            return 0;
        }
        Arrays.sort(nums);
        if(nums.length %2 == 0){
            return nums[nums.length/2-1];
        }else{
            return nums[(nums.length+1)/2-1];
        }
    }
}

方法二:時間複雜度最低 1ms

思路簡述:target用來記錄上一個值,count來計數,遍歷數組,當target等於當前數值時,count++;不等於時count--。當count 歸0時,target的值要更新成現在這個值,並且count 值重新歸1,最終返回的target就是超過數組一般長度的值。

爲什麼?因爲這個數超過數組長度的一半,所以不管這個數分佈在數組哪些位置,最終count不會歸0 。

class Solution {
    public int majorityElement(int[] nums) {
        int target = nums[0];
        int count = 1;
        for(int i = 1;i<nums.length;i++) {
	         if(target == nums[i]) {
	             count++;
	         }else {
	             count--;
	         }
	         if(count == 0) {//當count=0時,更換target的值爲當前訪問的數組元素的值,次數設爲1
	             target = nums[i];
	             count = 1;
	         }
        }
        return target;
    }
}

8.

我的解法: 14ms,有點慢。滑窗方法

class Solution {
    public int[] twoSum(int[] nums, int target) {
        if(nums.length < 2) return null;
        int[] result = new int[2];
        int pre = 0;
        int cur = 1;
        while(nums[pre] < target){
            if(nums[pre]+nums[nums.length-1]<target){
                pre++;
                cur = pre+1;
                continue;
            }else{
                int sum = nums[pre]+nums[cur];
                if(sum < target){
                    cur++;
                }else if(sum > target){
                    pre++;
                    cur = pre+1;
                }else{
                    result[0] = nums[pre];
                    result[1] = nums[cur];
                    break;
                }
            }
        }
        return result;
    }
}

大佬的雙指針法,其實和我的方法有些類似,只不過,大佬定位pre在頭,cur在尾。而我呢,就是cur放在pre後面一位。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int left=0,right=nums.length-1;
        int [] res=new int[2];
        while(left<right){
            if (nums[left]+nums[right]==target){
                res[0]=nums[left];
                res[1]=nums[right];
                return res;
            }
            else if (nums[left]+nums[right]<target)
                left++;
            else
                right--;
        }
        return res;
    }
}

9.

我的思路:雙指針思想 2ms

class Solution {
    public int[] exchange(int[] nums) {
        int tmp;
        int left = 0;
        int right = nums.length - 1;
        while(right>left){
            if(nums[right] %2 ==1){
                //後面出現了奇數
                if(nums[left] %2 == 0){
                    //前面出現了偶數
                    tmp = nums[right];
                    nums[right] = nums[left];
                    nums[left] = tmp;//交換了
                }else{
                    left++;
                }
            }else{
                right--;
            }
        }
        return nums; 
    }
}

10.

借鑑了別人的思想:我復現了他的思想 1ms

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode tmpA = headA;//初始化雙指針
        ListNode tmpB = headB;
        int count = 0;//計數的,因爲只能走一遍,走完一遍還是沒有的話就得返回null
        if(headA == null || headB == null) return null;
        while(count<3){ 
            if(tmpA == tmpB){
                return tmpA;
            }
            if(tmpA.next == null){
                tmpA = headB;
                count++;
            }else{
                tmpA = tmpA.next;
            }
            if(tmpB.next == null){
                tmpB = headA;
                count++;
            }else{
                tmpB = tmpB.next;
            }
        }
        return null;
    }
}

解法二:看到一個,速度雖然有點慢,但是比較容易想得到 9ms

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode cur=headA;
        Set<ListNode> set=new HashSet();
        while(cur!=null){
            set.add(cur);
            cur=cur.next;
        }
        cur=headB;
        while(cur!=null){
            if(set.contains(cur)) return cur;
            else cur=cur.next;
        }
        return null;
    }
}

 

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