精选算法入门——day1

news2024/12/25 12:34:02

题目一

题干

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
数组样式如下:

1 2 3 4
2 3 4 5
3 4 5 6

解题思路一(×)

这道题目拿到手上,自然而然想到的是,一个一个的遍历,比如我们想要寻找这个数组里有没有数字7,那么首先我们遍历第一行:1 2 3 4,发现没有,那么紧接着遍历第二行,以此类推,这样可不可以解决,当然可以,但是并不是最优解,回到题干中去,我们发现题目中的“每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序”这个条件并没有使用,那么很明显我们需要把这个条件运用起来,那么就引申出第二个解题思路。

解题思路二(√)

其实查找的过程也是筛选的过程。这句话什么意思?查找是寻找一个数字(或者是一类数字),而筛选就是将不是这个数字的数字给剔除掉,那么是筛选一个数字快还是一部分数字快呢?有同学肯定会说:那不是废话嘛!肯定是筛选一部分数字快,其实解题思路一中就是一个一个的遍历,接着一个一个的筛选,那么这个效率自然而然的就很低了,而当使用题干中的每行每列的顺序条件时,效率就很高了(可以筛选一部分的数字)。具体操作如下:
在这里插入图片描述
在这个数组中的右上角的数字:4,是这一行中的最大数,是这一列的最小数,如果当**target(目标数字)<4(右上角的数字)时,那么表明在这一列中没有我们要找的数字,则可以把这一列都去掉,如果当target(目标数字)>4(右上角的数字)**时,那么表明在这一行中没有我们要找的数字,则可以把这一行都去掉,这样一来我们筛选的效率就大大提高了,代码也很好写。效果如下:
在这里插入图片描述

代码

C++:

class Solution{
public:
	bool Find(int target, vector<vector<int> > array) { 
		int i = 0; 
		int j = array[0].size()-1; 
		while( i < array.size() && j >= 0){ 
			if(target < array[i][j]){ //array[i][j]一定是当前行最大的,当前列最小的
				//target < array[i][j] 排除当前列 
				j--;
			}
			else if(target > array[i][j]){ 
			//target > array[i][j] 排除当前行 
				i++; 
			}
			else{
				//找到 
				return true;
			}
		}
	}
}

Java:

public class Solution {
	public boolean Find(int target, int [][] array) {
		if(array == null){ 
			return false; 
		}
		int i = 0; 
		int j = array[0].length - 1;
		while( i < array.length && j >= 0){ 
			if(target < array[i][j]){//array[i][j]一定是当前行最大的,当前列最小的 
				//target < array[i][j] 排除当前列 
				j--; 
			}
			else if(target > array[i][j]){
				//target > array[i][j] 排除当前行
				i++;
			}
			else{
				//找到 
				return true;
			}
		}
		return false;
	}
}

题目二

题干

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋
转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的
所有元素都大于0,若数组大小为0,请返回0。

解题思路一(×)

这条题目乍一看其实就是求数组中的最小元素,那么最先想到的肯定是遍历整个数组,首先将数组的第一个元素记下来(result),然后依次往后遍历,发现有比result更小的数字,那么进行替换,直到最后一个数字。这个思路和想法是没有问题的,但是依然没有用到题干中的其他条件,那么这个思路并不是最优解。

解题思路二(×)

通过题干我们可以了解到旋转数组可以分为两个正常的非递减排序的数组,例如:
在这里插入图片描述
因此,跟思路一一样,我们也是遍历整个数组,只不过发生一次替换的情况,我们就停止循环,因为第一替换发生就是最小的数字,后面的数字都不会比第一次替换的那个数字小。

解题思路三(√)

这道题目其实可以采用二分法的思想来解决,通过上面的分析我们知道旋转数组可以分为两个正常的非递减排序的数组,也就是说最左边(left)的一个数字和最右边(right)的一个数字一定是分在两个非递减排序的数组中的,那么通过中间的数字(mid)与left和right对比,我们就可以判定最小的数字在mid的左边还是mid的右边,解释永远没有例子更直观,我们通过例子来分析:
在这里插入图片描述
在例1当中,mid(5)>left(3),那么我们自然就会知道,mid和left应该是属于同一个非递减排序的数组,又因为是旋转数组,left一定是属于较大的数组(指的是该数组中所有元素都不会小于另一个数组,反之为较小数组),那么较小数组就会在mid的右边,同理在例2中,mid(2)<left(5),由于是旋转数组,那么mid就是在较小数组中(且最小值只会在mid的左边或者就是mid)。如此一来我们就可以通过二分查找的方法逐步筛选出旋转数组的最小值。当然有同学会问,那么当mid=left的时候呢?这个时候我们需要分为两种情况:

