Array算法题详细解析

从旋转的有序数组中查找元素


问题描述:

给定一个排序和旋转的数组,如下所示:

int arr[]={16,19,21,25,3,5,8,10};

复制代码

该数组是从一个有序数组arr[]={3,5,8,10,16,19,21,25}旋转后所得。

要求在在O(log n)时间复杂度的数组中搜索到一个指定元素。

思路:

  • 如果直接遍历数组,则时间复杂度为O(n),不符合题目要求;

  • 因为数组是由有序数组旋转所得,所以在某个下标的之前和之后是有序的;

  • 然后按照二分法查找。

算法逻辑:

  • 计算出中位下标mid=(low+high)/2;

  • 如果arr[mid]等于要查找的数字则返回;

  • 如果[low…mid]是有序的;

  • 如果要查找的数字在[low…mid],则high=mid-1;

  • 如果要查找的数字不在[low…mid],则low=mid+1;

  • 如果[mid…high]是有序的;

  • 如果要查找的数字在[mid…high],则low=mid+1;

  • 如果要查找的数字不在[mid…high],则high=mid-1;

代码实现:

public class SearchElementSortedAndRotatedArrayMain {

public static void main(String[] args) {

int arr[] = {16, 19, 21, 25, 3, 5, 8, 10};

System.out.println("Index of element 5 : " + findElementRotatedSortedArray(arr, 0, arr.length - 1, 5));

}

public static int findElementRotatedSortedArray(int[] arr, int low, int high, int number) {

int mid;

while (low <= high) {

mid = low + ((high - low) / 2);

if (arr[mid] == number) {

return mid;

}

if (arr[mid] <= arr[high]) {

// 右边部分是有序的

if (number > arr[mid] && number <= arr[high]) {

low = mid + 1;

} else {

high = mid - 1;

}

} else {

// 左边部分是有序的

if (arr[low] <= number && number < arr[mid]) {

high = mid - 1;

} else {

low = mid + 1;

}

}

}

return -1;

}

}

复制代码

找到有序旋转数组中的最小元素


问题描述:

有序旋转数组定义同上一题,例如:

int arr[]={16,19,21,25,3,5,8,10};

复制代码

同样要求时间复杂度为O(log n)

思路:

与上题类似,因数组可以拆分为前后两个有序数组,可以通过二分查找法的变体来完成;

  • 计算出中位下标mid=(low+high)/2;

  • 如果[mid…high]是有序的;

  • 最小值在右边,high=mid;

  • 否则最小值在左边,low= mid+1;

代码实现:

public class MinimumElementSortedAndRotatedArrayMain {

public static void main(String[] args) {

int arr[] = {16, 19, 21, 25, 3, 5, 8, 10};

System.out.println("Minimum element in the array : " + findMinimumElementRotatedSortedArray(arr, 0, arr.length - 1));

}

public static int findMinimumElementRotatedSortedArray(int[] arr, int low, int high) {

int mid;

while (low < high) {

mid = low + ((high - low) / 2);

if (arr[mid] < arr[high]) {

high = mid;

} else {

low = mid+1;

}

}

return arr[low];

}

}

复制代码

查找数组中第二大的与元素


问题描述:

给定一个旋转有序数组,如下所示:

int[] arr1={7,5,6,1,4,2};

复制代码

找出第二大的元素:6

思路:

可以对数组排序,然后返回数组中的倒数第二个元素,时间复杂度为O(nlogn)

  • 定义最大值highest 和第二大值变量secondHighest

  • 对数组进行遍历

  • 如果当前元素大于最大值

  • 将highest赋值给secondHighest

  • 将当前元素赋值给highest

  • 否则,如果当前元素大于secondHighest

  • 将当前元素赋值给secondHighest

代码实现:

public class FindSecondLargestMain {

public static void main(String args[]) {

int[] arr1 = {7, 5, 6, 1, 4, 2};

int secondHighest = findSecondLargestNumberInTheArray(arr1);

System.out.println("Second largest element in the array : " + secondHighest);

}

public static int findSecondLargestNumberInTheArray(int array[]) {

int highest = Integer.MIN_VALUE;

int secondHighest = Integer.MIN_VALUE;

// 遍历数组

for (int i = 0; i < array.length; i++) {

// 如果当前值大于最大值

if (array[i] > highest) {

// 最大值赋值给第二大值

secondHighest = highest;

// 当前值赋值给最大值

highest = array[i];

} else if (array[i] > secondHighest && array[i] != highest)

// 将当前值赋值给第二大值

secondHighest = array[i];

}

return secondHighest;

}

}

