算法通关村第三关—数组基本操作(青铜)

2023-12-03 19:12

本文主要是介绍算法通关村第三关—数组基本操作(青铜),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

            数组基本操作

一、数组的创建和初始化

创建

int[] arr = new int[10];

初始化

int[] arr = new int[]{0,1,2,3}
int[] arr = {0,1,2,3}

二、增加一个元素

将给定的元素插入到有序数组的对应位置中,我们可以先找位置,再将其后元素整体右移,最后插入到空位置上。这里需要注意,算法必须能保证在数组的首部、尾部和中间位置插入都可以成功。该问题貌似个for循环就搞定了,但是如果面试直接让你写并能正确运行,我相信很多人还是会折腾很久,甚至直接会挂。因为自己写的时候会发现游标写size还是size-1,判断时要不要加等于等等,这里推荐一种实现方式。

/***dparam arr*dparam size*数组已经存储的元素数量,从1开始编号*@param element待插入的元素*return*/
public static int addByElementSequence(int[]arr,int size,int element){//问题①:是否应该是size>arr.lengthif (size > arr.length) retrun -1;//问题②想想这里是否是index=0或者size-1?int index = size;//找到新元素的插入位置,问题③这里是否应该是size-1?for (int i = 0;i < size;i++){if (element < arr[i]){index = i;break;}//元素后移,问题④想想这里为什么不是s1ze-1for (int j = size; j > index; j--){arr[j] = arr[j-i];//index下标开始的元素后移一个位置}arr[index] = element;//插入数据return index;}

三、删除一个元素

对于删除,要分为两个步骤,先从最左侧开始查是否存在元素,如果元素存在,则从该位置开始执行删除操作。
例如序列是1 2 3 4 5 6 7 8 9,要删除5,则应先遍历,找到5,然后从5开始执行删除操作,也就是从6开始逐步覆盖上一个元素,最终将序列变成1 2 3 4 6 7 8 9 [9]。
这个方法和增加元素一样,必须自己亲自写才有作用,该方法同样要求删除序列最前、中间、最后和不存在的元素都能有效,下面给一个参考实现:

/***从数组中删除元素key*@param arr数组*@param size数组中的元素个数,从1开始*@param key删除的目标值*/
public int removeByElement(int[]arr,int size,int key){int index = -1;for(int i = 0; i < size; i++){if (arr[i] = key){index = i;break;}}if(index != -1){for (int i = index + 1; i < size; i++)arr[i-1] = arr[i];size--;}return size;
}

四、算法热身-单调序列

先看个热身问题,我们在写算法的时候,数组是否有序是一个非常重要的前提,有或者没有可能会采用完全不同的策略。LeetCode896.判断一个给定的数组是否为单调数组。
分析:如果对于所有i<=j,A[i]<=A[j],那么数组A是单调递增的。如果对于所有i<=j,A[i>=A[j],那么数组A是单调递减的。所以遍历数组执行这个判定条件就行了,由于有递增和递减两种情况。
于是我们执行两次循环就可以了,代码如下:

//这里用i和j的长度判断是否整个数组都是单调的
class Solution {public boolean isMonotonic(int[] nums) {if(nums.length <= 2) return true;int i = 1;while(i < nums.length){if(nums[i] > nums[i - 1]) break;i++;}int j = 1;while(j < nums.length){if(nums[j] < nums[j - 1]) break;j++;}return i == nums.length || j == nums.length;}
}

老师的改进方法,变成一次循环

public boolean isMonotonic(int[] nums){boolean inc true, dec true;int n = nums.length;for(int i = 0; i < n-1; ++i){if (nums[i] > nums[i + 1]){inc = false;}if (nums[i] < nums[i + 1]){dec = false;}}return inc || dec;
}

我们判断整体单调性不是白干的,很多时候需要将特定元素插入到有序序列中,并保证插入后的序列仍然有序,例如leetcodes35:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
这个问题没有让你将新元素插入到原始序列中,还是比较简单的,只要遍历一下就找到了。如果面试官再问你,该如何更快的找到目标元素呢?那他其实是想考你二分查找。以后凡是提到在单调序列中查找的情况,我们应该马上想到是否能用二分来提高查找效率。这里只看一下实现代码:

public int searchInsert(int[]nums,int target){int n = nums.Length;int left = 0, right = n-1,ans = n;while (left <= right){int mid = ((right -left)>>1) + left;if (target <= nums.[mid]){ans = mid;right = mid - 1;}else left = mid + 1;}
}
return ans;
}

五、算法热身-数组合并专题

数组合并就是将两个或者多个有序数组合并成一个新的。这个问题的本身不算难,但是要写的够出彩才可以。还有后面要学的归并排序本身就是多个小数组的合并,所以研究该问题也是为了后面打下基础。
先来看如何合并两个有序数组,LeetCode88:给你两个按非递减顺序排列的整数数组nums1和nums2,另有两个整数m和n,分别表示nums1和nums2中的元素数目。请你合并nums2到nums1中,使合并后的数组同样按非递减顺序排列。
注意:最终合并后数组不应由函数返回,而是存储在数组nums1中。为了应对这种情况,nums1的初始长度为m+n,其中前m个元素表示应合并的元素,后n个元素为0应忽略。nums2的长度为n。

思路一:把数组2插入到数组1后面,然后对数组1排序

class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {for(int i = m; i < m + n; i++){nums1[i] = nums2[i - m];}Arrays.sort(nums1);}
}