第一种情况

在这里插入图片描述
如果left和mid处于较大数组中时,并且mid(3)=left(3),那么我们可知较小数组在mid的右边,因此我们可以把这种情况归到mid>left中去。

第二种情况

在这里插入图片描述
当left=mid并且mid处于较小数组中时,那么right一定也等于mid,也就是说left=mid=right,有的同学一看:啊,这个简单,随便选一个left,mid,right当最小值不就行了?可事实真的是这样吗?我们再看一种情况
在这里插入图片描述
我们发现虽然left=mid=right,但是最小值并不是left,mid或者是right,拿这种情况怎么办呢?那就只能用解题思路二中的方法,逐个遍历直到出现一次替换的时候。总结:

left,right,mid之间的关系如何解决
left=mid=right逐个比较
mid≥leftmid赋值给left
mid<leftmid赋值给right

代码

C++:

class Solution {
public:
	int minNumberInRotateArray(vector<int> rotateArray) {
		if(rotateArray.empty()){
			return 0;
		}
		int left = 0;
		int right = rotateArray.size() - 1; 
		int mid = 0;
		//要一直满足该条件,以证明旋转特性
		while(rotateArray[left] >= rotateArray[right]){
			if(right - left == 1){ 
			//两个下标已经相邻了 
			mid = right; 
			break; 
		}
		mid = left + ((right - left) >> 1); //注意操作符优先级
		if(rotateArray[mid] == rotateArray[left] && rotateArray[left] == rotateArray[right]){
		//无法判定目标数据在mid左侧,还是右侧我们采用线性遍历方式
		int result = rotateArray[left]; 
		for(int i = left+1; i < right; i++){ 
			if(result > rotateArray[i]){ 
				result = rotateArray[i]; 
			} 
		}
		return result; 
	}
	if(rotateArray[mid] >= rotateArray[left]){
	//试想两者相等, 隐含条件 rotateArray[left] >= rotateArray[right]
	//说明mid在前半部分
		left = mid;
		}else{
			//说明mid在后半部分 
			right = mid;
			}
		}
		return rotateArray[mid];
	}
}

Java:

public static int minNumberInRotateArray (int[] nums) {
    int left = 0;
    int right = nums.length - 1;
    int mid = 0;
    while(left <= right) {
        if(right - left == 1){
            return nums[right];
        }
        mid = (left + right)>>1;
        if(nums[left] == nums[mid] && nums[left] == nums[right]){
            int result = left + 1;
            while(left < right){
                if(nums[result]>nums[left]){
                    result = left;
                }
                left++;
            }
            return nums[result];
        }
        if(nums[mid] >= nums[left]){
            left = mid;
        }
        else {
            right = mid;
        }
    }
    return nums[mid];
}

题目一

题干

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位
于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

解题思路一(×)

在这个题目当中如果没有这句话“并保证奇数和奇数,偶数和偶数之间的相对位置不变。”这条题目的难度就大大降低了,我们可以用两个头尾指针(left和right)指向数组的左边与右边,left指针找偶数,right指针找奇数,当left指针找到偶数时,right指针找到奇数时,将左右指针的内容进行互换。如图:
在这里插入图片描述
我们发现虽然用这个方法可以完成奇数在前半部分,偶数在后半部分,但是他们的相对位置发生变化。因此这个方法不可行,那么在不改变相对位置的情况下如何解决呢?

解题思路二(√)

我们可以采用插入排序的思想,首先从第一个位置开始向后找,当找到奇数的时候,将前面所有的偶数都往后移动一个位置,再把找到的奇数放到这些偶数的前面,直到循环结束。我们还是直接上例子:
在这里插入图片描述
初始情况如图所示,当i在1时,我们发现是奇数,但是由于i在第一个位置,那么不需要变化,当i在2和6时,发现是偶数,那么直接往后移动。
在这里插入图片描述
当i在3时,由于3是奇数,从1开始把后面的所有偶数(2和6)都往后移动一格位置,如此一来数组的顺序就变成了1,3,2,6,4,5,紧接着i继续往后移动,当i在4的时候由于是偶数,不予考虑,接着往后移动。
在这里插入图片描述
当i在3时,由于5是奇数,从3开始把后面的所有偶数(2、6和4)都往后移动一格位置,如此一来数组的顺序就变成了1,3,5,2,6,4,此时循环结束,完成。

