ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【Leecode笔记】第二十四周 剑指offer(3.29-4.4)

2021-04-05 13:57:23  阅读:198  来源: 互联网

标签:4.4 return nums int offer Leecode new root public


【第一题】数组中数字出现的次数II

在这里插入图片描述
分析:
方法一:集合;
方法二:哈希表;

//哈希好慢
class Solution {
    public int singleNumber(int[] nums) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int i = 0;i < nums.length;i++){
            map.put(nums[i],map.getOrDefault(nums[i],0)+1);
        }
        for(int key:map.keySet()){
            if(map.get(key) == 1){return key;}
        }
        return -1;
    }
}

【第二题】最小的K个数

在这里插入图片描述

分析:
方法一:排序;输出前k个元素。
方法二:大根堆;用前k个元素组成一个大根堆,然后每进来一个元素就和堆根进行比较,如果大则抛之,如果小则加入堆,把堆根元素(堆中最大值)弹出堆。
方法三:分治;将所有元素分为k部分,每部分选出一个最小的。
方法四:二叉搜索树。
方法五:先找到第k小的数,然后遍历一遍数组,将所有比k小的数加入结果集。
方法六:快速选择。利用快排的partition思想,进行不完全的快速排序,只需要直到最小的k个数是哪些即可。

在这里插入图片描述

//大根堆
class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
		int[] vec = new int[k];
		if(k == 0){return vec;}
		PriorityQueue<Integer> queue = new PriorityQueue<Integer>(new Comparator<Integer>(){
			public int compare(Integer num1,Integer num2){
				return num2 - num1;
			}
		});
		for(int i = 0;i < k;++i){
			queue.offer(arr[i]);
		}
		for(int i = k;i < arr.length;i++){
			if(queue.peek() > arr[i]){
				queue.poll();
				queue.offer(arr[i]);
			}
		}
		for(int i = 0;i < k;++i){
			vec[i] = queue.poll();
		}
		return vec;
	}
}
public int[] getLeastNumbers(int[] arr, int k) {
    if (k == 0) {
        return new int[0];
    } else if (arr.length <= k) {
        return arr;
    }
    
    // 原地不断划分数组
    partitionArray(arr, 0, arr.length - 1, k);
    
    // 数组的前 k 个数此时就是最小的 k 个数,将其存入结果
    int[] res = new int[k];
    for (int i = 0; i < k; i++) {
        res[i] = arr[i];
    }
    return res;
}

void partitionArray(int[] arr, int lo, int hi, int k) {
    // 做一次 partition 操作
    int m = partition(arr, lo, hi);
    // 此时数组前 m 个数,就是最小的 m 个数
    if (k == m) {
        // 正好找到最小的 k(m) 个数
        return;
    } else if (k < m) {
        // 最小的 k 个数一定在前 m 个数中,递归划分
        partitionArray(arr, lo, m-1, k);
    } else {
        // 在右侧数组中寻找最小的 k-m 个数
        partitionArray(arr, m+1, hi, k);
    }
}

// partition 函数和快速排序中相同,具体可参考快速排序相关的资料
// 代码参考 Sedgewick 的《算法4》
int partition(int[] a, int lo, int hi) {
    int i = lo;
    int j = hi + 1;
    int v = a[lo];
    while (true) { 
        while (a[++i] < v) {
            if (i == hi) {
                break;
            }
        }
        while (a[--j] > v) {
            if (j == lo) {
                break;
            }
        }

        if (i >= j) {
            break;
        }
        swap(a, i, j);
    }
    swap(a, lo, j);

    // a[lo .. j-1] <= a[j] <= a[j+1 .. hi]
    return j;
}

void swap(int[] a, int i, int j) {
    int temp = a[i];
    a[i] = a[j];
    a[j] = temp;
}


作者:nettee
链接:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/tu-jie-top-k-wen-ti-de-liang-chong-jie-fa-you-lie-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【第三题】扑克牌中的顺子

在这里插入图片描述

