《1024程序节-算法修行》无重复字符的最长子串的《四种解决方案》

简介: 《1024程序节-算法修行》无重复字符的最长子串的《四种解决方案》

image.png

目录

🍇 一、前言

🍉 二、无重复字符的最长子串

🍊 三、题目分析

🍋 四、通关方式一:暴力破解

🍍 五、通关方式二:滑动窗口法

🍎 五、通关方式三:滑动窗口法优化(一)

🍑 七、通关方式四:字符与ASCII映射

🍓 八:算法思想在实际业务中的使用

🍅 九:写在最后

image.png🍇 一、前言


 1024程序节,祝大家节日快乐呀!



 最近有小伙伴和我谈心,觉得刷算法题太难了,完全没有思路,很有挫败感,想要放弃了。想想自己也深有感触,有这些想法真都挺正常的,其实我们刷算法就是为了培养一个思考问题、解决问题的思维,这个思维养成并不是一蹴而就的,而是循序渐进的。 所以,这个是成长的必经之路,取经还要经历九九百十一难呢,但只要坚持下来,每天都是一个重生的自己,小白变成大神的路注定不会简单,但你愿意一直碌碌为为,还安慰自己平凡可贵?



 坚持下来,就能剥茧成蝶!请继续跟小诚一起学习,小诚会尽自己最大的努力,将每道算法题从题目剖析到解决方案都讲得更加通俗理解,与大家共同成长。如果大家刷题时,感觉到挫败了,想要放弃了,欢迎找小诚吐槽,小诚会给你充满能量再出发!

image.png

image.png

🍉 二、无重复字符的最长子串

  心里话讲完了,来看看今天遇到的Boss: 《无重复字符的最长子串》。

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

  示例:

示例 1:
输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例三:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
示例4:
输入: s = ""
输出: 0

通过提示:

0 <= s.length <= 5 * 104

s 由英文字母、数字、符号和空格组成

image.png

🍊 三、题目分析


 这个题目看起来其实相对来说比较简单,但是如果不能很好的理解它其中包含的细节,也很容易被误导方向。



 细节一:子串和子序列的区别



 子串: 子串是必须能够从原字符串中找到的,在原字符串必须连续的一段。如:字符串abc中ab、bc都为子串。



 子序列: 原序列中可以不连续的一段字符,如;字符串abc中ac即为字符序列



 细节二:字符串由英文字母、数字、符号和空格组成



 字符串不包含中文,这样方便我们使用第四种解决方案(猜猜是啥)

image.png

🍋 四、通关方式一:暴力破解


 方案介绍: 暴力破解,通过多个循环以穷举的方式 找出答案。简单且易想到的方案,包括之前我们刷的两道算法题也可以使用暴力破解来解决,缺点就是虽然简单,但是效率非常低,并非是最优方案。



 具体思路: 将字符串每个字符作为一个循环,和子串的组合进行比较,从而获取最长字串长度。如字符串abc,则第一轮比较为: 字符 a和子串a、ab、abc比较,第二轮则为字符b和子串b、bc比较,以此类推,最后获取不重复的子串长度。



 解题代码:

 /**
     * 方案一:暴力破解
          *
          * @param s
               * @return
               */
public static Integer lengthOfLongestSubstring(String s) {
            int maxLength = 0;
            //  每轮是用一个字符和子串进行比较,如果不存在重复字符则获取最大长度,知道最后一个字符位置
            for (int i = 0; i < s.length(); i++) {
                for (int j = i + 1; j <= s.length(); j++) {
                if (!judgeCharacterExist(i, j, s)) {
                    maxLength = Math.max(maxLength, j - i);
                }
                }
            }
            return maxLength;
}
/**
 * 判断子串中是否存在重复字符
 *
 * @param start
 * @param end
 * @param param
 * @return
 */
private static Boolean judgeCharacterExist(int start, int end, String param) {
    HashSet<Character> hashSet = new HashSet<>();
    for (int i = start; i < end; i++) {
        if (hashSet.contains(param.charAt(i))) {
            return true;
        }
        hashSet.add(param.charAt(i));
    }
    return false;
}

算法时间复杂度推导:



 通过上面实现的代码可知,随着输入问题规模增大(既字符串长度编程),需要进行判断的逻辑之间的函数为f(n) = n3,根据大O记法可推导出,暴力破解算法的时间复杂度为O(n3),三次方的函数,可想而知当输入规模变大时它的效率有多低了。



 解题结果: 放到leetcode上执行代码是没有问题的,但是提交的时候会提示超过,因为提交时需要验证987个案例,有些案例字符串包含字符非常多,最后导致执行超时,因此这个方案是绝对不推荐的!


