leetCode-hot100-数组专题之求和+数学定理+其他

数组专题之求和+数学定理+其他

求和

数组求和问题,即计算数组中所有元素的总和,是编程中常见的任务。以下是一些常用的解决方法:
1. 循环遍历

  • 基本思路: 通过循环遍历数组中的每个元素,并将它们累加起来。
  • 实现方式 :
    * for 循环 : 遍历数组索引,访问并累加每个元素。
    * while 循环 : 通过索引或指针遍历数组,访问并累加每个元素。
    * foreach 循环: 遍历数组元素,直接访问并累加每个元素 (适用于 PHP 等语言)。

2. 递归

  • 基本思路: 将数组分为两部分,递归计算每一部分的总和,然后将它们相加。
  • 实现方式: 定义一个递归函数,输入数组及其起始和结束索引,递归计算左右两部分的总和,并返回它们的和。

3. 高级方法

  • 数学公式: 利用数学公式,例如高斯求和公式,直接计算数组中所有元素的总和。
  • 并行计算: 利用多线程或分布式计算,将数组分割成多个部分,并行计算每个部分的总和,最后将它们相加。

4. 哈希表
(1)快速查找和去重:

  • 去重: 通过哈希表,我们可以快速判断一个元素是否已经存在于数组中,从而避免重复计数,这在计算不重复元素的总和时很有用。

  • 快速查找:在某些情况下,我们需要先查找数组中是否存在特定的元素,然后再决定是否将其加入总和。哈希表可以提供快速查找的功能,从而优化整个求和过程。

(2)优化排序数组求和:

  • 前缀和:如果数组是排序的,我们可以利用哈希表来快速计算任意区间内元素的总和。具体方法是将每个元素及其索引存储在哈希表中,然后利用二分查找快速定位到区间起始位置,并计算区间内元素的总和。

(3)复杂求和:

  • 加权求和: 如果数组元素具有不同的权重,我们可以将元素和权重作为键值对存储在哈希表中,然后根据权重计算加权总和。

1.两数之和

思路1:暴力枚举法

暴力枚举法很简单,遍历nums数组的每一个元素,找到和target-nums[i]相同的元素即可。
时间复杂度 :需要使用到两层for循环,所以时间复杂度为O(n^2)
代码实现

java 复制代码
class Solution {
    public int[] twoSum(int[] nums, int target) {
        for(int i=0;i<nums.length;i++){
           for(int j=i+1;j<nums.length;j++){
               if(nums[j]==target-nums[i]){
                   return new int[] {i,j};
               }   
           } 
        }
        return null;
    }
}

思路2:哈希表法

使用哈希表来解决该问题的思路是,创建一个哈希表来存放数据,其中keytarget-nums[i]value为对应的索引。在遍历数组时,将数组中元素的值和对应的索引记录到哈希表中,检查当前元素的补数(target-nums[i])是否在哈希表中,如果在,其value即为所求,若不在,将元素记录到哈希表中并接着遍历数组。
时间复杂度 :只需要遍历一次数组,用到一个for循环,所以时间复杂度为O(n)
代码实现

java 复制代码
class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> tab = new HashMap<>();
        for(int i=0;i<nums.length;i++){
            int tar = target-nums[i];
            if(tab.containsKey(tar)){
                return new int[] {i,tab.get(tar)};
            }else{
                tab.put(nums[i],i);
            }
        }
         return null;
    }
}

15.三数之和

思路

本题采用枚举+双指针的做法,外循环对nums的元素依次遍历,在遍历时寻找可以和外循环元素相加为0的元素对,内层循环采用首尾双指针进行遍历,但是这样做的前提是需要首先对数组进行排序,最后需要注意由于不能输出重复的结果,所以每次遍历时都要跳过相同元素。详细的讲解参考视频讲解-三数之和
时间复杂度

这段代码的时间复杂度为O(n^2),其中n为数组nums的长度。主要的时间复杂度来源于两层循环,外层循环遍历数组nums,内层循环使用双指针法遍历数组中的剩余元素。在最坏情况下,内层循环的时间复杂度为O(n),所以总的时间复杂度为O(n^2)
代码实现

