Leetcode 题解

1.两数之和

题目:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

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

方法一:暴力破解

class Solution {
public:
  vector<int> twoSum(vector<int>& nums, int target) {
   for(int i=0;i<nums.size();i++)
   {
    for(int j=i+1;j<nums.size();j++)
    {
      if (nums[i]+nums[j]==target)
       {
       return{i,j};
       }
    }
  }
   return{};
  }
};

方法二:哈希表

当要判断该元素是否出现过,就考虑使用哈希表

可以使用数组和set作哈希表的映射,但是在该题中需要存放元素以及其下标,使用map数据结构

map: key--->元素,value--->下标

因为需要查找元素是否出现过,因此,将元素作为key

map: map、unordered_map、multi_map 13是实现红黑树,2是哈希结构,进行映射

本题使用2,存读速率最快

class Solution {
    public int[] twoSum(int[] nums, int target) {
        // 创建一个哈希表,用于存储数组元素及其下标
        // 键:数组中的元素值,值:该元素在数组中的下标
        Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
        
        // 遍历数组中的每个元素
        for (int i = 0; i < nums.length; ++i) {
            // 计算当前元素的互补数,即 target 减去当前元素的值
            // 如果存在另一个元素等于互补数,则两数之和为 target
            int complement = target - nums[i];
            
            // 检查哈希表中是否已存在互补数
            if (hashtable.containsKey(complement)) {
                // 如果存在,说明找到了两个数的和为 target
                // 返回互补数的下标(存储在哈希表中)和当前元素的下标 i
                return new int[]{hashtable.get(complement), i};
            }
            
            // 如果哈希表中不存在互补数
            // 将当前元素的值及其下标存入哈希表,供后续查找使用
            // 注意:这一步必须放在检查之后,以避免重复使用同一个元素
            hashtable.put(nums[i], i);
        } 
        // 如果遍历完整个数组都没有找到符合条件的两个数
        // 返回一个空数组表示无解
        return new int[0];
    }
}

2.两数相加

题目:给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

分析:2+5=7,没有进位,直接利用尾插法插入新链表;

4+6=10,两位数,有进位

(1)取个位:利用取模运算,10%10=0 ,直接插入新链表

(2)取进位值:利用除法运算,10/10=1,存入进位,累加到下一个数

3+4=7,还要加上前面的进位

前提条件:l1 l2不为空

使用迭代法,total=l1.val+l2.val+next(进位);

取个位:total%10

取进位:total/10

前提条件:l1 不为空,l2为空

赋l2.val=0,其余不变

前提条件:l2 不为空,l1为空

赋l1.val=0,其余不变

前提条件:都为空,但进位值为1

存入最后一个结点,值为1

代码:

           int total=0;
            int next=0;    //初始化
            ListNode *result=new ListNode(0); //创建一个虚拟头结点
            ListNode *cur=result; //当前结点指针,使用指针对后续值进行改变
​
            while(l1!=NULL&&l2!=NULL)
            {
                total=l1->val+l2->val+next;
                l1=l1->next;  //指向下一个  
                l2=l2->next;  //指向下一个 
                cur->next=new ListNode(total%10);  //将结果链表的指针指向 创建的一个结点(这个结点存放的是两数之和)
                cur=cur->next; //移动当前结点指针
                next=total/10; //next存放进位
            }
            while(l1!=NULL&&l2==NULL) //l2遍历完了
            {
                total=l1->val+next;  //不再加l2的值
                l1=l1->next;  //指向下一个   
                cur->next=new ListNode(total%10);  //将结果链表的指针指向 创建的一个结点(这个结点存放的是两数之和)
                cur=cur->next; //移动当前结点指针
                next=total/10; //next存放进位
            }   
            while(l2!=NULL&&l1==NULL) //l1遍历完了
            {
                total=l2->val+next;  //不再加l1的值
                l2=l2->next;  //指向下一个   
                cur->next=new ListNode(total%10);  //将结果链表的指针指向 创建的一个结点(这个结点存放的是两数之和)
                cur=cur->next; //移动当前结点指针
                next=total/10; //next存放进位
            }   
            if (next==1)
            {
                cur->next=new ListNode(1);  //存入进位
                cur=cur->next; //移动当前结点指针
            }
        return result->next; //返回结果链表的头结点

