常见解题方法(位运算、双指针、前缀和)

news/2024/5/19 21:14:06 标签: java, 排序算法, 快速排序

目录

      • 位运算
      • 双指针
      • 前缀和

对于自己刷题过程中遇到的一些常见简单解题方法进行了一个总结:

数组在数据结构中是线性表的一种,在算法题中常常以整数数组和字符串等形式展现,其实数组中包含有更多的数据类型,这一段主要说明整数数组的一些常见问题解法;

数组的一个特点,可以通过下标对于数据进行一个快速访问,即a[i] = xx;

位运算

本质上应该算在数学系算法的一大类,通过数组中的各个数进行一个位运算来获得最终的结果。
位运算指数转化为二进制后的一系列运算,比如与、或、非、同或、异或、左移右移等,通过这些运算达到一个高效率的算法;在这里插入图片描述
常见的是利用它的一些特性来解决问题:比如左右移位的按位处理;异或结果的两个运算数相同为0,不同为1;
以一些例题说明:

1、leetcode29两数相除

这一题要求实现两个数相除;不允许使用除号、称号、取余;

解法:分析问题直接从结果来看需要得到除法,假设计算A/B,除法本质是数A中包含少个B;
那么一种直观的解法是采用减法循环实现,就用 A=A-B这种形式,直到无法再减;比如10/3;它可以减3次,到第三次A等于1无法再减了;总共循环了三次,所以结果为3;

接受两个参数A\B,除数与被除数
define count = 0;计数器;
while(A大于B){
        A-B,;计数器加1;
}
返回计数器;
(这里没有考虑越界情况,仅仅解释算法)

这种算法显然有待优化,优化的方式是 每次尝试去减B的倍数,尝试一次多记几次计数器;比如13/3;尝试13/(3+3+3+3)= 1;实际结果应该是这四个三相加; 不能使用乘法;所以这里可以使用左移,即3<<<4;可以直接得到结果而不用再加;因此可以用高位来慢慢向下移动直到找到结果;

		接受参数A和B;
		define 计数器
		for(i=31 to i=0){
		如果B左移 i 次的结果大于A;那么计数器等于这个值;
		}
		return 计数器

2、leetcode137只出现一次的数字
这一题是指一个数组中只有一个数出现一次,其他出现3次;求这个数,比如[2,2,1,2,3,3,3]的结果为1;

解法:以数字的二进制形式来说,对于出现三次的数字,二进制位出现的次数都是 3 的倍数。
因此,统计所有数字的各二进制位中1的出现次数,并对 3 求余,结果则为只出现一次的数字。
举个例子 比如 [1,1,1,3];每一个数的每一个二进制位都加起来然后取余3,得到的结果应该是011即3;

		      //循环32次,int有32位
		        for(int i = 0;i<32;i++){
		            int sum = 0;
		            for(遍历数组中的数n){
		                //对数组中的每一个数,每次右移 i 位在与1;即取出第i位上的值;
		                sum += (n>>i)&1;
		            }
		            //对每一二进制位上的数求和取余得到最终结果的第i位上的值;在归位;
		            sum%=3;
		            ans+=sum<<i;
		        }
		        return ans;
		    }

3、leetcode318求最大单词长度
给定一个字符串数组 words,找到 length(word[i]) * length(word[j]) 的最大值,并且这两个单词不含有公共字母。你可以认为每个单词只包含小写字母。如果不存在这样的两个单词,返回 0。
即 数组中两个完全不包含重复字母的单词最长结果;

解法:不同单词包含不同的字母,可以直接想到为每个单词建立一个字典记录他们的各个字母;因此可以使用二进制来标识他们各个字母的出现情况,“ac”标识为101;而“ad”标识为1001;“abc”表示为111;这样就可以把每个单词表示开来;最后在遍历数组时比较二进制位即可;

		标识过程:
		定义记录数组;
		for(遍历整个数组){
		    for(遍历该位置上的字符串数组){
		             以ascll表为例,用记录数组记录1右移每个字符编码减去a位置上的编码的位数;
		    }
		}
		检测过程
		for(遍历记录数组){
		      for(遍历当前下标后边的记录数组){
		             比较单词上的二进制制位是否相同;如果相同则记录最大值;
		      }
		}

