Unit 6:数组和矩阵

Unit 6 数组和矩阵

Q1:螺旋遍历矩阵
Q2:将矩阵顺时针旋转90度
Q3:之字形打印矩阵
Q4:求数组中无序连续子数组的长度
Q5:数组中出现次数超过一半的数字
Q6:搜索二维矩阵
Q7:求最长可整合子数组的长度
Q8:未排序数组中累加和为给定值k的最长子数组长度
Q9:未排序正数数组中累加和为k的最长子数组长度
Q10:自然数数组的排序
Q11:按奇偶排序数组
Q12:连续子数组的最大和
Q13:子矩阵的最大累加和
Q14:寻找任意峰值数组
Q15:子数组的最大累乘积
Q16:边界都是1的最大正方形大小
Q17:不包含本位置值的累乘数组
Q18:缺失的最小正整数
Q19:寻找数组中重复的数字
Q20:寻找旋转升序数组中的最小值
Q21:矩阵中的单词
Q22:机器人在矩阵中的运动范围
Q23:使数组中奇数排在偶数前面
Q24:把数组排成最小的数
Q25:数组中的逆序对
Q26:统计数字在排序数组中出现的次数
Q27:数组中只出现一次的数字
Q28:买卖股票的最佳时机
Q29:找出数组中第k大的值

 

unit 6 Q1:螺旋遍历矩阵(顺时针)

Leetcode 54 难度:中等 剑指offer 第29题 牛客链接
在这里插入图片描述
时间复杂度O(N),额外时间复杂度O(N)
date:2019/10/08
思路:
1.按圈层打印矩阵,一圈接一圈。
用左上角坐标(tR,tC)和右下角坐标(bR,bC)来确定一个矩阵;
在这里插入图片描述
2.每圈置curR、curC用于遍历,初值为tR和tC
分为三种情况:

  • (1).只有一行(tR== bR),从左至右遍历一次
  • (2).只有一列(tC== bC),从上至下遍历一次
  • (3).正常情况,开始绕圈:
    先curC一直向右走,走到bC
    再curR一直向下走,走到bR
    再curC一直向左走,走到tC
    再curR一直向上走,走到tR

3.每走完一圈后更新矩阵,tR++,tC++,bR- -,bC- -

代码:

    public ArrayList<Integer> printMatrix(int [][] matrix) {
        ArrayList<Integer> list = new ArrayList<>();
        if(matrix==null||matrix.length==0||matrix[0].length==0)
            return list;
        int tR=0,tC=0,bR=matrix.length-1,bC=matrix[0].length-1;
        int curR=0,curC=0;
        for(; tR<bR && tC<bC ;tR++,tC++,bR--,bC--){
            //从[tR][tC]到[tR][bC]
            for(curC=tC;curC<bC;curC++)
                list.add(matrix[tR][curC]);
            //从[tR][bC]到[bR][bC]
            for(curR=tR;curR<bR;curR++)
                list.add(matrix[curR][bC]);
            //从[bR][bC]到[bR][tC]
            for(curC=bC;curC>tC;curC--)
                list.add(matrix[bR][curC]);
            //从[bR][tC]到[tR][tC]
            for(curR=bR;curR>tR;curR--)
                list.add(matrix[curR][tC]);
        }
        //只有一行
        if(tR==bR)
            for(curC=tC;curC<bC+1;curC++)
                list.add(matrix[tR][curC]);
        //只有一列
        else if(tC==bC)
            for(curR=tR;curR<bR+1;curR++)
                list.add(matrix[curR][tC]);
        return list;
    }

 

unit 6 Q2:将矩阵顺时针旋转90度

Leetcode 48 难度:中等
要求:额外时间复杂度为O(1)
date:2019/10/09
思路:
还是按圈层遍历的思路,一层一层的转。对一层的每个位置都转一遍。如图所示
在这里插入图片描述
代码:U6Q2_rotateMatrix.java

 

unit 6 Q3:之字形打印矩阵

在这里插入图片描述
要求额外时间复杂度为O(1)
date:2019/10/10
思路:
1.还是分圈层遍历。不用之处在于,(tR,tC)指向矩阵的右上,(bR,bC)指向矩阵的左下,初值均为(0,0)
2.每层遍历打印(tR,tC)和(bR,bC)之间即斜对角线上的点即可。使用isPos决定正着打印还是反着打印。
3.矩阵更新规则:
(tR,tC):右上角先沿着矩阵第一行向右移动(tC++),到头后再沿着矩阵最后一列向下移动(tR++);
(bR,bC):左下角先沿着矩阵第一列向下移动(bR++),到头后再沿着矩阵最后一行向右移动(bC++);
右上角(tR,tC)走到矩阵的右下角时停止。
示例:
在这里插入图片描述
代码:U6Q3_zigZagMatrix.java

 

unit 6 Q4:求数组中无序连续子数组的长度

给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
你找到的子数组应是最短的,请输出它的长度。
Leetcode 581 难度:简单
要求时间复杂度为O(N),额外空间复杂度为O(1)
date:2019/10/11
思路:
想一想,如果一个点比它左边的最大值小,那该点肯定不符合升序,是无序子数组里边的;

同理,如果一个点比它右边的最小值大,那该点肯定也不符合升序,也是无序子数组里边的。
实现:
两趟遍历。

  • 1.从左往右遍历,找不符合升序(比左边最大值max还要小)的点的下标,该点指向无序子数组的最右边,存为subRight
  • 2.从右往左遍历,找不符合升序(比右边最小值min还要大)的点的下标,该点指向无序子数组的最左边,存为subLeft

(subRight-subLeft+1)即为无序子数组的长度,返回之。

代码:U6Q4_unsortedSubArr.java

 

unit 6 Q5:数组出现次数超过一半的数字

剑指offer 39 Leetcode 169 难度:简单
date:2019/10/12 date:2020/02/14更新!!!
思路:
方法1(可):遍历数组用hashtable存储各元素的出现次数,记录出现最多的数mode和它的出现次数count,每次遍历更新之。
时间复杂度O(N),额外空间复杂度O(N)