简化代码:

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        int total=0;
        int next=0;    //初始化
        ListNode *result=new ListNode(0); //创建一个虚拟头结点
        ListNode *cur=result; //当前结点指针,使用指针对后续值进行改变
        while(l1!=NULL||l2!=NULL)  //只有当二者都为空,才退出循环
        {
            int a;
            int b;
            if(l1==NULL)
                a=0;
            else
                a=l1->val;
            if(l2==NULL)
                b=0;
            else
                b=l2->val;
            total=a+b+next;
           if(l1!=NULL)
            l1=l1->next;  //指向下一个  
            if(l2!=NULL)
            l2=l2->next;  //指向下一个 
            cur->next=new ListNode(total%10);  //将结果链表的指针指向创建的一个结点(存放的是两数之和)
            cur=cur->next; //移动当前结点指针
            next=total/10; //next存放进位
        }  
            if (next==1)
        {
            cur->next=new ListNode(1);  //存入进位
            cur=cur->next; //移动当前结点指针
        }
    return result->next; //返回结果链表的头结点
}
};

3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3

思路分析:

遍历字符串,需要判断字符是否出现过,这就想到了第一题官方题解使用的哈希表

通过依次从第一位、第二位.(i)..开始遍历,若在哈希表中不存在,则将字符放入哈希表,若存在则将当前哈希表的大小数值存入result数组中,然后将哈希表清空,从下一位开始遍历,直至遍历完整个字符串

取数组的最大值即为结果

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        vector<int> results;                // 存放多个结果
        unordered_map<char, int> hashTable; // 创建hash表,数据类型是char字符型
        if (s.length() == 0) {
            return 0; //""
        } else if (s.length() == 1) {
            return 1;   //" " //"a".......
        } else if (s.length() > 1) {
            for (int i = 0; i < s.length(); i++) {
                for (int j = i; j < s.length(); j++) {
                    if (hashTable.find(s[j]) ==
                        hashTable
                            .end()) // 不在哈希表,find()方法只能查找键(key)不能直接查找值
                        hashTable.insert({s[j], 1}); // 插入
                    else {
                        results.push_back(hashTable.size());
                        hashTable.clear();
                        break;
                    }
                }
            }
        }
            int maxVal = *max_element(results.begin(), results.end());
            return maxVal;
    }
};
​

使用set的题解

1.不能存放相同元素,使用集合set这一数据结构,并且只需要一个参数:字符,刚才的哈希值需要键和值;

2.使用左右指针:左指针在每个字符串的开头,右指针向右滑动,取字符,若能放入set,表明没有重复,放入set;若不能放入,说明重复,左右指针间的长度就是当前字符串的长度;然后取出左指针的字符,左指针再+1,右指针再滑动,重复该步骤;

3.定义length和 max_length:每次指针滑动(不重复),length就+1,+1后与max_length比较,若小于max_length,max_length不变;若大于max_length,max_length更新为当前length (通过设置两个值,来找最大数,大于就更新)

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_set<char> occ; // 定义集合,用来判断是否出现过
        int length=0;
        int maxlength=0;
        int j=0;//右指针
            for (int i = 0; i < s.length(); i++) {  //左指针
                while(j<s.length())
                {
                    if (occ.count(s[j]) == 0) {  //如果集合里没有出现过
                        occ.insert(s[j]);   //插入该值
                        j++;   //右指针移动
                        length++;   //长度+1
                        if (length >= maxlength) 
                                maxlength =length;   //获取最大长度
                    } else {    //集合中出现过
                        length--;   //左指针之后要移动,提前将长度-1
                        occ.erase(s[i]);   //删除左指针对应的字符
                        break;   //跳出内循环,左指针移动
                    }
                }
            }
        return maxlength;
    }
};

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

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

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

题解一:空间复杂度O(m+n)

分析:

1.先对数组排序,使用vector迭代器,直接使用merge和sort()方法对两个数组进行合并以及排序

2.偶数个数的中位数是中间两个数相加再除以2 长度/2 长度/2+1

奇数个数的中位数是中间那个数 长度/2+1