双指针

双指针从广义上来说,是指用两个变量在线性结构上遍历而解决的问题。在数组中,可以是同向或是双向的;在链表里,两个指针为同向移动,但速率可以不同,一般叫做快慢指针;
双指针的一些常见应用场景:滑动窗口、排序、二分查找等;
分类:同向指针:快慢、间隔指针; 异向指针:对撞;
使用时需要注意的问题:指针指向,移动方向,起始位置,何时移动、移动速率;
1、对撞指针的应用;
对撞指针一般指两个不同方向移动的指针,比如一个从头向尾,一个从尾向头移动;典型的一个对撞指针应用是二分查找的算法;二分查找一般的使用方式是这样

定义左指针 = 0;
右指针等于长度 - 1;
while(左指针小于右指针){
             程序操作;
             if(条件){
             	左++;
             }
             if(条件){
				右--;
			}
             
}

例题
lc15三数之和
给一个数组,求数组中的三元组[a,b,c]使得a+b+c=0;
解法:这题本质还是一个查找的算法;如果我们知道"a",则在数组里找" -(b+c) ";
使用排序加+对撞指针来做

		  数组排序
		 查找结果
		
		   定义结果集;
		    for (从第一个数到倒数第3个数) {
		        if(i!=0&&nums[i]==nums[i-1]) continue;
		       定义左右指针,左指针等于外循环的i,右指针为数组末尾;
		       while(left<right){
		             跳过重复的值;防止记录重复结果;
		            
		            记录左右指针处的数的和sum;
		            
		           
		           if(如果sum刚好等于当前外循环数a的相反数){
		                          记录左右指针结果;并移动左右指针到值不重复的地方;
		           
		           }else if( 这个左右指针数的和比外循环的值的相反数小){
		                          左指针移动eft++;
		           
		           }else{ //这个和比外循环的值的相反数大;
		                           右指针移动right--;
		           }
		       }
		    }
		最后返回结果集即可;

2、同向快慢指针;左右指针都从一侧向另一侧移动;经典案例是链表的是否有环;
通过速度不一致的两个指针,一前一后一起移动,直到指针重合在一起;
代码:

java">	public boolean hasCycle(ListNode head) {
	    ListNode slow = head;
	          ListNode fast = head;
	    while (slow != null && fast != null) {
	                 ListNode n = fast.next;
	     fast = n == null ? null : n.next;
	     if (slow == fast) {
	         return true;
	     }
	                 slow = slow.next;
	    }
	    return false;
	    }

3、同向间隔指针;类似于滑动窗口,一个指针在前,一个指针后面,同步或者不同步移动来解决某些问题;即定义一个左指针在左边移动,右指针在后面移动;

lc 3 无重复字符的最长字串

判断字符串中没有字符的最长字串;

解法:通过滑动窗口移动解决,左指针不移动,右指针向前移动;当右指针移动到重复的字符时,将左指针拉到右指针处,记录最大值;

	public int lengthOfLongestSubstring(String s) {
	        if (s.length()==0) return 0;
	        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
	        int max = 0;
	        int left = 0;
	        for(int i = 0; i < s.length(); i ++){
	            if(map.containsKey(s.charAt(i))){
	                left = Math.max(left,map.get(s.charAt(i)) + 1);
	            }
	            map.put(s.charAt(i),i);
	            max = Math.max(max,i-left+1);
	        }
	        return max;
	        
	    }

前缀和

通过预先处理数组来获得结果,前缀和是指记录数组的前n项和;比如数组[1,2,3,4],即在新数组中存储结果[1,3,6,10];通过这样的数组来解决某些问题;
通过前缀和可以很容易的求到子数组的n项和,而通过子数组的和可以解决很多求和类问题,比如求数组中间数使前面和等于后边的和,或者和大于某数的子数组等;利用这个思想也可以解决乘积相关的问题;
lc560和为k的子数组
给你一个整数数组 nums 和一个整数 k ,请你统计并返回该数组中和为 k 的连续子数组的个数。