方法2(不可)
2020/02/14更新:既然此方法时间复杂度O(nlogn),还有额外空间复杂度。为啥不能先O(nlogn)排好序,再遍历一遍找出现次数大于(n/2)的次数呢?还没有空间复杂度。。。

分治算法递归求解:
int mode_recur(int[] arr,int left,int right){
//返回arr[left…right]内的众数}
int countInRange(int[] arr,int num,int left,int right){
//返回arr[left…right]内num的出现次数}

每一层的mode_recur()都将当前数组arr[left…right]从中间一分为二。

  • 1.如果子数组只有一个元素则众数就是该元素,返回之。
  • 2.递归调用mode_recur()求得左边数组的众数 leftMode,递归调用mode_recur()求得右边数组的众数rightMode;
  • 3.调用countInRange()求得左众数 leftMode 在左子数组arr[left…mid]中的出现次数,
    调用countInRange()求得右众数 rightMode 在右子数组arr[mid+1…right]中的出现次数
    左右子数组的众数中,出现次数较多者即为当前数组的众数,返回之。

时间复杂度O(nlogn): 会求解量个长度为n/2的子问题,并做两遍长度为n的遍历。T(n)=2T(n/2)+2n,解得T(n)=O(nlogn)
额外空间复杂度O(logn):该递归树是平衡的,从根到叶节点的长度为logn

方法三(推荐):(2020/02/14更新)
如果该数出现次数超过一半,则该数出现次数大于其他数出现次数之和。则此方法遍历数组时设置一个res和次数times。遍历到arr[i]等于res时,times++;反之times- -。times减到0时,重置res并置times为1。如果出现次数超过一半的数存在,则遍历后剩下的res即是该数。
与剑指offer原书不同的是,牛客网OJ上的题设,有可能不存在出现次数超过一半的数。这时候需要再遍历一遍数组并记录res的出现次数,做个验证即可。
该方法时间复杂度O(n),额外空间复杂度O(1)

代码:

    public int MoreThanHalfNum_Solution(int [] array) {
        if(array==null||array.length==0)
            return 0;
        int times=1,res=array[0];
        for(int i=1;i<array.length;i++){
            if(times==0){
                res=array[i];
                times=1;
            }else{
                if(array[i]==res)
                    times++;
                else
                    times--;
            }
        }
        //和剑指offer原书不同之处在于,他有可能不存在出西安次数超过一半的数字。所以做一个验证
        return isLegal(array,res)?res:0;
    }
    private boolean isLegal(int[] arr,int res){
        int times=0;
        for(int i=0;i<arr.length;i++){
            if(arr[i]==res)
                times++;
        }
        return times>(arr.length/2);
    }

 

unit 6 Q6:搜索二维矩阵

剑指offer 4 Leetcode 74 难度:中等

判断M × \times ×N的矩阵matrix中是否存在目标值target。
该矩阵的特点:
1.每行升序排列
2.每行的第一个数大于前一行的最后一个数
date:2019/10/13
思路:
M × \times ×N的有序矩阵可以理解为长度为M × \times ×N的有序数组,改动二分查找,以时间复杂度O(log(M × \times ×N))完成
实现:
1.置左边界left为0,右边界right为(M × \times ×N-1)
2.while(left<=right):

  • (1).置中间序号pivot=(left+right)/2;
  • (2).中间序号对应到数组中即arr[pivot]的值 等于 matrix[pivot/n][pivot%n]
  • (3).target等于中间值,找到了,返回true;
    target比中间值大,则向右找即left=pivot+1;
    target比中间小,则向左找即right=pivot-1.

3.出while循环还没找到,返回false。

date:2020/05/10更新:
剑指offer 4 是这样的。差不多的意思,对每行求一个二分查找
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
代码:

public class Solution {
    public boolean Find(int target, int [][] array) {
        if(array==null||array.length==0||array[0].length==0) 
        return false;
        //对每一行做一次二分查找
        for(int i=0;i<array.length;i++){
        	//二分查找
            int low=0;
            int high=array.length-1;
            while(high>=0&&low<=high){
                int mid=(low+high)/2;
                if(array[i][mid]==target){
                    return true;
                }else if(array[i][mid]>target){
                    high=mid-1;
                }else{
                    low=mid+1;
                }
            }
        }
        return false;
    }
}

 

unit 6 Q7:求最长可整合子数组的长度

先给出可整合数组的定义: 如果一个数组arr在排序之后,从最小值到最大值的顺序中,每相邻两个数之间差的绝对值都为1,则arr为可整合数组。 例如: arr = {5,3,4,6,2},再排序之后为:{2,3,4,5,6},排序后符合每相邻两个数之间差的绝对值都为1,所以arr是可整合数组。 给定一个整形数组arr,请返回其中长度最大的可整合子数组的长度。
[5,0,1,2,4,3,9],最长可整合子数组为[5,0,1,2,4,3],所以返回6
[6,7,3,0,1,2,4,7],最长可整合子数组为[3,0,1,2,4],所以返回5
要求:如果数组长度为N,时间复杂度请达到O( N 2 N^2 N2)
date:2019/10/14
思路
核心是可整合数组的优化判断法:如果子数组无重复元素且(max-min+1)=子数组的长度,则该子数组为可整合数组
实现
方法一:暴力解法,穷举所有子数组O( N 2 N^2 N2),判断是否是可整合数组O(N l o g 2 log_2 log2N),总时间复杂度为O( N 3 N^3 N3 l o g 2 log_2 log2N),略。
方法二:判断是否是可整合数组时采用优化判断法O(1),总时间复杂度为O( N 2 N^2 N2)
1.两层for循环遍历arr,
第一层用i固定住左边,设置最大值max和最小值min,设置hashSet型set判断重复。
第二层用j向右遍历,每次j移动都更新max和min。
如果当前arr[j]在set中出现过,直接break,否则将arr[j]添加入set。
如果当前(max-min+1)==(j-i+1),则当前arr[i…j]是可整合数组,长度为(j-i+1),更新最长的maxLen
2.两层for循环之后,maxLen即为最长可整合子数组的长度,更新之。

代码:U6Q7_longestIntrgSubArr.java

 

unit 6 Q8:未排序数组中累加和为给定值k的最长子数组长度

Leetcode 325 难度:中等 (付费题)
参考博客:数组中累加和为定值K的最长子数组长度
给定一无序数组arr,求arr的所有子数组中累加和为k的最长子数组长度
时间复杂度:O(N),额外空间复杂度O(N)
date:2019/10/15
思路
设s(i)为arr[0…i]的累加和,s[j]即为arr[0…j]的累加和
则子数组arr[j+1,i]的累加和=s[i]-s[j]。
理论上,算出s[0]、s[1]…s[n-1],下标大的减下标小的,即可求出所有的子数组长度。
如果采用暴力方法,两层for循环遍历求出符合(s[i]-s[j])==k的子数组记录maxLen,时间复杂度O(N^2)
现在采用哈希表记录存之前的每个(k,v)=(s[j],j),只用时间复杂度O(N)
实现
1.遍历前设置hashTable存(key,value)=(arr[0…j]的累加和,j)
向hashtable中添加(0,-1) (因为子数组若以下标0开头,hashtable中找不到key=0的项,出现错误)
置maxLen记录最长子数组的长度,初值为0
置sum记录当前数组arr[i]的长度,初值为0

2.一轮for循环遍历,

  • (1).更新sum=sum+arr[i],即更新s[i]
  • (2).想找arr[j+1…i]累加和为k,
    则置need_sj计算当前需要前数组arr[0…j]的累加和等于多少,need_sj=sum-k。
  • (3).如果need_sj在hashtable中存在,取其下标j=hashtable.get(need_sj)(此时说明arr[j+1…i]是累加和为k的数组)
    更新maxLen,maxLen取maxLen和(i-j)中较大者
  • (4).如果hashtable中没有sum即s[i],将当前(sum,i)加入hashtable

3.出for循环后记得最长子数组长度,返回maxLen

代码:U6Q8_longestEqualToKSubArr.java

 

unit 6 Q9:未排序正数数组中累加和为k的最长子数组长度

在 U6Q8的问题上,增加数组是正数这一条件。
时间复杂度O(N),额外空间复杂度O(1)
date:2019/10/16
参考博客:数组中累加和为定值K的最长子数组长度
思路
滑动窗口机制,arr[left…right]表示子数组。
先固定左边不动,右边扩展。当前数组累加和小于k,则一直向右扩展,直到子数组累加和大于k为止。
这时左边向左收缩一个,累加和相应变小,再右边扩张,如此反复至right到头为止,每一次累加和等于k时更新maxLen。
实现
1.置left,right分别指向子数组的左右,初值为0。置sum记录子数组累加和,初值为0。置maxLen记录子数组最大长度,初值为0。
2.right不到头时while循环:

  • (1).sum<k:将arr[right]加入sum,右边向外扩,right++
  • (2).sum=k:更新maxLen;arr[left]移出sum,左边向里缩,left++
  • (3).sum>k:arr[left]移出sum,左边向里缩,left++

3.返回maxLen。

代码:U6Q9_longestEqualToSubArr2.java

 

unit 6 Q10:自然数数组的排序

给定一个长度为N的整型数组arr,其中有N个互不相等的自然数1-N,请实现arr的排序,但是不要把下标0-(N-1)位置上的数通过直接赋值的方式替换成1-N
要求:时间复杂度为O(N),额外空间复杂度为O(1)
date:2019/10/19
思路:
for循环连着遍历:
如果arr[i]==i+1:下一位
如果arr[i]!=i+1:
(1想去位置0的位置)arr[i]想去arr[i]-1的位置,
将arr[arr[i]-1]和arr[i]对换即可,
while循环直到换到arr[i]==i+1时,再i++遍历下一个

奇奇怪怪一道题,看起来简单而已

代码:U6Q10_naturalNumSort.java

 

unit 6 Q11:按奇偶排序数组

Leetcode 922 难度:简单
date:2019/10/20
给定一个非负整数数组 A, A 中一半整数是奇数,一半整数是偶数。
对数组进行排序,以便当 A[i] 为奇数时,i 也是奇数;当 A[i] 为偶数时, i 也是偶数。
你可以返回任何满足上述条件的数组作为答案。
思路
找到一个偶数位是奇数的前提下,找奇数位上的偶数,找到之后再互换。
实现

  • 1.for循环里套while循环,for循环遍历偶数位,while遍历奇数位找奇数位上的偶数
  • 2.置i指向偶数位置初值为0,置j指向奇数位置初值为1.
  • 3.for循环用i遍历偶数位,当i碰到奇数时,停下进入while循环:
    while循环j遍历奇数位,当j碰到偶数时,停。交换arr[i]和arr[j]。
  • 4.继续for循环直到i走到头为止。

代码:U6Q11_sortArrByParity.java

 

unit 6 Q12:连续子数组的最大和

剑指offer 42 牛客链接 Leetcode 53 难度:简单
要求时间复杂度O(N),空间复杂度O(1)
思路:
动态规划(?)
max存储最大值,sum存储累加和。

  • 1.for遍历arr,sum为正(sum对结果有增益效果)一直对sum累加。
  • 2.sum变成负数就不继续加了(因为此时sum对结果无增益效果),重新计数将arr[i]赋给arr[i]
  • 3.更新max
  • 4.出for循环返回max

代码:

    public int FindGreatestSumOfSubArray(int[] arr) {
        int sum=0,max=Integer.MIN_VALUE;
        for(int i=0;i<arr.length;i++){
            if(sum<0)
                sum=arr[i];//sum<0,当前累加和不能对结果产生增益(结果子数组肯定不包含当前sum)
            else
                sum=sum+arr[i];//当前sum是有用的
            max=Math.max(sum,max);//最大的sum存在max中
        }
        return max;
    }

 

unit 6 Q13:子矩阵的最大累加和

leetcode 363 难度:困难
date:2019/10/22
思路:
把待求子矩阵压缩到一行,用Q12最大子序和的方法求该行最大累加和,即为待求子矩阵的最大累加和。
两重循环遍历子矩阵O( N 2 N^2 N2),Q12的方法O(N),总时间复杂度O( N 2 N^2 N2)

代码:U6Q13_maxSubMatrix.java

 

unit 6 Q14:寻找任意峰值数组

Leetcode 162 难度:中等
峰值元素是指其值大于左右相邻值的元素。
给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其下标。数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。
date:2019/10/25
思路:
方法一:遍历一遍找结果,找到第一个符合条件的返回,时间复杂度O(N),空间复杂度O(1)
方法二:二分查找,时间复杂度O(logN),空间复杂度O(1)
1.先判断左右两边即arr[0],arr[len-1]是不是峰值
2.置left初值为1,right初值为(len-2),开始while循环二分查找
3.置mid=(left+right)/2,当mid比左右两边都大时,返回mid
当arr[mid-1]<arr[mid],mid正处于上升坡度,峰值在右,向右爬,left=mid-1;
否则,mid正处于下降坡度,峰值在左,向左爬,right=mid+1。

代码:U6Q14_findPeakIndex.java

nbsp;

unit 6 Q15:子数组的最大累乘积

date:2019/10/27
Leetcode 152 难度:中等
思路:
遍历数组时计算当前最大值。
1.置imax表示以当前节点结束的子数组的最大累乘积,初值为arr[0];
置imin表示以当前节点结束的子数组的最小累乘积,初值为arr[0];
置max记录最大累乘积,初值为MIN_VALUE
2.
遍历数组arr,更新imax,imin,max。更新规则:
如果当前arr[i]为正,则imax从(imaxarr[i])和arr[i]中取,imin从(iminarr[i])和arr[i]中取
如果当前arr[i]为负,arr[i]乘的越小积越大,反过来,imax从(iminarr[i])和arr[i]中取,imin从(imaxarr[i])和arr[i]中取
每回更新最大值max

代码:U6Q15_maxProduct.java

 

unit 6 Q16:边界都是1的最大正方形大小

Leetcode 1139 难度:中等
给你一个由若干 0 和 1 组成的二维矩阵M,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回 0。
date:2019/10/28
思路:
常规思路 O(N^4):
1.对矩阵M[m][n]中的每一位置点M[i][j]。O(N^2)
2.寻找以该点为左上角的正方形,这种正方形有 min(m-i,n-j) 个。O(N)
3.检查每个正方形的四条边是否全为1。O(4N)=O(N)
空间换时间的思路O(N^ 3),空间复杂度O(N^2)
改变常规思路中的第三步。
在这里插入图片描述
1.设置right[][]矩阵,right[i][j]表示M[i][j]右边有几个连续的1;
设置down矩阵,down[i][j]表示M[i][j]下边有几个连续的1。
在这里插入图片描述
2.
Q:如何判断以len为边长的正方形四条边是否全为1?
A:right[i][j]>=len:上边合格
down[i][j]>=len:左边合格
right[i+len-1][j]>=len:下边合格
down[i][j+len-1]>=len:右边合格
3.
Q:如何构造right矩阵、down矩阵?
A:(1).从右下角开始向上,设置right、down矩阵的最右边。
设置规则:
如果M[i][n-1]==1:则right[i][n-1]=1,down[i][n-1]=down[i+1][n-1]+1;
否则M[i][n-1]==0:则right[i][n-1]=0,down[i][n-1]=0;
(2).从右下角开始向左,设置right、down矩阵的最下边。
设置规则:
如果M[m-1][j]==1:则 right[m-1][j]=right[m-1][j+1]+1,
down[m-1][j]=1;
否则M[m-1][j]==0:则 right[m-1][j]=0;
down[m-1][j]=0;

(3).从右下角开始遍历,填满剩下的right、down矩阵。
填充规则:
如果M[i][j]==1:
right[i][j]=right[i][j+1]+1,down[i][j]=down[i+1][j]+1;
如果M[i][j]==0:
right[i][j]=0,down[i][j]=0。

真的麻烦,不知道搞这么复杂干嘛。
代码:U6Q16_largest1BorderedSquare.java

 

unit 6 Q17:不包含本位置值的累乘数组

给定一个数组arr,返回不包含本位置值的累乘数组
例如,arr=[2,3,1,4],返回[12, 8, 24, 6],即除自己外,其他位置上的累乘
要求:时间复杂度为O(n),额外空间复杂度为O(1)
难度:简单
date:2019/10/30
思路:
1.遍历数组,算出全部非0元素的累乘积all;计算0元素的个数count0
2.
如果没有0元素,总累乘积除以当前值即可,res[i]=all/arr[i]
如果0元素只有一个,则0元素的点的res[i]为all,其余点均为0;
如果0元素大于1,则res所有位置都为0;

date:2020/02/25 更新:
题目再加限定:不让用除法
剑指offer 66 牛客链接
构建辅助数组C[i]=A[0]*…*A[i-1]*A[i],O(n)
构建辅助数组D[i]=A[i]A[i+1]…*A[len-1],O(n)
这样通过B[i]=A[i-1]*C[i+1],O(n),求得B

//不能使用除法的方法:
import java.util.ArrayList;
public class Solution {
    //构建辅助数组C[i]=A[0]*...*A[i-1]*A[i],O(n)
    //构建辅助数组D[i]=A[i]*A[i+1]*...*A[len-1],O(n)
    //这样B[i]=A[i-1]*C[i+1],O(n)
    public int[] multiply(int[] A) {
        int len=A.length;
        int[] B=new int[len];
        int[] C=new int[len];
        int[] D=new int[len];
        //建立C[i]
        //temp暂存累乘的积
        for(int i=0,temp=1;i<len;++i){
            temp=temp*A[i];
            C[i]=temp;
        }
        //如法炮制建立D[i]
        for(int i=len-1,temp=1;i>=0;--i){
            temp=temp*A[i];
            D[i]=temp;
        }
        //按照公式B[i]=C[i-1]*C[i+1]构建B[i]
        for(int i=0;i<len;++i){
            int left=(i==0)?1:C[i-1];
            int right=(i==len-1)?1:D[i+1];//边界情况
            B[i]=left*right;
        }
        return B;
    }
}

 

unit 6 Q18:缺失的最小正整数

给定一个未排序的整数数组,找出其中没有出现的最小的正整数。
例:[1,2,0],返回3
[-2,-3,-1,-4],返回1
[7,8,9,11,12],返回1
Leetcode 41 难度:困难
date:2019/10/31
思路:
先"排序",“排序”后第一个不符合要求的点arr[i],(i+1)即为缺失的最小正整数。
排序规则:arr[i]一定要存值为(i+1)的元素
1.“排序”:
从i=0开始for循环遍历arr,将arr[i]的值num换到arr[num-1]上去,一直换换换,换到arr[i]的值为(i+1)或不在范围内(怎么也排不到)停止
2.找不符合的arr[i]
再遍历一遍arr,找到不符合arr[i]!=i+1的,返回i+1。
3.如果能跳出第二遍遍历,说明arr每个元素都符合arr[i]==i+1,返回(len+1)。

代码:U6Q18_firstMissingPos.java

 

unit 6 Q19:寻找数组中重复的数字

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
例:输入: [1,3,4,2,2] 输出: 2
输入: [3,1,3,4,2] 输出: 3
剑指offer 3 Leetcode 287 难度:中等
date:2019/11/5

方法一:
我让1必须出现在0的位置上,2必须出现在1的位置上。得到的规律:arr[i]位置上的值为i+1;i须出现在arr[i-1]位置上.

1.每轮for循环使得arr[i]位置上的值必须为i+1。
2.for循环里的while循环把当前值cur=arr[i]放在arr[cur-1]的位置上,只有满足当前arr[i]==i+1时才会中止while循环。
3.while循环中遇到arr[cur-1]==cur,则cur就是重复的那个,返回cur。
4.出for循环,肯定哪出错了,返回-1。
代码:

class Solution {
    public int findDuplicate(int[] arr) {
        //1必须出现在0的位置上。得到的规律:
        //arr[i]位置上的值为i+1;i须出现在arr[i-1]位置上
        int len=arr.length;
        //每轮遍历使得arr[i]位置上的值必须为i+1
        for(int i=0;i<len;++i){
            //此位置满足
            if(arr[i]==i+1)
                continue;
            //不满足则进while循环找,找到arr[i]==i+1为止
            while(arr[i]!=i+1){
                int cur=arr[i];//cur应放在arr[cur-1]位置上
                //cur想放的地儿重复了,找到你了
                if(arr[cur-1]==cur)
                    return cur;
                //arr[i]与arr[cur-1]互换
                swap(arr,i,cur-1);
                //一定能换到arr[i]==i+1
            }
        }
        return -1;
    }

    void swap(int[] nums,int i,int j){
        int temp=nums[i];
        nums[i]=nums[j];
        nums[j]=temp;
    }
}

方法二(不移动数组):
二分查找法,找重复的数出现在哪一边。时间复杂度O(Nlog2N)。

class Solution {
    //如果正常的left到right无重复,则<=mid的个数等于mid的大小
    //例:1~6无重复,mid=3,<=mid的个数应该等于mid。
    //如果<=mid的个数大于mid,则重复的数出现在1~mid之间;
    //如果<=mid的个数等于mid,则重复的数在mid右边,mid+1~right之间。
    public int findDuplicate(int[] arr) {
        int len=arr.length;
        int left=1,right=len;//刚开始,重复数字在left~right之间
        while(left<right){
            int mid=(left+right)/2;//中间的数
            int curSmaller=0;//<=mid的个数
            //统计arr中<=mid的个数
            for(int i=0;i<len;++i){
                if(arr[i]<=mid)
                    curSmaller++;                    
            }
            //小于等于mid的数多了,left~mid不正常,要去左边left~mid中间找
            if(curSmaller>mid)
                right=mid;
            else
            //小于等于mid的数相等,left~mid正常,去mid+1~right中间找
                left=mid+1;
        }
        return left;
    }
}

 

unit 6 Q20:寻找旋转升序数组中的最小值

剑指offer 11 Leetcode 153(数组中元素不可重复) 难度:中等
Leetcode 154(数组中元素可重复) 难度:困难
要求:
假设按照升序排序的数组在预先未知的某个点上进行了旋转,请找出其中最小的元素。
例:数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2]。寻找后者的最小元素为0。
date:2019/12/5
思路
最简单的方法:全遍历一遍,时间复杂度O(n);改写二分查找,可以做到时间复杂度O(log2n)
将旋转升序数组分为两个部分,最小值其实就是第二个数组的首元素,我们要找到第二个数组首元素下标i
1.while循环,二分查找,不断缩小left和right范围
2.返回条件:找分界点,分界点右边的即为所求。有可能mid落在分界点左或分界点右,需要分类讨论

  • A[mid]>A[mid+1]:mid+1是i,返回A[mid+1]
  • A[mid-1]>A[mid]:mid是i,返回A[mid]

3.缩小left和right的范围

  • A[mid]>A[right]:mid比最右边大,则mid一定在第一个数组,i满足mid<i<=right。向右找,执行left=mid+1
  • A[mid]<A[right]:mid比最右边小,则mid一定在第二个数组,i满足left<=i<=mid。向左找,执行right=mid
  • A[mid]==A[right]:mid等于最右边,将mid向左挪一个。执行right=right-1

代码:

    public int findMin(int[] A){
        int left=0,right=A.length-1;
        while(left<=right) {
            int mid = (left + right) / 2;
            //返回条件:返回分界点右,第二数组之首
            if (A[mid] > A[mid + 1])
                return A[mid + 1];
            if (A[mid - 1] > A[mid])
                return A[mid];

            if (A[mid] > A[right])
                //mid比最右边大,则mid一定在第一个数组。
                //则最小值下标i一定满足mid<i<=right,向右找i,执行left=mid+1
                left = mid + 1;
            else if (A[mid] < A[right])
                //mid比最右边小,则mid一定在第二个数组。
                //i满足left<=i<=mid,向左找i,right=mid
                right = mid;
            else
                //A[mid]==A[right] 一样大,right向左挪一格。
                right = right - 1;
        }
        return -1;
    }

 

unit 6 Q21:矩阵中的单词

剑指offer 12 Leetcode 79 难度:中等
要求
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
例:
board =[
[‘A’,‘B’,‘C’,‘E’],
[‘S’,‘F’,‘C’,‘S’],
[‘A’,‘D’,‘E’,‘E’]],

给定 word = “ABCCED”, 返回 true.
给定 word = “SEE”, 返回 true.
给定 word = “ABCB”, 返回 false.
date:2019/12/6
思路
改写深度优先搜索DFS,每次遍历的时候,如果遍历到当前M[i][j]与words[start]对上了,则向上、下、左、右四个方向试探,看这四个方向的能不能与words[start+1]对上。
dfs会一直向下走,只有words中的所有字母都对上了,才会一路返回上来,返回true;中间有一个没对上,只能一路返回false。
要注意,进入匹配上下左右的递归dfs时,对当前visited[i][j]置为true,如果M[i][j]上下左右都没匹配成功,说明这个点虽然遍历过但这次没用上,置其visited[i][j]为false
这道题不简单,复习看代码,看注释。

代码:

    public boolean exist(char[][] matrix,String word){
        char[] words=word.toCharArray();
        int m=matrix.length,n=matrix[0].length;//m:长 n:宽
        boolean[][] visited=new boolean[m][n];
        //每个点开头都试一次
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(dfs(matrix,i,j,words,0,visited))
                    return true;
            }
        }
        //出了for循环,dfs还没true过。没找到,返回false
        return false;
    }

    //遍历到matrix[i][j],看matrix[i][j]是不是words[start],如果是,接着试旁边的点
    public boolean dfs(char[][] matrix,int i,int j,char[] words,int start,boolean[][] visited){
        //i,j坐标不合法 or start不合法,false
        if(i>=matrix.length || j>=matrix[0].length || i<0 || j<0 || start>=words.length)
            return false;
        //如果该点遍历过了,不用再遍历,false
        if(visited[i][j]) return false;
        
        char cur=words[start];
        //如果当前对上了
        if(cur==matrix[i][j]){
            //words找到最后一个了,终止dfs递归
            if(start==words.length-1)
                return true;

            //还没找完呢,则向上下左右找
            visited[i][j]=true;
            //上
            if(dfs(matrix,i-1,j,words,start+1,visited)) return true;
            //上走不通则下
            if(dfs(matrix,i+1,j,words,start+1,visited)) return true;
            //左
            if(dfs(matrix,i,j-1,words,start+1,visited)) return true;
            //右
            if(dfs(matrix,i,j+1,words,start+1,visited)) return true;
            //走到这还没返回,说明M[i][j]点的上、下、左、右都不行
            visited[i][j]=false;
        }
        //当前没对上,或者之前的上下左右都没走通。返回false
        return false;

    }

 