3.在迭代器中,取第n个数利用起始迭代器,移位进行寻找第n个,代码如下:

        auto it = result.begin();   //起始迭代器
        advance(it, n-1);      //寻找第n个数

完整代码:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        vector<int> result(nums1.size() + nums2.size());
        
        merge(nums1.begin(),nums1.end(), nums2.begin(),nums2.end(), result.begin()); //合并
        sort(result.begin(),result.end());   //排序
        
        auto it = result.begin();
        advance(it, 1);
        if (result.size()%2==1)
            {
            auto it = result.begin();
            advance(it, result.size()/2);
            return *it;
            }
        else{
            auto a=next(result.begin(), result.size()/2-1);
            auto b=next(result.begin(), result.size()/2);
            return (float(*a)+float(*b))/2;
            }
    }
};

题解二(官方题解):空间复杂度O(log (m+n))

以下思路及图片来自官方题解

分析:

1.竖线分割

对于两个数组,可以使用两条竖线进行分割

对于个数和为偶数的两个数组:两条竖线左边的最大值和右边的最小值之和/2 就是中位数

对于个数和为偶数的两个数组:两条竖线左边的最大值 就是中位数 (前提是竖线左边的个数是(总数+1)/2,也可以是右边的最小数)

2.怎么分割竖线?

(1)竖线左边的大小

对于偶数个数和:左边大小为(m+n)/2 =(m+n+1)/2 (因为在运算中,/向下取整)

对于奇数个数和:左边大小为(m+n+1)/2

(2)竖线要满足的关系

第一个数组左边的最大值要 小于 第二个数组右边的最小值

第二个数组左边的最大值要 小于 第一个数组右边的最小值

这样才能保证:竖线左边的值都小于右边的值

(3)两个极端情况

a.竖线在较短数组的左/右没有元素

竖线在较短的数组左边没有元素

竖线在较短的数组右边没有元素

因此,应该在较短的数组上确定竖线的位置

b.对于两个长度相同的数组,竖线在数组的左/右没有元素

代码:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        // 优化:确保nums1是较短的数组,减少二分查找的范围
        if (nums1.size() > nums2.size()) {
            return findMedianSortedArrays(nums2, nums1);
        }  
        
        int m = nums1.size();  // 较短数组的长度
        int n = nums2.size();  // 较长数组的长度
        int left = 0, right = m;  // 二分查找的左右边界
        
        // median1:合并后数组左半部分的最大值
        // median2:合并后数组右半部分的最小值
        int median1 = 0, median2 = 0;
​
        // 二分查找在nums1中合适的分割点
        while (left <= right) {       
            // 在nums1中选取分割点i,将nums1分为两部分
            int i = (left + right) / 2;
            // 在nums2中选取分割点j,使得i+j = (m+n+1)/2
            // 这样左半部分的元素个数等于或比右半部分多一个
            int j = (m + n + 1) / 2 - i;     
​
            // 处理边界情况,获取分割点左右的元素值
            // 如果分割点在数组边界,使用INT_MIN或INT_MAX作为哨兵值
            int nums_im1 = (i == 0 ? INT_MIN : nums1[i - 1]);  // nums1左半部分的最大值
            int nums_i = (i == m ? INT_MAX : nums1[i]);        // nums1右半部分的最小值
            int nums_jm1 = (j == 0 ? INT_MIN : nums2[j - 1]);  // nums2左半部分的最大值
            int nums_j = (j == n ? INT_MAX : nums2[j]);        // nums2右半部分的最小值
​
            // 检查分割是否正确:左半部分的所有元素都应小于等于右半部分的所有元素
            if (nums_im1 <= nums_j) {
                // 分割正确,更新中位数候选值
                median1 = max(nums_im1, nums_jm1);  // 左半部分的最大值
                median2 = min(nums_i, nums_j);      // 右半部分的最小值
                left = i + 1;  // 尝试在右半部分寻找更好的分割点
            } else {
                // 分割不正确,需要将i向左移动
                right = i - 1;
            }
        }
​
        // 根据总元素个数的奇偶性返回中位数
        // 如果总元素个数为偶数,返回左右两部分的平均值
        // 如果总元素个数为奇数,返回左半部分的最大值
        return (m + n) % 2 == 0 ? (median1 + median2) / 2.0 : median1;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值