分析:首先,有重复的牌就不能作为顺子;第二,一个0只能填一个“坑”(根据0的个数、下标与元素之差,两者之间的关系来判断坑)

//执行用时:1 ms, 在所有 Java 提交中击败了91.86% 的用户
//内存消耗:36.1 MB, 在所有 Java 提交中击败了10.17% 的用户
class Solution {
    public boolean isStraight(int[] nums) {
        if(nums.length <= 1){return true;}
        Arrays.sort(nums);
        //gap为下标与数组之差
        //count为0的个数
        int gap = 0,i = 0,count = 0;
        while(nums[i]==0){
            i++;
            count++;
        }
        gap = nums[i] - i;
        //System.out.println(count + " " + i +" "+gap);
        int j = 0;
        for(j = i;j < nums.length-1;j++){
            //判断重复元素
            if(nums[j] == nums[j+1]){return false;}
            if(nums[j] - j - gap <= count){
                continue; 
            }else{
                return false;
            }
        }
        //判断最后一个位置是否满足条件
        return nums[j]-j-gap <= count?true:false;
    }
}

【第五题】数组中数字出现的次数

在这里插入图片描述

分析:空间复杂度为O(1)——立即推 位运算!
参考链接:接地气讲解(分组位运算)「还不懂就来P城砍我」

//执行用时:8 ms, 在所有 Java 提交中击败了18.05% 的用户
//内存消耗:40.1 MB, 在所有 Java 提交中击败了63.30% 的用户
//双指针
class Solution {
    public int[] singleNumbers(int[] nums) {
        if(nums.length == 2){
            if(nums[0]!=nums[1]){
                return nums;
            }
        }
        Arrays.sort(nums);
        int[] arr = new int[2];
        //双指针比较
        int i = 0,j = 1;
        int k = 0;
        while(i < nums.length && j < nums.length){
            if(nums[i] ==nums[j]){
                i+=2;
                j+=2;
            }else{
                if(k <=1){
                    arr[k++] = nums[i];
                }else{break;}
                i++;
                j++;
            }
        }
        if(k <= 1){
            arr[k] = nums[i];
        }
        return arr;
    }
}
class Solution {
    public int[] singleNumbers(int[] nums) {
        int sum = 0;
        //最后得到的是这两个不同的数的异或结果
        for(int num:nums){
            sum ^= num;
        }
        int flag = sum & (-sum);

        int res = 0;
        //分成两个组进行异或
        for(int val:nums){
            if((flag & val) !=0){
                res ^= val;
            }
        }
        return new int[]{res,sum ^ res};
    }
}
public int[] singleNumbers(int[] nums) {
        //xor用来计算nums的异或和
        int xor = 0;

        // 计算异或和 并存到xor
        // e.g. [2,4,2,3,3,6] 异或和:(2^2)^(3^3)^(4^6)=2=010
        for(int num : nums) xor ^= num;

        //设置mask为1,则二进制为0001
        // mask是一个二进制数,且其中只有一位是1,其他位全是0,比如000010,
        // 表示我们用倒数第二位作为分组标准,倒数第二位是0的数字分到一组,倒数第二位是1的分到另一组
        int mask = 1;

        // & operator只有1&1时等于1 其余等于0
        // 用上面的e.g. 4和6的二进制是不同的 我们从右到左找到第一个不同的位就可以分组 4=0100 6=0110
        // 根据e.g. 010 & 001 = 000 = 0则 mask=010
        // 010 & 010 != 0 所以mask=010
        // 之后就可以用mask来将数组里的两个数分区分开
        while((xor & mask)==0){
            mask <<= 1;
        }

        //两个只出现一次的数字
        int a=0, b=0;

        for(int num : nums){
            //根据&是否为0区分将两个数字分区,并分别求异或和
            if((num & mask)==0) a ^= num;
            else{
                b ^= num;
            }
        }
        return new int[]{a,b};
    }

【第六题】从上到下打印二叉树

在这里插入图片描述