复制代码

找出数组中出现奇数次的数


题目描述:

给定一个整数数组, 只有一个数字出现奇数次,其他数字都出现偶数次。 你需要找到奇数次出现的数字。 需要用O(n)时间复杂度和O(1)空间复杂度来解决它。

思路1:

暴力解法:使用两层循环,记录每个元素出现的次数,这种方法的时间复杂度为O(n^2),不是最优解。

思路2:

使用Hash,将数字作为key,出现的次数作为value,每当key重复时,value+1,这种解法的时间复杂度为O(n),但是空间复杂度也为O(n),不符合要求。

代码实现:

int getOddTimesElementHashing(int ar[]) {

int i;

HashMap<Integer, Integer> elements = new HashMap<Integer, Integer>();

for (i = 0; i < ar.length; i++) {

int element = ar[i];

if (elements.get(element) == null) {

elements.put(element, 1);

} else{

elements.put(element, elements.get(element) + 1);

}

}

for (Entry<Integer, Integer> entry : elements.entrySet()) {

if (entry.getValue() % 2 == 1) {

return entry.getKey();

}

}

return -1;

}

复制代码

思路3:

基于位运算异或操作。

异或运算:相同为0,不同为1。如a=1001,b=1010,则ab=0010,aa=0000。

按照题目描述,数组中只有1个数字出现奇数次,其他数字都是偶数次,则将所有数字异或运算后的结果就是出现奇数次的数字。

代码实现:

int getOddTimesElement(int arr[]) {

int i;

int result = 0;

for (i = 0; i < arr.length; i++) {

result = result ^ arr[i];

}

return result;

}

复制代码

该解法的时间复杂度为O(n);因为只使用了一个变量result,所以空间复杂度为O(1)

计算火车站需要的最少站台数


题目描述:

给定两个数组,分别对应火车站车辆到达的时间和出站的时间,计算出火车站最少需要几个站台。

// 到达时刻表

arrival[] = {1:00, 1:40, 1:50, 2:00, 2:15, 4:00}

// 出站时刻表

departure[] = {1:10, 3:00, 2:20, 2:30, 3:15, 6:00}

// 最少需要的站台数 = 4

复制代码

思路1:

遍历两个数组,检查每个到达和出站的时间间隔有多少重叠。

这种方式的时间复杂度为O(n^2),不是最优解。

思路2:

使用归并排序的逻辑。

  • 对到达和出发的数组进行排序;

  • 因为到达和出站数量一样,则从两个数组中取出相同位置的元素比较时间大小;

  • 如果到达时间早,则站台数加1;

  • 如果出站时间早,则站台数减1;

  • 在这个过程中记录最大的站台数;

  • 最后返回最大站台数的值。

这种算法的时间复杂度为O(n*log n)。

代码实现:

public class TrainPlatformMain {

public static void main(String args[]) {

// arr[] = {1:00, 1:40, 1:50, 2:00, 2:15, 4:00}

// dep[] = {1:10, 3:00, 2:20, 2:30, 3:15, 6:00}

int arr[] = {100, 140, 150, 200, 215, 400};

int dep[] = {110, 300, 210, 230, 315, 600};

System.out.println(“最少需要的站台数 =” + findPlatformsRequiredForStation(arr, dep, arr.length));

}

static int findPlatformsRequiredForStation(int arr[], int dep[], int n) {

int platform_needed = 0, maxPlatforms = 0;

Arrays.sort(arr);

Arrays.sort(dep);

int i = 0, j = 0;

// 类似归并排序中的归并操作

while (i < n && j < n) {

if (arr[i] < dep[j]) {

platform_needed++;

i++;

if (platform_needed > maxPlatforms)

maxPlatforms = platform_needed;

} else {

platform_needed–;

j++;

}

}

return maxPlatforms;

}

}

