剑指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;
    }
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值