分析:直接利用队列层序遍历。

class Solution {
    public int[] levelOrder(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null){return list.stream().mapToInt(Integer::intValue).toArray();}
        //if(root == null){return list.toArray(new Integer[0]);}
        Queue<TreeNode> q = new LinkedList<>();
        q.add(root);
        while(!q.isEmpty()){
            int count = q.size();
            for(int i = 0;i < count;i++){
                TreeNode node = q.poll();
                //arr[j++] = node.val;
                list.add(node.val);
                if(node.left!=null){
                    q.add(node.left);
                }
                if(node.right !=null){
                    q.add(node.right);
                }
            }
        }
        //将Integer类型list复制为int数组 使用了java8的stram新特性
        return list.stream().mapToInt(Integer::intValue).toArray();
        //return list.toArray(new Integer[list.size()]);
    }
}

【第七题】重建二叉树

在这里插入图片描述

分析:根据前序中序遍历还原二叉树。
1、根据前序遍历找到父节点;
2、根据父节点在中序遍历中确定左右子树的顺序;

在这里插入图片描述

class Solution{
	public TreeNode buildTree(int[] preorder,int[] inorder){
		int n = preorder.length;
		if(n == 0){return null;}
		int rootVal = preorder[0];
		
		int index = 0;
		for(int i = 0;i < n;i++){
			if(inorder[i] == rootVal){
				index = i;
				break;
			}
		}
		TreeNode node = new TreeNode(rootVal);
		root.left = buildTree(Arrays.copyRange(preorder,1,index+1),Arrays.copyRange(inorder,0,Index));
		root.right = buildTree(Arrays.copyRange(preorder,index+1,n),Arrays.copyRange(inorder,index+1,n));
		return root;
	}
}

【第八题】丑数(动态规划挖坑)

在这里插入图片描述

【第九题】二叉树中和为某一值的路径

在这里插入图片描述

分析:深度优先.

class Solution{
	LinkedList<List<Integer>> res = new LinkedList<>();
	LinkedList<Integer> path = new LinkedList<>();
	public List<List<Integer>> pathSum(TreeNode root,int sum){
		recur(root,sum);
		return res;
	}
	void recur(TreeNode root,int tar){
		if(root == null) return;
		path.add(root.val);
		tar -= root.val;
		if(tar == 0 && root.left == null && root.right == null){
			res.add(new LinkedList(path));
		}
		recur(root.left,tar);
		recur(root.right,tar);
		path.removeLast();
	}
}
class Solution {
    List<List<Integer>> res=new ArrayList<>();
    List<Integer> temp=new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        if(root==null){
            return res;
        }
        dfs(root,sum);
        return res;

    }
    public void dfs(TreeNode root,int sum){
        sum-=root.val;
        if(sum==0&&root.left==null&&root.right==null){
            temp.add(root.val);
            res.add(new ArrayList<>(temp));
            temp.remove(temp.size()-1);
            return;
        }
        temp.add(root.val);
        if(root.left!=null){
            dfs(root.left,sum);
        }
        if(root.right!=null){
            dfs(root.right,sum);
        }
        temp.remove(temp.size()-1);
       
    }
}

19216801
在这里插入图片描述

【第十题】汉诺塔问题

分析:假如是一步,则直接从A移动到C;假如是两个以上的盘子,则先(A,B),再(A,C),再(B,C)。

class Solution{
	public void hanota(List<Integer> A,List<Integer> B,List<Integer> C){
		int n = A.size(); 
		move(n,A,B,C);
	}
	public void move(int n,List<Integer> A,List<Integer> B,List<Integer> C){
		if(n == 1){
			C.add(A.size()-1);
			A.remove(A.size()-1);
		}
		move(n-1,A,C,B);
		C.add(A.size()-1);
		A.remove(A.size()-1);
		move(n-1,B,A,C);
	}
}

在这里插入图片描述

【第十一题】字符串压缩

在这里插入图片描述
分析:
方法一:一个for循环

