数组经典题目

循环不变式:如果某命题初始为真,且每次更改后仍然保持该命题为真,则若干次更改后该命题仍然为真。

1. 求局部最大值

问题描述:给定一个无重复元素的数组A[0...N-1],求找到一个该数组的局部最大值。

分析思路:

  • 如果遍历数组一遍,用一个变量记录数组中的最大值,这样得到的最大值一定也是局部最大值。这种方案的时间复杂度为O(N),空间复杂度为O(1);
  • 考虑另一种方案。用空间换时间。用二分查找的思想,使得时间复杂度为O(logN).

    • 定义一个辅助数组“高原数组”:Array[from...to]满足

      • Array[from] > Array[from-1]
      • Array[to] > Array[to+1]
      • 当该数组的长度为1时,该数组的值就是局部最大值。
      • 为保持一致性,我们定义数组外边界的值为无穷小。即A[0]>A[-1], A[N]>A[N+1].
    • 用两个指针left,right分别指向数组的首和尾,求中点mid=(left+right)/2:
      • A[mid]>A[mid+1],则子数组A[left...mid]为高原数组——right=mid;
      • A[mid]<A[mid+1],则子数组A[mid+1...right]为高原数组——left=mid+1;
      • 递归直至left=right
    • /**
           * Get the local maximum from an array
           * @param nums
           * @return
           */
          public int getLocalMax(int[] nums) {
              int left=0;
              int right = nums.length-1;
              int mid = 0;
              while(left < right) {
                  mid = (left + right) / 2;
                  if(nums[mid] > nums[mid+1])
                      right = mid;
                  else if(nums[mid] < nums[mid+1])
                      left = mid+1;
              }
              return nums[left];
          }

 2. 第一个缺失的整数

问题描述:给定一个数组A[0...N-1],找到从1开始,第一个不在数组中的正整数。

如:3,5,1,2,-3,7,14,8输出4.

分析思路:

  • 思路一:从1开始遍历,看每个数字是否在数组中出现。这样时间复杂度为O(N2);
  • 思路二:申请一个额外的数组,从1开始,将数组遍历一遍,如果该数字在数组中出现则标为true。时间复杂的为O(N),空间复杂度为O(1);
  • 思路三:将一个整数直接放到他应该放的位置。如果是负数或者大于了数组的范围则直接丢弃:时间复杂度为O(N),空间复杂度为O(1).
    • 假定前i-1个数已经找到,并且依次存放在A[1,2,...i-1]中,继续考察A[i]:

      • 若A[i] <= 0 或A[i] >N,则可以直接丢弃;
      • 若A[i] < i且A[i] >= 1,则说明A[i]在前面i-1个位置中已经出现过了,可以直接丢弃;
      • 若A[i] > i且A[i] <= N,则说明应该将该元素放到后面的某个位置上:
        • A[i] 与 A[A[i]]的元素直接交换,i不变;
      • 若A[i] = i,则说明A[i]处于正确的位置上,i++即可,循环不变式扩大。
    • 如果丢弃A[i]呢?
      • 将A[N]赋值给A[i],然后N--。
  •     /**
         * Find the fist positive integer not presented in the array count from 1.
         * @param nums
         * @return
         */
        public int getMinPosNotExist(int[] nums) {
            int i = 0;
            int len = nums.length;
            while(i < len) {
    
                if(nums[i] == i+1)
                    i++;
                else if(nums[i] <= 0 || nums[i] >= len || nums[i] < i-1 ) {
                    nums[i] = nums[len-1];
                    len--;
                }
                else if(nums[i] > i+1 ) {
                    int temp = nums[i];
                    nums[i] = nums[nums[i]-1];
                    nums[temp-1] = temp;
                }
            }
            return i+1;
    
        }

 3. 查找旋转数组的最小值

问题描述:假定一个排序数组一某个未知元素为支点做了旋转,如原数组0,1,2,3,4,5,6,7旋转后得到了4,5,6,7,0,1,2。找出旋转后的数组的最小值。假定数组中没有重复数字。