image.png

image.png

🍍 五、通关方式二:滑动窗口法


 滑动窗口:是数组和字符串中一个抽象的概念,可以分为滑动和窗口两个概念理解。


 窗口:即表示一个范围,通常是字符串和数组从开始到结束两个索引范围中间包含的一系列元素集合。 如字符串abcd,如果开始索引和结束索引分别为0、2的话,这个窗口包含的字符则为:abc。



 滑动:它表示窗口的开始和结束索引是可以往某个方向移动的。 如上面的例子开始索引和结束索引分别为0、2的话,当开始索引和结束索引都往右移动一位时,它们的索引值则分别为1、3,这个窗口包含的字符为:bcd。



 下面使用具体的图片来更加形象地认识“滑动窗口”:

image.png

介绍完“滑动窗口”的概念后,下面我们来看看如何通过这个方式来解决无重复字符的最长子串问题,思路如下:



 使用一个HashSet来实现滑动窗口,用来检查重复字符。 维护开始和结束两个索引,默认都是从0开始,然后随着循环【向右移动结束索引】,遇到不是重复字符则放入窗里,遇到重复字符则【向右侧移动开始索引】,最终得到结果,下面来看具体图解:



 代码如下:

    /**
     * 方案二:滑动窗口
          *
          * @param s
               * @return
               */
public static Integer lengthOfLongestSubstring2(String s) {
            int maxLength = 0;
            // 左指针
            int leftPoint = 0;
            // 右指针
            int rightPoint = 0;
            // 用于判断重复的Set集合(窗口)
            Set<Character> set = new HashSet<>();
            while (leftPoint < s.length() && rightPoint < s.length()) {
                if (!set.contains(s.charAt(rightPoint))) {
                // 不存在重复字符时将字符存储入set,并将右边指针向后移动一位
                set.add(s.charAt(rightPoint++));
                maxLength = Math.max(maxLength, rightPoint - leftPoint);
                } else {
                // 存在重复元素,则将左边指针向右移动一位(删除与当前相同的字符)
                set.remove(s.charAt(leftPoint++));
                }
            }
            return maxLength;
}

image.png

算法时间复杂度推导:


  通过上面实现的代码可知,随着输入问题规模增大(既字符串长度编程),需要进行判断的逻辑之间的函数为f(n) = n,根据大O记法可推导出,滑动窗口算法的时间复杂度为O(n),相比于暴力破解,它的效率大大提高了。


  执行结果:

image.png

image.png

🍎 五、通关方式三:滑动窗口法优化(一)

  虽然方式使用了滑动窗口时间复杂度只有O(n),但是如果存在重复字符还需要移动开始索引,因此我们可以考虑借助之前其他算法谈到的“空间换时间”的想法,通过借助HashMap建立字符和索引映射,避免手动移动索引。

  代码如下:

 /**
     * 滑动窗口优化一
     *
     * @param s
     * @return
     */
    public static Integer lengthOfLongestSubstring(String s) {
        char[] charArray = s.toCharArray();
        // 使用HashMap来建立字符和索引的映射
        HashMap<Character, Integer> keyMapping = new HashMap<>();
        Integer maxLength = 0;
        // 开始索引
        Integer leftIndex = 0;
        for (int i = 0; i < charArray.length; i++) {
            if (keyMapping.containsKey(charArray[i])) {
              //  存在重复数据则获取索引值最大的
                leftIndex = Math.max(leftIndex, keyMapping.get(charArray[i]));
            }
            // 值重复则进行覆盖
            keyMapping.put(charArray[i], i + 1);
            maxLength = Math.max(maxLength, i - leftIndex + 1);
        }
        return maxLength;
    }

算法时间复杂度推导:



 通过上面实现的代码可知,随着输入问题规模增大(既字符串长度编程),需要进行判断的逻辑之间的函数为f(n) = n,根据大O记法可推导出,滑动窗口优化算法的时间复杂度为O(n), 虽然和上一种方案的时间复杂度是一样的,但是效率还是有一定的提高(思考问题时要思考是否还有其他有优质的方案,培养发散思维)。



 执行结果:

image.png

image.png