unit 6 Q22:机器人在矩阵中的运动范围

剑指offer 13
有一m行n列的矩阵。一个机器人从坐标(0,0)的格子开始移动,每次都可以上、下、左、右移动一格,但如果行坐标和列坐标的数位之和大于k,该格子不能被进入。求问能进入多少个格子。
例:k=18时:(35,37)可进入,因为3+5+3+7=18;(35,38)不可进入,因为3+5+3+8=19>18
date:2019/12/7
思路
跟Q21题很像,用深度优先搜索,从(0,0)开始,如果当前(i,j)可以进入,则尝试其上下左右四个方向能否进入,将其能入的计数,最后相加。还需要将整数逐位相加,这一步是基本功难度不大。

代码:

    public int robotCount(int m,int n,int k){
        int[][] matrix=new int[m][n];
        boolean[][] visited=new boolean[m][n];
        return dfs(matrix,0,0,k,visited);
    }

    private int dfs(int[][] matrix,int i,int j,int k,boolean[][] visited){
        if(i>=matrix.length || j>=matrix[0].length || i<0 || j<0)
            return 0;
        //访问过了
        if(visited[i][j])
            return 0;
        int count=0;
        //如果该点合法
        if(isValid(i,j,k)){
            count++;
            visited[i][j]=true;
            //看看上下左右,能加就加进去
            int up=dfs(matrix,i-1,j,k,visited);
            int down=dfs(matrix,i+1,j,k,visited);
            int left=dfs(matrix,i,j-1,k,visited);
            int right=dfs(matrix,i,j+1,k,visited);
            count=count+up+down+left+right;
        }
        return count;
    }

    private boolean isValid(int i,int j,int k){
        int sum=0;
        //对i位置累加
        while(i/10!=0){
            sum+=i%10;
            i=i/10;
        }
        sum+=i;
        //对j位置累加
        while(j/10!=0){
            sum+=j%10;
            j=j/10;
        }
        sum+=j;
        return (sum<=k);
    }

 