复制代码

数组中最两数之和最接近0的两个数


题目描述:

一个数组中有一系列的正数和负数,找出数组中两数之和最接近0的两个数。

例如:

array[]={1,3,-5,7,8,20,-40,6};

// 和最接近0的两个数是 : -5 and 6

复制代码

思路1:

暴力解法:将所有数字两两求和,如果和的绝对值更小,则赋值记录两数的值。

这种方法的时间复杂度为O(n^2),不是最优解。

代码实现:

public static void findPairWithMinSumBruteForce(int arr[]) {

if (arr.length < 2)

return;

// 预设前两个数的和最接近于0

int minimumSum = arr[0] + arr[1];

int pair1stIndex = 0;

int pair2ndIndex = 1;

for (int i = 0; i < arr.length; i++) {

for (int j = i + 1; j < arr.length; j++) {

int tempSum = arr[i] + arr[j];

if (Math.abs(tempSum) < Math.abs(minimumSum)) {

pair1stIndex = i;

pair2ndIndex = j;

minimumSum = tempSum;

}

}

}

System.out.println(" 和最接近0的两个数是 : " + arr[pair1stIndex] + " " + arr[pair2ndIndex]);

}

复制代码

思路2:

  • 对数组进行排序;

  • 定义两个索引对应数组的开始位置l=0,一个在结束位置r=n-1;

  • 按照l<r的条件循环

  • 计算arr[l]+arr[r]的和sum

  • 如果abs(sum)<abs(minSum),将l和r记录在最小的两个数组元素变量上;

  • 如果sum>0,则sum要更接近0需要将大值往小移动,r–;

  • 如果sum<0,则sum要更接近0需要将小值往大移动,l++。

代码实现:

public static void findPairWithMinSum(int arr[]) {

Arrays.sort(arr);

int sum = 0;

int minimumSum = Integer.MAX_VALUE;

int n = arr.length;

// left and right index variables

int l = 0, r = n - 1;

int minLeft = l, minRight = n - 1;

while (l < r) {

sum = arr[l] + arr[r];

// 如果abs(sum)<abs(minSum),将l和r记录在最小的两个数组元素变量上;

if (Math.abs(sum) < Math.abs(minimumSum)) {

minimumSum = sum;

minLeft = l;

minRight = r;

}

if (sum < 0)

l++;

else

r–;

}

System.out.println(" 和最接近0的两个数是 : " + arr[minLeft] + " " + arr[minRight]);

}

复制代码

该算法的时间复杂度为O(NLogN)

数组中最两数之和最接近X的两个数


该问题为上一题的进阶版,找出两数之和最接近X的数。

思路1:

暴力解法:两层循环,求和与最接近的数进行比较,如果更小则记录对应数字,时间复杂度为O(n^2),不是最优解。

思路2:

与上一题的区别在于该问题要对求和结果比较的值是指定的X,不是0,不能直接使用abs()函数的结果进行比较;那该如何处理呢?

可以通过将abs()结果减去X,则对应该问题的解法。

  • 对数组进行排序;

  • 定义两个索引对应数组的开始位置l=0,一个在结束位置r=n-1;

  • 按照l<r的条件循环

  • 计算arr[l]+arr[r]-x的结果diff

  • 如果abs(diff)<minDiff,将l和r记录在最小的两个数组元素变量上;

  • 如果sum>x,则sum要更接近x需要将大值往小移动,r–;

  • 如果sum<x,则sum要更接近x需要将小值往大移动,l++。

代码实现:

public static void findPairWithClosestToX(int arr[], int X) {

Arrays.sort(arr);

int minimumDiff = Integer.MAX_VALUE;

int n = arr.length;

int l = 0, r = n - 1;

int minLeft = l, minRight = n - 1;

while (l < r) {

int currentDiff = Math.abs(arr[l] + arr[r] - X);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Java)

最后

分享一套我整理的面试干货,这份文档结合了我多年的面试官经验,站在面试官的角度来告诉你,面试官提的那些问题他最想听到你给他的回答是什么,分享出来帮助那些对前途感到迷茫的朋友。

面试经验技巧篇
  • 经验技巧1 如何巧妙地回答面试官的问题
  • 经验技巧2 如何回答技术性的问题
  • 经验技巧3 如何回答非技术性问题
  • 经验技巧4 如何回答快速估算类问题
  • 经验技巧5 如何回答算法设计问题
  • 经验技巧6 如何回答系统设计题
  • 经验技巧7 如何解决求职中的时间冲突问题
  • 经验技巧8 如果面试问题曾经遇见过,是否要告知面试官
  • 经验技巧9 在被企业拒绝后是否可以再申请
  • 经验技巧10 如何应对自己不会回答的问题
  • 经验技巧11 如何应对面试官的“激将法”语言
  • 经验技巧12 如何处理与面试官持不同观点这个问题
  • 经验技巧13 什么是职场暗语

面试真题篇
  • 真题详解1 某知名互联网下载服务提供商软件工程师笔试题
  • 真题详解2 某知名社交平台软件工程师笔试题
  • 真题详解3 某知名安全软件服务提供商软件工程师笔试题
  • 真题详解4 某知名互联网金融企业软件工程师笔试题
  • 真题详解5 某知名搜索引擎提供商软件工程师笔试题
  • 真题详解6 某初创公司软件工程师笔试题
  • 真题详解7 某知名游戏软件开发公司软件工程师笔试题
  • 真题详解8 某知名电子商务公司软件工程师笔试题
  • 真题详解9 某顶级生活消费类网站软件工程师笔试题
  • 真题详解10 某知名门户网站软件工程师笔试题
  • 真题详解11 某知名互联网金融企业软件工程师笔试题
  • 真题详解12 国内某知名网络设备提供商软件工程师笔试题
  • 真题详解13 国内某顶级手机制造商软件工程师笔试题
  • 真题详解14 某顶级大数据综合服务提供商软件工程师笔试题
  • 真题详解15 某著名社交类上市公司软件工程师笔试题
  • 真题详解16 某知名互联网公司软件工程师笔试题
  • 真题详解17 某知名网络安全公司校园招聘技术类笔试题
  • 真题详解18 某知名互联网游戏公司校园招聘运维开发岗笔试题

资料整理不易,点个关注再走吧
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
如何回答系统设计题

  • 经验技巧7 如何解决求职中的时间冲突问题
  • 经验技巧8 如果面试问题曾经遇见过,是否要告知面试官
  • 经验技巧9 在被企业拒绝后是否可以再申请
  • 经验技巧10 如何应对自己不会回答的问题
  • 经验技巧11 如何应对面试官的“激将法”语言
  • 经验技巧12 如何处理与面试官持不同观点这个问题
  • 经验技巧13 什么是职场暗语

[外链图片转存中…(img-cZAqKNm7-1713812130940)]

面试真题篇
  • 真题详解1 某知名互联网下载服务提供商软件工程师笔试题
  • 真题详解2 某知名社交平台软件工程师笔试题
  • 真题详解3 某知名安全软件服务提供商软件工程师笔试题
  • 真题详解4 某知名互联网金融企业软件工程师笔试题
  • 真题详解5 某知名搜索引擎提供商软件工程师笔试题
  • 真题详解6 某初创公司软件工程师笔试题
  • 真题详解7 某知名游戏软件开发公司软件工程师笔试题
  • 真题详解8 某知名电子商务公司软件工程师笔试题
  • 真题详解9 某顶级生活消费类网站软件工程师笔试题
  • 真题详解10 某知名门户网站软件工程师笔试题
  • 真题详解11 某知名互联网金融企业软件工程师笔试题
  • 真题详解12 国内某知名网络设备提供商软件工程师笔试题
  • 真题详解13 国内某顶级手机制造商软件工程师笔试题
  • 真题详解14 某顶级大数据综合服务提供商软件工程师笔试题
  • 真题详解15 某著名社交类上市公司软件工程师笔试题
  • 真题详解16 某知名互联网公司软件工程师笔试题
  • 真题详解17 某知名网络安全公司校园招聘技术类笔试题
  • 真题详解18 某知名互联网游戏公司校园招聘运维开发岗笔试题

[外链图片转存中…(img-grmu1FPs-1713812130941)]

资料整理不易,点个关注再走吧
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值