但是这么写只是为了开拓思路,面试官会不喜欢,太没技术含量了。这个问题的关键是将B合并到A的仍然要保证有序。因为A是数组不能强行插入,如果从前向后插入,数组A后面的元素会多次移动,代价比较高。此时可以借助一个新数组C来做,先将选择好的放入到C中,最后再返回。这样虽然解决问题了,但是
面试官可能会问你能否再优化一下,或者不申请新数组就能做呢?更专业的问法是:上面算法的空间复杂度为0(n),能否有0(1)的方法?
思路二
比较好的方式是从后向前插入,A和B的元素数量是固定的,所以排序后最远位置一定是A和B元素都最大的那个,依次类推,每次都找最大的那个从后向前填就可以了,代码如下:

class Solution {public void merge(int[] nums1, int m, int[] nums2, int n) {int len = m + n - 1,len1 = m - 1, len2 = n -1;while(len1 >= 0 && len2 >= 0){if(nums1[len1] > nums2[len2]){nums1[len] = nums1[len1];len1--;}else{nums1[len] = nums2[len2];len2--;}len--;}//假如两个数组还有剩余while(len1  >= 0){nums1[len] = nums1[len1];len1--;len--;}while(len2 >= 0){nums1[len] = nums2[len2];len2--;len--;}}
}

这篇关于算法通关村第三关—数组基本操作(青铜)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/450553

相关文章

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

Python进阶之Excel基本操作介绍

《Python进阶之Excel基本操作介绍》在现实中,很多工作都需要与数据打交道,Excel作为常用的数据处理工具,一直备受人们的青睐,本文主要为大家介绍了一些Python中Excel的基本操作,希望... 目录概述写入使用 xlwt使用 XlsxWriter读取修改概述在现实中,很多工作都需要与数据打交

Java 字符数组转字符串的常用方法

《Java字符数组转字符串的常用方法》文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及A... 目录1. 使用String构造函数1.1 基本转换方法1.2 注意事项2. 使用String.valu

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

vue如何监听对象或者数组某个属性的变化详解

《vue如何监听对象或者数组某个属性的变化详解》这篇文章主要给大家介绍了关于vue如何监听对象或者数组某个属性的变化,在Vue.js中可以通过watch监听属性变化并动态修改其他属性的值,watch通... 目录前言用watch监听深度监听使用计算属性watch和计算属性的区别在vue 3中使用watchE

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个