代码

C++:

class Solution { 
public: 
	void reOrderArray(vector<int> &array) { 
		int k = 0; 
		for(int i = 0; i < array.size(); ++i){ 
			if(array[i] & 1){ //从左向右,每次遇到的,都是最前面的奇数,一定将来要被放在k下标处 
				int temp = array[i]; //现将当前奇数保存起来 
				int j = i; 
				while(j > k){ 
					//将该奇数之前的内容(偶数序列),整体后移一个位置 
					array[j] = array[j-1]; 
					j--; 
				}
				array[k++] = temp; //将奇数保存在它将来改在的位置,因为我们是从左往右放的,没有跨越奇 数,所以一定是相对位置不变的 
			} 
		} 
	} 
};

Java:

public static void reOrderArray(int[] array) {
    if (array.length == 0) {
        return;
    }
    int k = 0;
    for (int i = 0; i < array.length; i++) {
        if ((array[i] & 1) == 1) {
            int tmp = array[i];
            int j = i;
            while (j > k) {
                array[j] = array[j - 1];
                j--;
            }
            array[k] = tmp;
            k++;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2183268.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【STM32单片机_(HAL库)】4-3-1【定时器TIM】串口打印功能打开

1.硬件 STM32单片机最小系统CH340模块 2.软件 main.c程序 #include "sys.h" #include "delay.h" #include "led.h" #include "uart1.h"int main(void) {HAL_Init(); /* 初始化HAL库 */stm32_clock_init(R…

常见的RTSP播放器有哪些?

VLC播放器 特点&#xff1a;VLC 是一款功能强大、跨平台的多媒体播放器&#xff0c;支持多种音频和视频格式以及流媒体协议&#xff0c;包括 RTSP。它具有广泛的解码器支持&#xff0c;能播放大多数常见的视频和音频格式。其开源特性使得它拥有活跃的开发者社区&#xff0c;不断…

【教学类-59-01】20241001双面五星红旗国旗(中2班)

背景需求&#xff1a; 国庆在即&#xff0c;怎么能不做一面五星红旗呢&#xff1f; 【教学类-39】A4红纸-国旗灯笼纸模&#xff08;庆祝中华人民共和国成立74周年&#xff09;_a4 打印 灯笼-CSDN博客文章浏览阅读626次。【教学类-39】A4红纸-国旗灯笼纸模&#xff08;庆祝中华…

车辆重识别(注意力 U-Net:学习在哪些区域寻找胰腺)论文阅读2024/10/01

什么是注意力机制&#xff1f; 什么是加性注意力&#xff1f; 大致说一下流程&#xff1a; 对于一张特征图来说&#xff0c;对于这张图中的每一个像素向量&#xff08;例如a&#xff09;&#xff0c;计算该向量与所有像素向量的相似度&#xff0c;对这些相似度进行激活函数…

nvm实现nodejs版本管理

nvm相关操作 1. nvm的作用2. 下载nvm之前的操作3. 下载nvm3.1 zip不同的名称区别 4. 验证是否安装成功5. 配置镜像,提高下载速度6. 安装nodeJS7. 查看目前已经安装的版本8.安装成功后npm无法使用9. 卸载指定node版本10. 切换node版本11.NVM常用命令 1. nvm的作用 nvm是一个方便…

24-10-1-读书笔记(二十一)-《契诃夫文集》(四)下([俄] 契诃夫 [译] 汝龙) 我爱你,娜坚卡。

文章目录 《契诃夫文集》&#xff08;四&#xff09;下&#xff08;[俄] 契诃夫 [译] 汝龙 &#xff09;目录阅读笔记记录总结 《契诃夫文集》&#xff08;四&#xff09;下&#xff08;[俄] 契诃夫 [译] 汝龙 &#xff09; 十月第一篇&#xff0c;放假了&#xff0c;挺高兴的&…

如何使用SCCMSecrets识别SCCM策略中潜在的安全问题

关于SCCMSecrets SCCMSecrets是一款针对SCCM策略的安全扫描与检测工具&#xff0c;该工具旨在提供一种有关 SCCM 策略的全面安全检测方法。 该工具可以从各种权限级别执行&#xff0c;并将尝试发现与策略分发相关的潜在错误配置。除了分发点上托管的包脚本外&#xff0c;它还将…

螺狮壳里做道场:老破机搭建的私人数据中心---Centos下Docker学习01(环境准备)

1 准备工作 由于创建数据中心需要安装很多服务器&#xff0c;这些服务器要耗费很所物理物理计算资源、存储资源、网络资源和软件资源&#xff0c;作为穷学生只有几百块的n手笔记本&#xff0c;不可能买十几台服务器来搭建数据中心&#xff0c;也不愿意跑实验室&#xff0c;想躺…

信息安全数学基础(23)一般二次同余式

前言 信息安全数学基础中的一般二次同余式是数论和密码学中的一个重要概念&#xff0c;它涉及到了二次方程、模运算以及同余关系等多个方面。 一、定义 设m是正整数&#xff0c;a,b,c是整数&#xff0c;且a0&#xff0c;则形如ax2bxc≡0(modm)的同余式称为模m的二次同余式。…

Git傻傻分不清楚(上)

环境&#xff1a;Idea2022.3.3、Git&#xff08;忘辽~&#xff09; 怎么上传自己的项目到Github上&#xff1f; Idea和Github进行账号关联将项目上传到本地仓库&#xff08;Commit&#xff09;将本地仓库中的项目上传到Github上&#xff08;Push&#xff09; 一、关联账号 …

移动应用中提升用户体验的因素

用户体验&#xff08;UX&#xff09;是任何移动应用程序成功的关键因素。随着数以百万计的应用程序争夺注意力&#xff0c;提供无缝、愉快和高效的体验可能是获得忠实用户或在一次互动后失去忠实用户之间的区别。无论是商业应用程序、游戏还是社交平台&#xff0c;增强用户体验…

TypeScript 封装 Axios 1.7.7

随着Axios版本的不同&#xff0c;类型也在改变&#xff0c;以后怎么写类型&#xff1f; 1. 封装Axios 将Axios封装成一个类&#xff0c;同时重新封装request方法 重新封装request有几个好处&#xff1a; 所有的请求将从我们定义的requet请求中发送&#xff0c;这样以后更换…

WebRTC入门

主要参考资料&#xff1a; WebRTC 在 ESP32 系列硬件平台上的实现: https://www.bilibili.com/video/BV1AEHseWEda/?spm_id_from333.337.search-card.all.click&vd_sourcedd284033cd0c4d1f3f59a2cd40ae4ef9 火山 RTC豆包大模型&#xff0c;给用户体验装上银色子弹: https:…

第九篇——数列和级数(二):传销骗局的数学原理

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 文章不长&#xff0c;但是道理深刻&#xff1b;相邻两个数的差值&#xf…

Prompt 模版解析:诗人角色的创意引导与实践

Prompt 模版解析&#xff1a;诗人角色的创意引导与实践 Prompt 模版作为一种结构化工具&#xff0c;旨在为特定角色——本例中的“诗人”——提供明确的指导和框架。这一模版详尽地描绘了诗人的职责、擅长的诗歌形式以及创作规则&#xff0c;使其能在自动化系统中更加精确地执…

咸鱼sign逆向分析与爬虫实现

目标&#xff1a;&#x1f41f;的搜索商品接口 这个站异步有点多&#xff0c;好在代码没什么混淆。加密的sign值我们可以通过搜索找到位置 sign值通过k赋值&#xff0c;k则是字符串拼接后传入i函数加密 除了开头的aff…&#xff0c;后面的都是明文没什么好说的&#xff0c;我…

学习记录:js算法(五十):二叉树的右视图

文章目录 二叉树的右视图我的思路网上思路 总结 二叉树的右视图 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 图一&#xff1a; 示例 1:如图一 输入: [1,2,3,null,5,null,4] …

uni-app #ifdef #ifndef #endif的使用和区别

1. uni-app #ifdef #ifndef #endif的使用和区别 条件编译是一种在编程中根据特定平台选择性编译代码的技术。如#ifdef和#endif根据APP-PLUS平台标识在微信小程序、支付宝小程序、百度小程序、头条小程序、QQ小程序以及H5应用中实现代码的平台特定执行。下面介绍了如何使用#ifde…

基于Java+SpringBoot3+vue3+uniapp点餐/外卖管理系统设计与实现(视频讲解)

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

微服务sentinel解析部署使用全流程

sentinel源码地址&#xff1a; 介绍 alibaba/Sentinel Wiki GitHub sentinel官方文档&#xff1a; https://sentinelguard.io/zh-cn/docs/introduction.html Sprong Cloud alibaba Sentinel文档【小例子】 : Sentinel alibaba/spring-cloud-alibaba Wiki GitHub 目录 1、…