class Solution {
    public String compressString(String S) {
        if(S.length() <= 2){return S;}
        StringBuilder sb = new StringBuilder();
        
        int count = 1;
        int i = 0;
        for(i = 0;i < S.length()-1;i++){
            if(S.charAt(i) == S.charAt(i+1)){
                count++;
            }else{
                sb.append(S.charAt(i));
                sb.append(count+"");
                count = 1;
            }
        }
        sb.append(S.charAt(i));
        sb.append(count);
        String S1 = sb.toString();
        return S1.length() >= S.length()?S:S1;
    }
}

方法二:快慢指针

class Solution {
    // 快慢指针
    public String compressString(String S) {
        if(S.length() == 0) return S;
        char[] chars = S.toCharArray();
        StringBuilder sb = new StringBuilder();
        int slow = 0, fast = 0;
        while(fast <= chars.length){
            if(fast == chars.length){
                sb.append(chars[slow]).append(fast-slow);
            }else if(chars[fast] != chars[slow]){
                sb.append(chars[slow]).append(fast-slow);
                slow = fast;
            }
            fast++;
        }
        return sb.length() < chars.length ? sb.toString() : S;
    }
}

【第十二题】交换数字

在这里插入图片描述

分析:

class Solution {
    public int[] swapNumbers(int[] numbers) {
        numbers[0] = numbers[0] ^ numbers[1];
        numbers[1] = numbers[0] ^ numbers[1];
        numbers[0] = numbers[0] ^ numbers[1];
        return numbers;
    }
}
//但是有溢出风险
class Solution{
	public int[] swapNumbers(int[] numbers){
		numbers[0] = numbers[0] + numbers[1];
		numbers[1] = numbers[0] - numbers[1];
		numbers[0] = numbers[0] - numbers[1];
		return numbers;
	}
}

【第十三题】幂集

在这里插入图片描述

分析:涉及到穷举和剪枝——立即推,回溯。

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<Integer> list = new LinkedList<>();
        List<List<Integer>> res = new ArrayList<>();
        backTrace(nums,0,list,res);
        return res;
    }
    private void backTrace(int[] nums,int cur,List<Integer> list,List<List<Integer>> res){
        /**回溯模板:
            1、递归出口
            2、for循环
                2.1 将元素加入列表
                2.2 向下递归
                2.3 向上回溯
        **/
        res.add(new ArrayList<>(list));
        for(int i = cur;i < nums.length;i++){
            list.add(nums[i]);
            backTrace(nums,i+1,list,res);
            list.remove(list.size()-1);
        }
    }
}

【第十四题】检查子树

在这里插入图片描述

分析:

class Solution {
    public boolean checkSubTree(TreeNode t1, TreeNode t2) {
        if(t1 == null){return false;}
        //假如两棵树的根节点相同,那么就继续向下递归比较
        if(t1.val == t2.val){
            if(digui(t1,t2)){
                return true;
            }
        }
        //假如根节点不同,那么就在t1的左右子树递归匹配
        return checkSubTree(t1.left,t2) || checkSubTree(t1.right,t2);
    }
    public boolean digui(TreeNode t1,TreeNode t2){
        if(t1 == null && t2 == null){return true;}
        if(t1 == null || t2 == null || t1.val!=t2.val){return false;}
        return digui(t1.left,t2.left) && digui(t1.right,t2.right);
    }
}

【第十五题】阶乘尾数

在这里插入图片描述
分析:因为末尾的0是由是否是10的倍数决定,而是否是10——>是否有2*5——>多少个5(因为偶数的个数肯定比5多,因此只需要计算5的个数即可)。
在这里插入图片描述

class Solution {
    public int trailingZeroes(int n) {
		int count = 0;
		while(n >= 5){
			n /= 5;
			count+=n;
		}
		return count;
	}
}

标签:4.4,return,nums,int,offer,Leecode,new,root,public
来源: https://blog.csdn.net/weixin_40335368/article/details/115295467

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有