#### unit 6 Q23:使数组中奇数排在偶数前面

date:2020/02/08

  • 普通版:不要求保持奇数和奇数,偶数和偶数之间的相对位置不变。

剑指offer 20 Leetcode 905 难度:简单
模仿快速排序思想。左碰偶停,右碰奇停,i小于j,两边互换。

    public void reOrderArray(int [] array) {
        int i=0,j=array.length-1;
        while(i<j){
            while(i<j&&!isEven(i))
                i++;//从前面找到一个偶数
            while(i<j&&isEven(j))
                j--;//从后面找到一个偶数
            if(i<j){
                //交换
                int temp=array[i];
                array[i]=array[j];
                array[j]=temp;
                //i,j各进一步
                i++;
                j--;
            }
        }
    }
    //是否是偶数
    boolean isEven(int num){
        if(num%2==0)
            return true;
        else
            return false;
    }
  • 加强版:要求保持奇数和奇数,偶数和偶数之间的相对位置不变。
    牛客链接
    模仿插入排序的思想。每轮遍历,找到从i开始的第一个偶数。从该偶数开始,数组的后半部分全部前移;再该偶数插在最后。
    遍历结束条件:i>=len-k,k是已插过的偶数数量。i遍历是用来找偶数的,i都找到已插好的偶数中去了,则排序结束。
    public void reOrderArray(int [] array) {
            int i=0,k=0;//k:已插入k个偶数到最后了
            while(i<array.length-k){
                while(i<array.length-k&&!isEven(array[i]))
                    i++;//找到第一个偶数
                if(i<array.length-k){
                    int temp=array[i];
                    //将该偶数之后的所有,都向前挪一个
                    for(int j=i;j+1<array.length;j++)
                        array[j]=array[j+1];
                    array[array.length-1]=temp;//把该偶数插到最后
                    k++;//又插好一个偶数
                }
            }
        }
    //是否是偶数
    boolean isEven(int num) {
        if (num % 2 == 0)
            return true;
        else
            return false;
    }

 

