IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> [LeetCode Hot 100]两数之和、加,无重复最长子串、正序数组中位数 -> 正文阅读

[数据结构与算法][LeetCode Hot 100]两数之和、加,无重复最长子串、正序数组中位数

1.两数之和

力扣第一题,题目描述不再贴上来了。关键是数组无序,方法返回值要求是两数的索引

常规解法:

双重循环

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int n = nums.length;
        for(int i = 0; i < n; i++){
        //序号比i小的元素均与当前i比对过,故只需向后看
           for(int j = i + 1; j < n;j++){
               if(nums[j] == target - nums[i]){
                   return new int[]{i,j};
               }
           }
        }
        return new int[0];
    }
}

HashMap

采用空间换时间的思路,建立值-索引的映射关系。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> hashTable = new HashMap<>();
        for(int i = 0; i < nums.length; i++){
            if(hashTable.containsKey(target - nums[i])){
                //值到索引的映射(题目返回的是索引值)
                return new int[]{hashTable.get(target - nums[i]),i};
            }
            hashTable.put(nums[i],i);
        }
        return new int[0];
    }
}

进阶解法

以下摘自alexhilton的题解

二分法

确定一个数的索引后,在后续的值利用双指针从两头向中间找来节省时间

    public int[] twoSum(int[] nums, int target) {
        int[] result = {0, 1};
        if (nums.length <= 2) {
            return result;
        }

        for (int i = 0; i < nums.length - 1; i++) {
            result[0] = i;//首个索引
            int x = target - nums[i];//当前目标值
            //j从i+1向后找,k从后向前找,直至与j相遇
            for (int j = i + 1, k = nums.length - 1; j <= k; j++, k--) {
                //任一指针找到目标值,即返回指针位置。
                if (nums[j] == x) {
                    result[1] = j;
                    return result;
                }
                if (nums[k] == x) {
                    result[1] = k;
                    return result;
                }
            }
        }
        return result;
    }

四分法

相比于二分法,将固定数,也改为二分法。

    public int[] twoSum(int[] nums, int target) {
        int[] result = {0, 1};
        if (nums.length <= 2) {
            return result;
        }

        //主循环
        for (int i = 0, j = nums.length - 1; i < j; i++, j--) {
            if (nums[i] + nums[j] == target) {
                // Lucky
                result[0] = i;
                result[1] = j;
                return result;
            }

            // should be in [i+1, j-1], find them with double pointers.
            int x = target - nums[i];
            int y = target - nums[j];
            for (int k = i + 1, m = j - 1; k <= m; k++, m--) {
                result[0] = i;
                if (nums[k] == x) {
                    result[1] = k;
                    return result;
                } else if (nums[m] == x) {
                    result[1] = m;
                    return result;
                }

                result[1] = j;
                if (nums[k] == y) {
                    result[0] = k;
                    return result;
                } else if (nums[m] == y) {
                    result[0] = m;
                    return result;
                }
            }
        }

        return result;
    }

时空复杂度分析

双重循环HashMap二分法四分法
时间复杂度O(n2)O(n)O(nlogn)O(2/nlogn)
空间复杂度O(1)O(n)O(1)O(1)

两数相加

链表逆序存储两数对应位,考察链表遍历 + 加法进位的处理。
技巧:dummy虚拟头结点的使用

/**
 * Definition for singly-linked list.
 * 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; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        //创建和链表虚结点
        ListNode dummy = new ListNode(-1);
        //和链表的指针
        ListNode p = dummy;
        //两数链表的指针
        ListNode p1 = l1, p2 = l2;
        //记录进位
        int carry = 0;
        //两数位数不相同,高位补零对齐相加。
        while(p1 != null || p2 != null || carry != 0){
            //val记录当前位置的结果,进位 + 值
            int val = carry;
            if(p1 != null){
                val += p1.val;
                p1 = p1.next;
            }
            if(p2 != null){
                val += p2.val;
                p2 = p2.next;
            }
            carry = val/10;
            val = val % 10; //当前对应位和
            ListNode t = new ListNode(val);
            p.next = t;
            p = p.next;
        }
        
        return dummy.next;
    }
}

最长无重复子串

双指针 + 滑动窗口

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length(), res = 0;
        //记录字符最新位置
        Map<Character, Integer> map = new HashMap<>();
        int left = 0, right = 0;
        while(right < n){
            char cr = s.charAt(right);
            if(map.containsKey(cr)){
                //即前面已出现,左指针移动到最近相同位置后面
                left = Math.max(map.get(cr) + 1, left);
            }
            res = Math.max(res, right - left + 1);
            map.put(cr,right);
            right++;
        }
        
        return res;
    }
}

正序数组中位数

要求时间复杂度为O(log(m+n))
联想到二分法解题。

二分法(官方)

力扣官方

进阶解法:

假设中位数索引值为k
利用中位数特点与第k小结合;
在长度更短的数组里寻找最大的索引m(其下标不超过K),其小于更长数组k - m -1下标处的值。
m-1、k-m-1分别锁定了两个数组处于前K个最小数里的最大值的下标。
由中位数特点可知:
当两数组长度之和为奇数时,应在边界附近(此解法在边界左侧)
当两数组长度之和为偶数时,应取边界左侧(前K个数最大值)和右侧(两数组在前K个数里最大值下标+1后的最小值)的平均值。
时间复杂度:O(log min(m,n))

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int m = nums2.length;
        if(n>m){
            return findMedianSortedArrays(nums2, nums1);
        }
    
        int k = (n + m + 1)/2;//奇数中位数落在左边
        int left = 0;
        int right=n;

        //二分法寻找nums1中满足<nums[m2]的最大下标
        while(left < right){
            int m1 = left + (right - left) /2;
            int m2 = k - m1;
            if(nums1[m1] < nums2[m2 - 1]){
                left = m1 + 1;
            }
            else{
                right = m1;
            }
        }
        
        //m1-1是前K个最小数中nums1的最大下标,即最靠近中位数边界
        int m1 = left;
        int m2 = k - left;
        int c1 = Math.max(m1 <= 0 ? Integer.MIN_VALUE : nums1[m1-1],
                          m2 <= 0 ? Integer.MIN_VALUE : nums2[m2-1]);
        //奇数,中位数落在边界左边,必然是组成前K个里,nums1和nums2中下标更大的。
        if((n + m)%2 ==1) return c1;

        int c2 = Math.min(m1 >= n ? Integer.MAX_VALUE : nums1[m1],
                          m2 >= m ? Integer.MAX_VALUE : nums2[m2]);
        //偶数,边界平均值,中位数取前K个数的最大值和K+1的值(nums1/nums2中更小的数)的平均值
        return (c1 + c2) * 0.5;

    }   
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 19:09:52  更:2022-07-20 19:11:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/21 8:22:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码