分析思路:

  • 仍然按照循环不变式+二分查找的思路。

    • 数组由普通升序数组和旋转数组构成;
    • 用left,right分别指向首尾元素,元素不重复。
      • 若该数组是普通的升序数组,则a[left] < a[right];
      • 若该数组是旋转数组,则肯定是数组的前半段的所有元素大于后半段的所有元素,则有a[left]>a[right];
    • 令mid = (left + right) / 2.
      • 若旋转数组在左边,则a[mid] < a[right],令left = mid+1;
      • 若旋转数组在右边,则a[mid] > a[right],令right = mid;
    • /**
           * Get the minimum element of a rotate array.
           * @param nums
           * @return
           */
          public int getMiniOfRotateArray(int[] nums) {
              int left = 0;
              int right = nums.length-1;
              int mid;
              while(left < right) {
                  mid = (left + right) / 2;
                  if(nums[mid] > nums[right])
                      left = mid+1;
                  else if(nums[mid] < nums[right])
                      right = mid;
              }
              return nums[left];
          }

 4. 求一个子数组的值最接近0的问题。

问题描述:求对于长度为N的数组A,求连续子数组的和最接近0的值。如数组A:1,-2,3,10,-4,7,2,-5.它的所有子数组中,和最接近0的是[-4,7,2,-5],和为0.

分析思路:这种求子数组和的问题,一般需要有一个辅助数组,存储中间计算的过程。

用一个sum数组,第i个元素表示:该数组中从第1个元素到第i-1个元素的和,则从i到j这一段子数组的和为:sum[j]-sum[i-1]。

因为我们的数组中保存了每段子数组的和,则在所有子数组的和中差别最少的那部分就是要求的子数组的和。

也就是我们可以将所求的sun数组进行排序,然后返回相邻元素的差的绝对值最小的那个。

  • /**
         * Get the minimum sum of continues subArray which is closest to 0.
         * @param nums
         * @return
         */
        public int getClosestToZero(int[] nums) {
            int[] sum = new int[nums.length];
            sum[0] = nums[0];
            for(int i=1; i<nums.length; i++) {
                sum[i] = sum[i-1] + nums[i];
            }
            Arrays.sort(sum);
            int res = Integer.MAX_VALUE;
            int temp = 0;
            for(int i=1; i<sum.length; i++) {
                temp = Math.abs(sum[i] - sum[i-1]);
                res = Math.min(temp, res);
            }
            return res;
        }

 5. 最大子数组的和。

问题描述:给定一个数组A[0,...,n-1],求A的连续子数组,是的该子数组的和最大。

例如:数组:1,-2,3,10,-4,7,2,-5,最大子数组为:3,10,-4,7,2

思路分析:

这个题目与上面的题目不同,可以使用DP的思想来解决了。

  • 用sum[i]表示以a[i]为结尾的子数组中最大的和;
  • 因为和一定是以a[i]为结尾,所以它一定会有,关键看前面的和要不要。
  • 状态转移方程为:sum[i] = max(sum[i-1]+a[i], a[i])。
  • 时间复杂度为:O(n)。
  • /**
         * Get the max sum of subArray.
         * @param nums
         * @return
         */
        public int getMaxSumOfSubArray(int[] nums) {
            int[] sum = new int[nums.length];
            sum[0] = nums[0];
            int res = sum[0];
            for(int i=1; i<nums.length; i++) {
                sum[i] = Math.max(sum[i-1]+nums[i], nums[i]);
                res = Math.max(res, sum[i]);
            }
            return res;
        }
时间: 2024-08-10 15:12:03

数组经典题目的相关文章

十个利用矩阵乘法解决的经典题目

转载自    Matrix67: The Aha Moments 好像目前还没有这方面题目的总结.这几天连续看到四个问这类题目的人,今天在这里简单写一下.这里我们不介绍其它有关矩阵的知识,只介绍矩阵乘法和相关性质.    不要以为数学中的矩阵也是黑色屏幕上不断变化的绿色字符.在数学中,一个矩阵说穿了就是一个二维数组.一个n行m列的矩阵可以乘以一个m行p列的矩阵,得到的结果是一个n行p列的矩阵,其中的第i行第j列位置上的数等于前一个矩阵第i行上的m个数与后一个矩阵第j列上的m个数对应相乘后所有m个

动态规划经典题目:最大连续子序列和

最大连续子序列和问题 给定k个整数的序列{N1,N2,...,Nk },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j <= k.最大连续子序列是所有连续子序中元素和最大的一个,例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{11,-4,13},最大连续子序列和即为20. 注:为方便起见,如果所有整数均为负数,则最大子序列和为0. 解决这样一个问题是一个很有趣的过程,我们可以尝试着从复杂度比较高的算法