#### unit 6 Q24:把数组排成最小的数

剑指offer 45 牛客链接
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为"321323"。
date:2020/02/16
思想:
把int[]数组转换成字符串数组。然后对字符串数组排序,排序后合成一个字符串输出。
Q:排序时如何比较两个不等长字符串的大小?
A:把两字符串a、b合起来比较
ab>ba:a>b
ba>ab:b>a
ab=ba:a=b

代码:

    public String PrintMinNumber(int [] numbers) {
        String res="";
        if(numbers==null||numbers.length==0)
            return res;
        int len=numbers.length;
        String[] str=new String[len];
        for(int i=0;i<len;i++)
            str[i]=String.valueOf(numbers[i]);//转成str类型的
        bubbleSort(str);
        for(int i=0;i<len;i++)
            res=res+str[i];
        return res;
        
    }
    //ab>ba:a>b
    //ba>ab:b>a
    //ab=ba:a=b
    private int compareAtoB(String A,String B){
        String ab=A+B,ba=B+A;
        return ab.compareTo(ba);//A<B:<0 A=B:=0 A>B:>0
    }    
    
    private void bubbleSort(String[] str){
        int len=str.length;
        for(int i=0;i<len-1;i++){
            boolean flag=false;//本趟是否发生交换
            for(int j=len-1;j>i;j--){
                if(compareAtoB(str[j-1],str[j])>0){
                    String temp=str[j-1];
                    str[j-1]=str[j];
                    str[j]=temp;
                    flag=true;
                }
            }
            if(!flag)
                break;
        }
    }

 

