【Leecode笔记之java】第九周:数组专题(11.1-11.8)

【11.1】包含min函数的栈

分析:栈本质上也是一个链表。pop就是找到当前头部节点的上一个节点。
要保证min()函数的时间复杂度为O(1),可用辅助栈实现。
栈-链表示意图栈B的顶部节点表示最小元素`class MinStack {
Stack A, B;
//定义两个栈,A和B;
public MinStack() {
A = new Stack<>();
B = new Stack<>();
}
public void push(int x) {
//假如A现在push的元素小于B的顶点节点元素,则push到B栈中。
A.add(x);
if(B.empty() || B.peek() >= x)
B.add(x);
}
public void pop() {
//假如A弹出的元素等于B的顶点节点,则为了保持两个栈的一致性,弹出B的顶点节点
if(A.pop().equals(B.peek()))
B.pop();
}
public int top() {
return A.peek();
}
public int min() {
return B.peek();
}
}

作者:jyd
链接:https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof/solution/mian-shi-ti-30-bao-han-minhan-shu-de-zhan-fu-zhu-z/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
`

class MinStack {
    private Node head;
    /** initialize your data structure here. */
    public MinStack() {

    }
    
    public void push(int x) {
        if(head == null)
            head = new Node(x,x,null);
            //第一个x为节点的value,第二个x保存节点的最小值;第三个null为该节点的下一个节点
        else
            head = new Node(x,Math.min(head.min,x),head);
    }
    
    public void pop() {
        head = head.next;
    }
    
    public int top() {
        return head.val;
    }
    
    public int min() {
        return head.min;
    }

    private class Node{
        int val;
        int min;
        Node next;

        public Node(int val,int min,Node next){
            this.val = val;
            this.min = min;
            this.next = next;
        }
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.min();
 */

【11.2】

第一题:圆圈中最后剩下的数字(约瑟夫环)

在这里插入图片描述分析:
1.循环链表实现:节点每走三步,就删掉当时位置的结点;
优点是简单易懂,缺点是模拟整个游戏过程的时间复杂度太高。
2.公式法:
在这里插入图片描述按照上图显示的,上一轮和下一轮的坐标差3,越界了的话就循环(%总人数)。
因此,有递归函数:f(n,m) = (f(n-1,m)+m) % n,这样算出来的就是胜利者的下标位置。其中n为总人数。


class Solution {
    public int lastRemaining(int n, int m) {
        int p = 0;
        for(int i = 2;i <= n;i++){
            p = (p + m) % i;
        }
        return p;
    }
}

第二题:【和为s的两个数字】

在这里插入图片描述分析:设置双哨兵i,j,i指向最小值,j指向不大于target的最大值,假如i+j > target,则j向左边走,否则i向右边走;

//执行用时:2 ms, 在所有 Java 提交中击败了98.40% 的用户
//内存消耗:55.6 MB, 在所有 Java 提交中击败了54.09% 的用户
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int i = 0,j = nums.length-1;
        int[] arr = new int[2];
        while(i < j){
            while(nums[j] > target){
                j--;
            }
            if(nums[i] + nums[j] < target){
                i++;
            }
            else if(nums[i] + nums[j] > target){
                j--;
            }
            else{
                arr[0] = nums[i];
                arr[1] = nums[j];
                return arr;
            }   
        }
        return null;
    }
}

【11.3】

第一题:两个数组的交集

在这里插入图片描述分析:最直观的求交集就是两个for循环嵌套查找,这样时间复杂度是O(mn)。
第一种方法:哈希集合存储元素,则可用在O(1)的时间内判断一个元素是否在集合中,从而降低时间复杂度。
第二种方法:先排序,然后双指针。

//第一种方法:
//执行用时:3 ms, 在所有 Java 提交中击败了95.77% 的用户
//内存消耗:38.5 MB, 在所有 Java 提交中击败了93.55% 的用户
class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        //首先定义两个集合
        Set<Integer> set1 = new HashSet<Integer>();
        Set<Integer> set2 = new HashSet<Integer>();
        //遍历数组,将数组元素放到集合中
        for(int num:nums1){
            set1.add(num);
        }
        for(int num:nums2){
            set2.add(num);
        }
        return getIntersection(set1,set2);
    }

    public int[] getIntersection(Set<Integer> set1,Set<Integer> set2){
        //选小的集合
        if(set1.size() > set2.size()){
            return getIntersection(set2,set1);
        }
        //用哈希表集合作为答案集合
        Set<Integer> intersectionSet = new HashSet<Integer>();
        for(int num:set1){
            if(set2.contains(num)){
                intersectionSet.add(num);
            }
        }
        int[] intersection = new int[intersectionSet.size()];
        int index = 0;
        for(int num:intersectionSet){
            intersection[index++] = num;
        }
        return intersection;
    }
}
//第二种方法:
//执行用时:1 ms, 在所有 Java 提交中击败了99.95% 的用户
//内存消耗:38.8 MB, 在所有 Java 提交中击败了76.40% 的用户
class Solution{
    public int[] intersection(int[] nums1,int[] nums2){
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        int length1 = nums1.length,length2 = nums2.length;
        int[] intersection = new int[length1 + length2];
        int index = 0,index1 = 0,index2 = 0;
        while(index1 < length1 && index2 < length2){
            int num1 = nums1[index1],num2 = nums2[index2];
            if(num1 == num2){
                if(index == 0 || num1 != intersection[index -1]){
                    intersection[index++] = num1;
                }
                index1++;
                index2++;
            }else if(num1 < num2){
                index1++;
            }else{
                index2++;
            }
        }
        return Arrays.copyOfRange(intersection,0,index);
    }
}