java 复制代码
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        //对数组进行排序,方便采用首尾指针进行遍历
        Arrays.sort(nums);
        for(int i=0;i<nums.length;i++){
            //跳过重复元素
            if(i > 0 && nums[i] == nums[i-1]) continue;
            int l = i + 1;
            int r = nums.length - 1;
            int target = 0 - nums[i];
            while(l < r){
                if(nums[l] + nums[r] == target){
                    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(nums[l] + nums[r] < target){
                    l++;
                }else{
                    r--;
                }
            }
        }
        return ans;
    }
}

知识拓展Java中动态数组的使用(ArrayListList
区别

1.ListArrayList的泛型等效类,List是一个接口,而ArrayList是一个类,它实现了List接口,所以List不能被构造,List list=new List()这种写法是错误的,而ArrayList就可以被构造。List list = new ArrayList();这句创建了一个ArrayList的对象后把向上转型成了List。此时它是一个List对象了,有些ArrayList有但是List没有的属性和方法,它就不能再用了。而ArrayList list=new ArrayList();创建一对象则保留了ArrayList的所有属性。

2.List相比ArrayList来说更加安全,因为ArrayList加入的数据为object类型,需要装箱和拆箱操作。List声明时就决定了类型,所以是类型安全的,省掉了装箱与拆箱的过程,并且效率更高。
用法

1.定义
ArrayList

java 复制代码
ArrayList list1 = new ArrayList();

List

java 复制代码
List<List<Integer>> list2 = new ArrayList<>();

2.添加元素
ArrayList

(1)添加单个元素

java 复制代码
list1.Add(1);

(2)添加多个元素

java 复制代码
list1.addAll(Arrays.asList(a,b,c));

List:

(1)添加单个元素

java 复制代码
list2.Add(1);

(2)添加多个元素

java 复制代码
list2.add(Arrays.asList(a,b,c));

数学定理

169.多数元素

思路1:

使用哈希表来解决,在哈希表中记录每个数组元素出现的次数,以数组元素为key,出现次数为value,遍历数组,如果nums[i]的出现次数大于n/2,则返回该元素,否则,更新该元素在哈希表中的value值。
时间复杂度:

时间复杂度取决于两个主要因素:循环遍历不同元素的次数和在HashMap中添加和访问元素的次数。

  • 在循环遍历数组元素时,需要遍历n个元素,因此时间复杂度为O(n)
  • 在HashMap中添加或访问元素的平均时间复杂度为O(1)。但是在最坏情况下,所有元素都是不同的,因此在HashMap中添加或访问元素的时间复杂度为O(n)
  • 因此,总体时间复杂度为O(n^2)
    需要注意的是,虽然代码中使用了HashMap来记录元素出现的次数,但是在最坏情况下,每次遍历都需要进行线性搜索以查找元素是否已经在HashMap中。这会导致时间复杂度较高。
    代码实现:
java 复制代码
class Solution {
    public int majorityElement(int[] nums) {
        Map<Integer,Integer> record = new HashMap<>();
        int n = nums.length;
        int cpm = n / 2;
        for(int i = 0;i <n;i++){
            int count = record.getOrDefault(nums[i],0) + 1;
            if(count > cpm){
               return nums[i];
            }else{
                record.put(nums[i],count);
            }
        }
        return -1; 
    }
}

思路2:
Boyer-Moore 投票算法:每次都找出一对不同的元素,从数组中删除,直到数组为空或只有一种元素。如果存在元素 e 出现频率超过半数,那么数组中最后剩下的就只有 e。简单来说,就是数组元素之间相互抵消,遇到相同的就加1,不同的就减1,当count'为0的时候就需要更新比较的元素值,说明上一个元素已经被抵消完了,需要换一个出现次数更多的元素,最后剩下来的元素一定是那个多数元素,视频讲解点击视频讲解-多数元素
时间复杂度:

时间复杂度为O(n),其中n是数组nums的长度。
代码实现:

java 复制代码
class Solution {
    public int majorityElement(int[] nums) {
        int ans = -1;
        int count = 0;
        for(int i = 0; i < nums.length;i++){
            if(count == 0){
                ans = nums[i];
                count++;
            }else if(nums[i] == ans){
                count++;
            }else{
                count--;
            }
        }
        return ans;
    }
}

其他

4.寻找两个正序数组的中位数

思路

该题的解决采用二分思想,只需要给出两个有序数组一个恰当的【分割线】,中位数的值就由位于这个【分割线】的两侧的数来决定,确定分割线的位置使用二分查找法,需要注意的点是:分割线左边的所有元素的数值<分割线右边所有元素的数值

注:这里的思路写的比较简单,详细的可以看这个视频详细思路-寻找两个正序数组的中位数
时间复杂度

时间复杂度为O(log min(m,n)),m,n分别为两数组的长度,空间复杂度为O(1)

代码实现:

java 复制代码
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        //将nums1设置为长度较小的数组,方便代码的编写
        if(nums1.length>nums2.length){
            int[] temp = nums1;
            nums1 = nums2;
            nums2 = temp;
        }
        
        int m = nums1.length;
        int n = nums2.length;
        //计算分割线左边元素的数量
        int totalleft = (m+n+1)/2;
        
        //二分法查找nums1部分的分割线
        int left = 0;
        int right = m;
        while(left<right){
            //这是对于(left+right)/2的特殊处理方式,防止发生整型溢出
            //同时使用二分法时,如果出现left=i,则这里需要+1,否则不需要
            int i = left+(right-left+1)/2;
            int j = totalleft - i;
            if(nums1[i-1] > nums2[j]){
                right = i - 1;
            }else{
                left = i;
            }
        }
        int i = left;
        int j = totalleft - i;
        //最后得到两个数组分割线左右两边元素的最大值的最小值
        //为了防止出现分割线左右两边没有元素的极端情况加上判断
        int nums1LeftMax = i ==0 ? Integer.MIN_VALUE : nums1[i-1];
        int nums1RightMin = i ==m ? Integer.MAX_VALUE : nums1[i];
        int nums2LeftMax = j == 0 ? Integer.MIN_VALUE : nums2[j-1];
        int nums2RightMin = j == n ? Integer.MAX_VALUE : nums2[j];
        //计算中位数的值
        if(((m+n) % 2) == 1){
            return Math.max(nums1LeftMax,nums2LeftMax);
        }else{
            return (double) ((Math.max(nums1LeftMax,nums2LeftMax) + Math.min(nums1RightMin,nums2RightMin))) / 2;
        }
    }
}