#### unit 6 Q25:数组中的逆序对

剑指offer 51 Leetcode链接
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例:
输入: [7,5,6,4]
输出: 5
date:2020/02/19
思路:
链接:
归并排序的改进,把数据分成前后两个数组(递归分到每个数组仅有一个数据项),
合并数组,合并时,出现前面的数组值arr[i]大于后面数组值arr[j]时,则arr[i]大于arr[mid+1]到arr[j]的所有值。这些数有(j-mid)个。如果arr[i]<arr[j],j向后退。每次选出一个最大的放到copy数组里。
参考剑指Offer,看看代码吧,注释挺全。

	//Leetcode 通过版
	    int[] copy;
    public int reversePairs(int[] arr) {
        if(arr==null||arr.length==0)
            return 0;
        //对copy数组进行初始化
        copy=new int[arr.length];
        for(int i=0;i<arr.length;i++)
            copy[i]=arr[i];
        int count=reverseCore(arr,0,arr.length-1);
        return count;
    }
    //返回arr[low]到arr[high]区间内逆序对个数
    int reverseCore(int[] arr,int low,int high){
        if(low==high)
            return 0;
        int mid=(low+high)/2;
        //先向下递归
        int leftCount=reverseCore(arr,low,mid);
        int rightCount=reverseCore(arr,mid+1,high);
        //递归体
        int count=0;
        int i=mid,j=high;//i遍历左子数组,j遍历右子数组;都从最右边开始
        int locCopy=high;//复制数组的下标
        while(i>=low&&j>=mid+1){
            //左边比右边大。
            //由于左、右子数组都是有序的,则arr[i]比arr[mid+1]..arr[j]都大,可以和他们都组成逆序对。
            //arr[mid+1]到arr[j]共有j-(mid+1)+1=(j-mid)个数
            if(arr[i]>arr[j]){
                count+=j-mid;
                copy[locCopy]=arr[i];//较大者存进copy的右边。其实就是排序结果
                locCopy--;i--;
            }else{
                //左边比右边小,当前arr[i]和arr[j]都不能组成逆序对了
                //只更新排序数组
                copy[locCopy]=arr[j];
                locCopy--;j--;
            }
        }
        //出了while循环,一定是左、右两子数组有一个遍历到头了
        //把左、右子数组剩下的部分(已排序)放入copy即可
        //以下两个while只会执行一个
        while(i>=low){
            copy[locCopy]=arr[i];
            locCopy--;i--;
        }
        while(j>=mid+1){
            copy[locCopy]=arr[j];
            locCopy--;j--;
        }
        //将copy[low]..copy[high]赋给arr[low]..arr[high]使其有序
        for(int k=low;k<=high;k++)
            arr[k]=copy[k];
        //可以返回arr[low]...arr[high]的逆序对总数了
        return leftCount+rightCount+count;   
    }

 

