leecode刷题经验(java/C)持续更新

双指针

例题1

给你一个 非严格递增排列 的数组 nums ,请你** 原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k
class Solution {
    public int removeDuplicates(int[] nums) {
    /*    TreeSet<Integer> set = new TreeSet<>();
        for(int i=0;i<nums.length;i++){
            set.add(nums[i]);
        }
        Iterator<Integer> iterator = set.iterator();
        int temp=0;
        while(iterator.hasNext()){
            nums[temp] = iterator.next();
            temp++;
        }

        return set.size();
        */
        int p=0;
        int q = 1;
        if(nums==null||nums.length==0)return 0;
        while(q<nums.length){
            if(nums[p]!=nums[q]){
                nums[p+1]=nums[q];
                p++;
            }
            q++;
        }
        return p+1;
    }
}

复杂度分析:

时间复杂度:O(n)。 空间复杂度:O(1)

优化思路

  • 此数组没有重复元素,按照上面方法,每次比较nums[p]都不等于nums[q],因此会将q指向的元素原地复制一遍,这个操作是没有必要的
  • 因此我们可以添加一个小判断,当q-p>1时,在进行复制 这样就可以避免没有必要的重复
public int removeDuplicates(int[] nums) {
    if(nums == null || nums.length == 0) return 0;
    int p = 0;
    int q = 1;
    while(q < nums.length){
        if(nums[p] != nums[q]){
            if(q - p > 1){
                nums[p + 1] = nums[q];
            }
            p++;
        }
        q++;
    }
    return p + 1;
}

思路如图

效率明显比使用树结构要高

例题2

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2。请你找出并返回这两个正序数组的 中位数

算法的时间复杂度应该为 O(log (m+n))

class Solution {
       /*public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int[] sumNums = new int[nums1.length+nums2.length];
        System.arraycopy(nums1,0,sumNums,0,nums1.length);
        System.arraycopy(nums2,0,sumNums,nums1.length,nums2.length);
        Arrays.sort(sumNums);
        if(sumNums.length%2==0){
            return (double) (sumNums[(int)sumNums.length/2-1]+sumNums[(int)sumNums.length/2])/2;
        }
        else{
            return (double)sumNums[(int)sumNums.length/2];
        }

    }*/
    public static double findMedianSortedArrays(int[] nums1, int[] nums2){
        if(nums1.length>nums2.length){
           //return findMedianSortedArrays(nums2,nums1);//这样占内存大
           int []temp = nums1;
           nums1 = nums2;
           nums2 = temp;
        }
        int m = nums1.length;
        int n = nums2.length;       
        int left = 0,right = m;

        //median1 前一部分的最大值
        //median2 后一部分的最小值
        int median1 = 0 , median2 = 0;

        while (left <= right){
            //前一部分包括nums1[0..i-1]& nums2[0..j-1];
            //后一部分包括nums1[i..m-1]& nums2[j..n-1];
            int i = ( left + right ) / 2;
            int j = ( m + n + 1 ) / 2 - i;
            
            //nums_im1,nums_i,nums_jm1,nums_j分别表示 nums1[i-1],nums1[i],nums2[j-1],nums2[j]
            int nums_im1 = (i==0? Integer.MIN_VALUE:nums1[i-1]);
            int nums_i = (i==m?Integer.MAX_VALUE:nums1[i]);
            int nums_jm1 = (j==0?Integer.MIN_VALUE:nums2[j-1]);
            int nums_j = (j==n?Integer.MAX_VALUE:nums2[j]);

            if(nums_im1<=nums_j){
                median1 = Math.max(nums_im1,nums_jm1);
                median2 = Math.min(nums_i,nums_j);
                left = i + 1;
            }else{
                right = i - 1;
            }
            
        }
        return ( m + n ) % 2 == 0 ? ( median1 + median2 ) / 2.0 : median1;
    }

}

 
    

思路

使用划分的思想

4. 寻找两个正序数组的中位数 - 力扣(LeetCode)

例题2 三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

class Solution {
  public List<List<Integer>> threeSum(int[] nums) {
       List<List<Integer>> ans = new ArrayList();
       int len = nums.length;
       if(nums == null||len<3) return ans;
       Arrays.sort(nums);
       for(int i=0;i<len;i++){
           if(nums[i]>0) break;//如果当前位置数值大于零则三数之和肯定大于零(因为排序过)
           if(i>0&&nums[i]==nums[i-1]) continue;
           int L = i+1;
           int R = len-1;
           while(L<R){  //使用了双指针的方法
               int sum = nums[i] + nums[L] + nums[R];
               if(sum==0){
                   ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
                   while (L<R && nums[L]==nums[L+1]) L++;
                   while (L<R && nums[R]==nums[R-1]) R--;
                   L++;
                   R--;

               }
               else if(sum<0)L++;
               else if(sum>0)R--;
           }
           
       }
       return ans;
  }
}

思路

  • 判断数组是否为空或长度是否满足

  • 对数组进行排序(关键)

  • 遍历排序后的数组

    • 先判断第一个数的大小

      • 如果第一个数大于0,那么后面就不需要看(因为排序过)
    • 创建左右指针L和R去寻找后面两个数(L<R)

      • 如果sum=0;则继续判断L和R下一个位置是否与现在数值重复,如果重复L++,R - -;跳过重复值

      • 若sum大于0,说明右指针指的数值大,则左移,R–

      • 若sum小于0,说明左指针指的数值小,则右移,L++

复杂度分析

  • *时间复杂度: O(n^2),数组排序O(N log N),遍历数组O(n),双指针遍历O(n),总体O(N log N)+O(n)O(n)
  • 空间复杂度:O(n),对数组进行了排序