[转]十个利用矩阵乘法解决的经典题目

好像目前还没有这方面题目的总结.这几天连续看到四个问这类题目的人,今天在这里简单写一下.这里我们不介绍其它有关矩阵的知识,只介绍矩阵乘法和相关性质.    不要以为数学中的矩阵也是黑色屏幕上不断变化的绿色字符.在数学中,一个矩阵说穿了就是一个二维数组.一个n行m列的矩阵可以乘以一个m行p列的矩阵,得到的结果是一个n行p列的矩阵,其中的第i行第j列位置上的数等于前一个矩阵第i行上的m个数与后一个矩阵第j列上的m个数对应相乘后所有m个乘积的和.比如,下面的算式表示一个2行2列的矩阵乘以2行3列的矩阵

矩阵经典题目七:Warcraft III 守望者的烦恼(矩阵加速递推)

https://www.vijos.org/p/1067 很容易推出递推式f[n] = f[n-1]+f[n-2]+......+f[n-k]. 构造矩阵的方法:构造一个k*k的矩阵,其中右上角的(k-1)*(k-1)的矩阵是单位矩阵,第k行的每个数分别对应f[n-1],f[n-2],,f[n-k]的系数.然后构造一个k*1的矩阵,它的第i行代表f[i],是经过直接递推得到的.设ans[][]是第一个矩阵的n-k次幂乘上第二个矩阵,f[n]就是ans[k][1]. 注意:用__int64 #in

学习心得:《十个利用矩阵乘法解决的经典题目》from Matrix67

本文来自:http://www.matrix67.com/blog/archives/tag/poj大牛的博文学习学习 节选如下部分:矩阵乘法的两个重要性质:一,矩阵乘法不满足交换律:二,矩阵乘法满足结合律经典题目1 给定n个点,m个操作,构造O(m+n)的算法输出m个操作后各点的位置.操作有平移.缩放.翻转和旋转    这 里的操作是对所有点同时进行的.其中翻转是以坐标轴为对称轴进行翻转(两种情况),旋转则以原点为中心.如果对每个点分别进行模拟,那么m个操作总共耗时 O(mn).利用矩阵乘法可

[leetcode]53Maximum Subarray动态规划经典题目:最大子串问题

/** * Find the contiguous subarray within an array (containing at least one number) * which has the largest sum. For example, given the array [-2,1,-3,4,-1,2,1,-5,4], the contiguous subarray [4,-1,2,1] has the largest sum = 6. click to show more prac

矩阵乘法的经典题目_源自Matrix67_

嘛,都刷一遍好辣. 矩阵Am?n就是一个m行n列的数表. 考虑矩阵的乘法: C=A?B=∑aik?bkj 那么对于矩阵A的要求就是:A为m * n的矩阵 对于矩阵B的要求就是:B为n * p的矩阵 乘得的矩阵C的规模:m * p的矩阵 矩阵乘法是不满足交换律的.但它满足结合律和分配律. 经典题目1 给定n个点,m个操作,构造O(m+n)的算法输出m个操作后各点的位置.操作有平移.缩放.翻转和旋转 然后盗图 考虑实际上这个变换对应着一个类似于线性变换的东西,我们显然是可以用矩阵来搞的. 而对于翻转

矩阵经典题目八:hdu 2175 How many ways??

http://acm.hdu.edu.cn/showproblem.php?pid=2157 给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值 把给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j.令C=A*A,那么C(i,j)=ΣA(i,k)*A(k,j),实际上就等于从点i到点j恰好经过2条边的路径数(枚举k为中转点).类似地,C*A的第i行第j列就表示从i到j经过3条边的路径数.同理,如果要求经过k步的路径数,我们只需要二分求出A^k即可.

矩阵经典题目四:送给圣诞夜的礼品(使用m个置换实现对序列的转变)

https://vijos.org/p/1049 给出一个序列,含n个数.然后是m个置换,求对初始序列依次进行k次置换,求最后的序列. 先看一个置换,把置换表示成矩阵的形式,然后将m个置换乘起来.那么初始序列首先执行这个置换k/m次,然后顺次执行前k%m个置换,最后乘上初始矩阵. 最后注意矩阵乘法的顺序,A*B != B*A. #include <stdio.h> #include <iostream> #include <map> #include <set&g