unit 6 Q26:统计数字在排序数组中出现的次数

剑指offer 53_1 牛客链接
date:2020/02/19
思想:
暴力方法,时间复杂度O(n)。
由于是排序数组,想到用归并排序。
修改归并排序退出的规则,先找到该数第一次出现的下标firstK,再找到该数最后一次出现的下标lastK。(lastK-firstK+1)该数的出现次数。
由于归并排序的时间复杂度尾O(log2N),改进的方法时间复杂度尾O(log2N)
代码:

    public int GetNumberOfK(int [] array , int k) {
        int firstK=firstKIndex(array,k);
        int lastK=lastKIndex(array,k);
        return (firstK==-1||lastK==-1)?0:lastK-firstK+1;
    }
    
    int firstKIndex(int[] arr,int k){
        int low=0,high=arr.length-1;
        int mid;
        while(low<=high){
            mid=(low+high)/2;
            if(arr[mid]==k&&(mid-1<0||arr[mid-1]!=k))
                return mid;
            if(arr[mid]<k)
                low=mid+1;//往右走
            else
                high=mid-1;//arr[mid]相等或者大于k,都往左走
        }
        return -1;
    }
    
    int lastKIndex(int[] arr,int k){
        int low=0,high=arr.length-1;
        int mid;
        while(low<=high){
            mid=(low+high)/2;
            if(arr[mid]==k&&(mid+1>=arr.length||arr[mid+1]!=k))
                return mid;
            if(arr[mid]>k)
                high=mid-1;//向左走
            else
                low=mid+1;
        }
        return -1;
    }

 