128.最长连续序列

思路:

本题使用Set求解,将数组中的元素用Set存储,去掉重复元素。我们可以Set中的元素映射到一个数轴上来,可以看到连续的元素会形成一个区间,只要我们找到每个区间的第一个元素,然后通过判断Set中是否包含以该元素开头的连续区间的元素,最后得到区间的长度,然后在所有长度中取最大值即可。
时间复杂度:

时间复杂度为O(n),其中n是数组nums的长度。
代码实现:

java 复制代码
class Solution {
    public int longestConsecutive(int[] nums) {
       Set<Integer> set = new HashSet<>();
       int ans = 0;
       for(int num : nums){
        set.add(num);
       }
       for(int item : set){
        if(!set.contains(item - 1)){
            int x = item + 1;
            while(set.contains(x)) x++;
            ans = Math.max(ans,x - item);
        }
       }
    return ans;
    }
}

知识扩展:
HashSet的使用
HashMap同时也被称为集合,该容器中只能存储不重复的对象,常用来去重。

(1)新建一个HashSet

java 复制代码
HashSet<String> st = new HashSet<String>();

(2)添加元素

java 复制代码
st.add("hello");

(3)删除元素

java 复制代码
st.remove("hello");

(4)判断某个元素是否在Set中

java 复制代码
st.contains("hello");

(5)清空Set

java 复制代码
st.clear();
相关推荐
huapiaoy3 分钟前
Redis中数据类型的使用(hash和list)
redis·算法·哈希算法
liu_chunhai6 分钟前
设计模式(3)builder
java·开发语言·设计模式
冷白白16 分钟前
【C++】C++对象初探及友元
c语言·开发语言·c++·算法
鹤上听雷24 分钟前
【AGC005D】~K Perm Counting(计数抽象成图)
算法
一叶祇秋36 分钟前
Leetcode - 周赛417
算法·leetcode·职场和发展
武昌库里写JAVA41 分钟前
【Java】Java面试题笔试
c语言·开发语言·数据结构·算法·二维数组
ya888g42 分钟前
GESP C++四级样题卷
java·c++·算法
【D'accumulation】1 小时前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端
小叶学C++1 小时前
【C++】类与对象(下)
java·开发语言·c++
2401_854391081 小时前
高效开发:SpringBoot网上租赁系统实现细节
java·spring boot·后端