例题3 四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abcd 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list = new ArrayList<>();
        Arrays.sort(nums);//对数组进行排序处理
        int len = nums.length;//数组长度
        if (nums == null || len < 4) return list;

        for (int i = 0; i < len - 3; i++) {
            //if (nums[i] > target) break; 错误的,如果是负数,则会漏掉其他情况
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            if ((long)nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) break;//后面肯定大于target
            if ((long)nums[i] + nums[len - 3] + nums[len - 2] + nums[len - 1] < target) continue;
            for (int j = i + 1; j < len - 2; j++) {
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                if ((long)nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) break;
                if ((long)nums[i] + nums[j] + nums[len - 2] + nums[len - 1] < target) continue;//提上已知数值要大于int,所以应该强制转换为long

                int L = j + 1, R = len - 1;
                while (L < R) {
                    long sum = (long)nums[i] + nums[j] + nums[L] + nums[R];
                    if (sum == target) {
                        list.add(Arrays.asList(nums[i], nums[j], nums[L], nums[R]));
                        while (L < R && nums[L] == nums[L + 1]) L++;
                        while (L < R && nums[R] == nums[R - 1]) R--;
                        L++;
                        R--;
                    }
                    else if(sum<target)L++;
                    else if(sum>target)R--;
                }
            }

        }

        return list;
    }
}

思路

  • 与三数之和类似,多了一个for循环

复杂度分析

  • 时间复杂度:O(n^3),其中n是数组的长度,排序时间O(n log n),枚举四元组的时间复杂度是O(n3),因此总时间复杂度为O(n3+n log n) = O(n^3)
  • 空间复杂度:O(log n) ,其中n是数组长度.空间复杂度主要取决于排序额外使用的空间,此外排序修改了输入数组nums,实际情况中不一定允许,因此也可以看做成使用了一个额外数组存储nums,空间复杂度为O(n)

递归

概念

递归是指一个函数在其定义中调用自身的过程。简单来说,就是一个函数在执行过程中调用自身来解决问题的方法。

例题1 合并双链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

img

 public class ListNode {
        int val;
        ListNode next;

        ListNode() {
        }

        ListNode(int val) {
            this.val = val;
        }

        ListNode(int val, ListNode next) {
            this.val = val;
            this.next = next;
        }
    }
      

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

解题思路

使用递归思想

终止条件:当两个链表都为空时,表示我们对链表已合并完成。
如何递归:我们判断 list1 list2 头结点哪个更小,然后较小结点的 next 指针指向其余结点的合并结果。(调用递归)

. . . . . . . .

重复下去…

直到其中一个链表为null

枚举

枚举是一种数据类型,用于定义一组有限的命名常量。枚举类型可以包含多个枚举成员,每个枚举成员都有一个唯一的名称和一个对应的值。枚举成员的值可以是整数、字符或字符串等类型。枚举类型可以用于表示一组相关的常量,比如星期几、月份、颜色等。在编程中,枚举常常用于提高代码的可读性和可维护性,以及减少错误的可能性。

例题1 统计一个圆中点的数目

给你一个数组 points ,其中 points[i] = [xi, yi] ,表示第 i 个点在二维平面上的坐标。多个点可能会有 相同 的坐标。

同时给你一个数组 queries ,其中 queries[j] = [xj, yj, rj] ,表示一个圆心在(xj, yj)且半径为 rj 的圆。

对于每一个查询 queries[j] ,计算在第 j 个圆 内 点的数目。如果一个点在圆的 边界上 ,我们同样认为它在圆 内 。

请你返回一个数组 answer ,其中 answer[j]是第j个查询的答案。

URL:https://leetcode.cn/problems/queries-on-number-of-points-inside-a-circle/

public int[] countPoints(int[][] points, int[][] queries) {

       //枚举
        int len = queries.length;
        int[] answer = new int[len];
        for(int j = 0;j<len;j++){
            int temp=0;
            for(int i=0;i< points.length;i++){
                double x = Math.sqrt((points[i][0]-queries[j][0])*(points[i][0]-queries[j][0])+(points[i][1]-queries[j][1])*(points[i][1]-queries[j][1]));
            }
            answer[j]=temp;
        }
        return answer;
    }
}
//简单优化一下,减少对包的调用和对对象的创建
int[] answer = new int[queries.length];
        for(int j = 0;j<queries.length;j++){
            int temp=0;
            for(int i=0;i< points.length;i++){
                int x =((points[i][0]-queries[j][0])*(points[i][0]-queries[j][0])+(points[i][1]-queries[j][1])*(points[i][1]-queries[j][1]));
                  if(x<=queries[j][2]*queries[j][2]){
                      temp++;
                  }
            }
            answer[j]=temp;
        }
        return answer;
    }//时间减半

思路

枚举每一个情况,使用数学中圆的公式去限制条件
( c x − p x ) 2 + ( c y − p y ) 2 < = c 2 (c_x-p_x)^2+(c_y-p_y)^2<=c^2 (cxpx)2+(cypy)2<=c2

复杂度分析

  • 时间复杂度 O(mn),其中m与n是points和queries的长度
  • 空间复杂度:O(1)

[j][1])*(points[i][1]-queries[j][1]));
if(x<=queries[j][2]*queries[j][2]){
temp++;
}
}
answer[j]=temp;
}
return answer;
}//时间减半


### 思路

**枚举每一个情况,使用数学中圆的公式去限制条件**
$$
(c_x-p_x)^2+(c_y-p_y)^2<=c^2
$$

### 复杂度分析

- **时间复杂度 `O(mn)`,其中m与n是points和queries的长度**
- **空间复杂度:`O(1)`**

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刺猬崔‍️

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值