unit 6 Q27:数组中只出现一次的数字

date:2020/02/20

除了某元素只出现一次以外,其余每个元素均出现两次

Leetcode 136 难度:简单
异或的性质:

  1. 交换律: a ^ b ^ c == a ^ c ^ b
  2. 任何数与0异或为自己 0 ^ n == n
  3. 相同的数异或为0 n ^ n == 0

根据以上异或的三性质,用0开始分别与数组中每个数字异或,最后得到的结果是只出现一次的那个数。(出现两次的数通过交换律放在一起,异或为0)

代码:

class Solution {
    public int singleNumber(int[] nums) {
        int temp=0;
        for(int i=0;i<nums.length;i++)
            temp=temp^nums[i];
        return temp;
    }
}
变形1:除了两个元素只出现一次以外,其余每个元素均出现两次

剑指offer 56_1 牛客链接

不能直接异或得结果,我们可以想到,将数组分为两半,每一半都有一个只出现一次的数字,对两半数组分别做上一题的异或操作即可。
Q:如何实现将数组一分为二,并保证两个只出现一次的数字一边一个?
A:
设只出现一次的数字分别为 a 和 b。
先做一遍异或操作求出a和b的异或值bitResult。
由于该值不为0,则该值的二进制中必有一位是1。再求出该值的二进制从右向左数的第一个为1的下标index。
异或操作是逐位异或的,所以可以断定a和b在从右边数第index位上是不一样的,正因为这样异或结果的index位才是1。而一样的数字的index位一定相同。
所以可以通过index位是否为1,将数组一分为二。一样的数字因为index相同被划分在一起,而a和b因为index位不同而被分开。目的达到。

代码:

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] arr,int num1[] , int num2[]) {
        int len=arr.length;
        if(len==2){
            num1[0]=arr[0];
            num2[0]=arr[1];
        }
        //先遍历一遍,获得两个只出现一次的数字的异或值
        int temp=0;
        for(int i=0;i<len;i++)
            temp=temp^arr[i];
        int bitResult=temp;//结果存在bitResult里
        //找到两出现一次数字的异或值结果的二进制,从右边数第一个1的位置
        int index=findIndex(bitResult);
        //再遍历一遍
        //temp1遍历数组中从右边数第index位是1的数
        //temp2遍历数组中从右边数第index位是0的数
        int temp1=0,temp2=0;
        for(int i=0;i<len;i++)
            if(isBit1(arr[i],index))
                temp1=temp1^arr[i];
            else
                temp2=temp2^arr[i];
        //temp1,temp2交回数组
        num1[0]=temp1;
        num2[0]=temp2;
    }
    
    //找到异或结果的二进制从右边开始第几个数字是1
    private int findIndex(int bitResult){
        int index=0;//从右边数第几个
        //>>:右移运算符 &:位的”与“运算,对两数逐位做"与"运算
        //bitResult最后一位是1时跳出循环
        while((bitResult&1)==0){
            bitResult=bitResult>>1;//二进制的结果右移一位
            index++;
        }
        return index;
    }
    //数target的二进制从右边数第index位是否是1
    private boolean isBit1(int target,int index){
        return ((target>>index)&1)==1;
    }
}
unit 6 Q28:买卖股票的最佳时机

剑指offer 63 Leetcode 121 难度:简单
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。注意你不能在买入股票前卖出股票。

示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

date:2020/02/24
思路:
这道题本质上是找出一前一后两个数,使(后数-前数)的差值最大
暴力方法能想到,对于每个元素找到一个使差值最大的后数,比较所有差值,返回最大的差值。这种方法时间复杂度O(N^2)
进阶方法想到用动态规划的想法。遍历时设置min记录当前的最小值,maxDif记录最大的差值。这种方法O(N)

代码:

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null||prices.length==0)
            return 0;
        int min=prices[0];//迄今为止的最小值
        int maxDif=0;//迄今为止的最大差值
        for(int i=1;i<prices.length;++i){
            maxDif=Math.max(maxDif,prices[i]-min);//更新最大值
            min=Math.min(min,prices[i]);//更新最小值
        }
        return maxDif;
    }
}

 

unit 6 Q29:找出数组中第k大的值

Leetcode 215 难度:中等
date:2020/05/15
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

快速排序的思想。
第1个最大的元素是arr[len-1],则第K个最大的元素是arr[len-k]
快速排序每趟遍历固定一个,找到(len-k)位置上的值即可

class Solution {
    int k;
    int res;
    boolean isFind=false;//是否找到第k个最大的元素
    public int findKthLargest(int[] arr, int k) {
        //快速排序的思想,找到第len-k位置,即为第k个最大的元素
        this.k=k;
        if(arr.length==1)
            return arr[0];
        quickSort(arr,0,arr.length-1);
        return res;
    }
    
    void quickSort(int[] arr,int low,int high){
        //原来的基础上,加上一个isFind
        //快排中是low<high。因为这个需要多一步判断的操作,需要稍微修改
        if(low<=high&&!isFind){
            int pivot=partition(arr,low,high);
            //k=1,取arr[len-1];k=2,取arr[len-2]
            if(pivot==arr.length-k){
                res=arr[pivot];
                isFind=true;
                return;
            }
            quickSort(arr,low,pivot-1);
            quickSort(arr,pivot+1,high);
        }
    }
    
    int partition(int[] arr,int low,int high){
        int temp=arr[low];
        while(low<high){
            while(low<high&&arr[high]>=temp)
                high--;
                arr[low]=arr[high];
            while(low<high&&arr[low]<=temp)
                low++;
            arr[high]=arr[low];
        }
        arr[low]=temp;
        return low;         
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值