第二题:和为s的连续正数序列

在这里插入图片描述分析:由于是连续子序列,那么就有范围(也就是说一个上界,一个下界),那么最直观的就是一个一个暴力查询。例如以1为下界,累加2,累加3,假如累加和小于target那么继续累加,假如大于target,则退出,改变下界,只有当刚好等于target才停止。

如何优化?(优化可从两个方面来着手,一是改变计算的方式,例如说如何累加速度最快,公式、框架、模型都可以加快速度;二是减少计算量,先筛选掉/剪枝不可能的情况,再累加。)

  • 累加——累加公式;
  • 筛选——满足条件;

假设上界为y,下界为x,那么累加和为(x+y)(y-x+1)/2 = target
展开公式:y2+y−x2+x−2∗target=0,要使方程有整数解,需要满足两个条件

  • 判别式 b^2-4ac 开根需要为整数
  • 最后的求根公式的分子需要为偶数,因为分母为 2
class Solution {
    public int[][] findContinuousSequence(int target) {
        List<int[]> vec = new ArrayList<int[]>();
        int sum = 0, limit = (target - 1) / 2; // (target - 1) / 2 等效于 target / 2 下取整
        for (int x = 1; x <= limit; ++x) {
            long delta = 1 - 4 * (x - (long) x * x - 2 * target);
            if (delta < 0) {
                continue;
            }
            int delta_sqrt = (int) Math.sqrt(delta + 0.5);
            if ((long) delta_sqrt * delta_sqrt == delta && (delta_sqrt - 1) % 2 == 0) {
                int y = (-1 + delta_sqrt) / 2; // 另一个解(-1-delta_sqrt)/2必然小于0,不用考虑
                if (x < y) {
                    int[] res = new int[y - x + 1];
                    for (int i = x; i <= y; ++i) {
                        res[i - x] = i;
                    }
                    vec.add(res);
                }
            }
        }
        return vec.toArray(new int[vec.size()][]);
    }
}

【11.4】二进制中1的个数

在这里插入图片描述分析:首先将数字转化为二进制,然后遍历二进制的每一位,统计1的个数。
难点在于:
Q1.如何转化进制?Q2.如何确定二进制位的长度然后遍历统计?
A1:在存储器里存的就是二进制位…
A2:进行&1操作,判断当前二进制位的末尾是否为1,然后右移一位操作,再次进行判断,假如&的结果为1,则计数器+1,最后返回计数器的结果即可。

在这里插入图片描述这里出错是优先级问题,恒等运算符优先级比&要大。

//执行用时:1 ms, 在所有 Java 提交中击败了99.36% 的用户
//内存消耗:35.3 MB, 在所有 Java 提交中击败了93.08% 的用户
public class Solution {
    public int hammingWeight(int n) {
        int res = 0;
        while(n != 0) {
            res += n & 1;
            n >>>= 1;
        }
        return res;
    }
}

第二题:链表中倒数第k个节点

在这里插入图片描述分析:之前考研王道书上有这么个题。
第一种方法:说到倒数——栈;
第二种方法:设置两个哨兵,两者步长为k(先让j往前走k步),当j到达终点时,i所在的位置就是倒数第k个节点。
C语言是null,java是NULL

//执行用时:4 ms, 在所有 C 提交中击败了51.01% 的用户
//内存消耗:5.9 MB, 在所有 C 提交中击败了5.27% 的用户
struct ListNode* getKthFromEnd(struct ListNode* head, int k){
    struct ListNode* i = head;
    struct ListNode* j = head;
    if(head = NULL || k == 0){
        return NULL;
    }
    while(k--){
        j = j->next;
    }
    while(j!= NULL){
        i = i->next;
        j = j->next;
    }
    return i;
}
//执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:36.3 MB, 在所有 Java 提交中击败了91.64% 的用户
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode i = head,j = head;
        for(int a = 0;a < k;a++){
            j = j.next;
        }
        while(j != null){
            i = i.next;
            j = j.next;
        }
        return i;
    }
}

【11.5】打印从1到最大的n位数
在这里插入图片描述分析:主要就是如何表达这个“位数”。假如是最大的三位数:10的三次方-1即可。
但是问题又来了,pow函数结果是double类型,因此要强转成int
pow这个函数结果是double类型

//执行用时:1 ms, 在所有 Java 提交中击败了99.99% 的用户
//内存消耗:46.9 MB, 在所有 Java 提交中击败了22.27% 的用户
class Solution {
    public int[] printNumbers(int n) {
        int end = (int)Math.pow(10,n) - 1;
        int[] arr = new int[end];

        for(int i = 1;i <= end;i++){
            arr[i-1] = i; 
        }
        return arr;
    }
}

(大数问题)下面是考虑当n很大的时候,打印出来的数超过INT_MAX范围的情况。
对于大数问题,变量类型用字符串String类型存储。且本题的字符集实际上是n位0-9的全排列,因此可避开进位操作,直接递归生成String列表。
在这里插入图片描述有个疑惑的问题:假如从0开始的话,会不会输出的不是个位数1,2,而是01,02这种?

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值