java">
	public int subarraySum(int[] nums, int k) {
	        int count = 0;
	        //前缀和, 使用hash表来记录前缀和,如果在后续的遍历中存在有sum-k,   即当前前缀和-k = 以前前缀和,则代表从该位置到现在这个位置的和等于k
	        HashMap<Integer,Integer> map = new HashMap<>();
	        map.put(0,1);
	        int sum = 0;
	        for(int i = 0;i<nums.length;i++){
	            sum+=nums[i];
	            count+=map.getOrDefault(sum-k,0);
	            map.put(sum,map.getOrDefault(sum,0)+1);
	        }
	        return count;
	    }
	    

lc连续01子数组

差分数组
与前缀和类似,差分数组是指对原数组进行差分运算得到的新数组,即[1,2,3,4]的到的新数组为[1,-1,-2,-3];利用它也可以解决很多类似的问题;


http://www.niftyadmin.cn/n/1020481.html

相关文章

linux python 修改环境变量 添加自定义模块路径

举一个很简单的例子&#xff0c;如果你发现一个包或者模块&#xff0c;明明是有的&#xff0c;但是会发生这样的错误&#xff1a; >>> from algorithm import *Traceback (most recent call last): File "<stdin>", line 1, in <module>Import…

python调用驱动接口_基于Python Requests的数据驱动的HTTP接口测试

3) requests的返回值这里让我们来讨论下requests的返回值。见表1。表1&#xff1a;requests的返回值请求网址的内容信息在这里介绍一下请求页面的状态(状态码)&#xff0c;这个在基于HTTP协议的接口测试中经常作为一个验证点。1XX&#xff1a;表示消息这个比较少用2XX&#xf…

MySQL视图、存储过程与触发器;

目录视图使用注意事项存储过程使用触发器使用视图 视图是MySQL中的一个虚拟表的概念&#xff0c;类似于一个SQL语句的查询结果&#xff0c;它提供了从某一视角来看表的类型&#xff1b;将 一个表中不该显示的数据或列剔除&#xff1b;而得到自己想要的数据&#xff1b; 视图一…

sumblime快捷键

原文地址&#xff1a;https://blog.csdn.net/shutfuckingup/article/details/23846603 CtrlD 选词 &#xff08;反复按快捷键&#xff0c;即可继续向下同时选中下一个相同的文本进行同时编辑&#xff09;CtrlG 跳转到相应的行CtrlJ 合并行&#xff08;已选择需要合并的多行时&a…

多麦克风做拾音的波束_语音交互:先从麦克风阵列聊起

随着智能音箱、智能家居等智能硬件的普及&#xff0c;语音交互热度也不断飙升。想要了解语音交互&#xff0c;第一步是了解麦克风阵列&#xff0c;本文从概念、分类、作用几个方面对麦克风阵列展开了说明&#xff0c;与大家分享。语音交互从亚马逊音箱(Echo)诞生的那一刻&#…

spring mvc:输出json,输出多个json

spring mvc:输出xml/输出json 用到的注解ResponseBody ResponseBody用来输出json/xml等格式数据&#xff08;非html&#xff09; controller输出用到的类 org.springframework.web.bind.annotation.ResponseBody 需要bean解析支持 <!-- bean视图解析 --> <bean cla…

MySQL中innodb体系结构和特性研究

目录简介1、innodb体系结构是怎样的&#xff1f;2、innodb的线程3、如何理解innodb的内存4、innodb如何保证缓冲池和磁盘上的数据一致5、innodb的一些关键特性插入缓冲两次写自适应hash索引异步IO刷新邻接页简介 MySQL的体系结构可以分为连接层和服务层&#xff1b;连接层主要…

python包管理工具 ports_Python无法识别MacPorts安装的包

提前感谢您的建议&#xff01;在背景&#xff1a;相对于这个网站上的人&#xff0c;我对编程还是个新手&#xff0c;尽管做了一些研究&#xff0c;但我不熟悉使用Unix类型的shell&#xff0c;不熟悉管理包真正涉及的内容&#xff0c;或者不熟悉在“Dr.Java”IDE或defaultr GUI之…