🍑 七、通关方式四:字符与ASCII映射


 上面题目分析的时候就提到了要注意题目中提到的:【字符串英文字母、数字、符号和空格组成】,这些字符是可以使用ASCII表示(如字符a的ASCII值为97,想具体了解的可以百度下),那么我们就可以建议字符与ASCII的映射关系,从而实现重复字符的排除。



 代码如下:

public static Integer lengthOfLongestSubstring(String s) {
        int[] index = new int[128];
        int len = 0;
        int length = s.length();
        for (int i = 0, j = 0; j < length; j++) {
            // 如果存在重复字符,则获取最大的下标
            i = Math.max(index[s.charAt(j)], i);
            // 每轮都拿之前与【当前字符一样】最大的下标跟当前下标的差做为最大的字串长度
            len = Math.max(len, j - i + 1);
            index[s.charAt(j)] = j + 1;
        }
        return len;
    }

算法时间复杂度推导:


  通过上面实现的代码可知,随着输入问题规模增大(既字符串长度编程),需要进行判断的逻辑之间的函数为f(n) = n,根据大O记法可推导出,该算法的时间复杂度为O(n)。


  执行结果:

image.png

image.png

🍓 八:算法思想在实际业务中的使用


  1、滑动窗口算法常用于解决字符串、数组中涉及子元素的一些问题。如本题中的查找无重复字符串最长子串长度。


  2、滑动窗口算法也可以用于优化for循环,将多重循环转换成单层循环,用于降低时间复杂度,提高执行性能。

image.png

🍅 九:写在最后


 一个问题,有些时间不要只纠结于一种解法,可以通过发散思维去多方面考虑,寻找解决方案。发散思维也不是一开始就有,需要不断的联系,积累。因此,每刷一道题,都需要学会自我总结,转换成自己的东西。



 时光不负有心人!如果文章如您有帮助,欢迎给我点赞、关注、收藏(一键三连)。


相关文章
|
1月前
|
算法 前端开发 数据处理
小白学python-深入解析一位字符判定算法
小白学python-深入解析一位字符判定算法
48 0
|
3月前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
102 1
|
3月前
|
算法
【算法】位运算算法——判断字符是否唯一
【算法】位运算算法——判断字符是否唯一
|
3月前
|
算法
【算法】滑动窗口——无重复字符的最长子串
【算法】滑动窗口——无重复字符的最长子串
|
8天前
|
机器学习/深度学习 人工智能 监控
智慧交通AI算法解决方案
智慧交通AI算法方案针对交通拥堵、违法取证难等问题,通过AI技术实现交通管理的智能化。平台层整合多种AI能力,提供实时监控、违法识别等功能;展现层与应用层则通过一张图、路口态势研判等工具,提升交通管理效率。方案优势包括先进的算法、系统集成性和数据融合性,应用场景涵盖车辆检测、道路环境检测和道路行人检测等。
|
22天前
|
算法 测试技术 开发者
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗;代码审查通过检查源代码发现潜在问题,提高代码质量和团队协作效率。本文介绍了一些实用的技巧和工具,帮助开发者提升开发效率。
25 3
|
1月前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
1月前
|
监控 算法 数据安全/隐私保护
基于三帧差算法的运动目标检测系统FPGA实现,包含testbench和MATLAB辅助验证程序
本项目展示了基于FPGA与MATLAB实现的三帧差算法运动目标检测。使用Vivado 2019.2和MATLAB 2022a开发环境,通过对比连续三帧图像的像素值变化,有效识别运动区域。项目包括完整无水印的运行效果预览、详细中文注释的代码及操作步骤视频,适合学习和研究。
|
27天前
|
缓存 分布式计算 监控
算法优化:提升程序性能的艺术
【10月更文挑战第20天】算法优化:提升程序性能的艺术
|
5月前
|
监控 算法 Java
Java虚拟机(JVM)使用多种垃圾回收算法来管理内存,以确保程序运行时不会因为内存不足而崩溃。
【6月更文挑战第20天】Java JVM运用多种GC算法,如标记-清除、复制、标记-压缩、分代收集、增量收集、并行收集和并发标记,以自动化内存管理,防止因内存耗尽导致的程序崩溃。这些算法各有优劣,适应不同的性能和资源需求。垃圾回收旨在避免手动内存管理,简化编程。当遇到内存泄漏,可以借助VisualVM、JConsole或MAT等工具监测内存、生成堆转储,分析引用链并定位泄漏源,从而解决问题。
54 4
下一篇
无影云桌面