从基础刷算法之数组篇

1.删除排序数组中的重复项

题意:有序数组,删除重复的元素,要求不能申请额外的空间。
思路:
因为数组是有序的,定义一个变量index,用来记录非重复的长度,初始化为1,遍历数组,当发现i+1和i不相同时(这里判断i和i-1也是一样的),则证明数字开始不重复了,于是将i+1放到index的位置,index+1(后移一位)。这样遍历完成后index的位置就是数组的大小了。

class Solution {
    public int removeDuplicates(int[] nums) {
        int preIndex = 1;
        for (int i = 0; i + 1 < nums.length; i++) {
            if (nums[i] != nums[i + 1] ){
                nums[preIndex++] = nums[i + 1];
            }
        }
        return preIndex;
    }
}

2.买卖股票的最佳时机 II

题意:一个数组,数组中的每个数代表当天股票的价格,不考虑多次买卖的情况下,计算最大收益。
思路:不知道算不算贪心,还是遍历判断就好了,当i+1比i大,就该买i,当i+1比i小就该卖了。开始使用两个变量想标记一个阶段的最高点和最低点做差,然后发现如果数组完全是升序或者降序的比较难写,然后转换了一下思路,当i+1比i大,就赚了i+1-i,遍历数组做这个操作就可以了。

class Solution {
    public int maxProfit(int[] prices) {
        int count = 0;
        for (int i = 0; i < prices.length - 1; i++) {
            if (prices[i + 1] > prices[i]){
                count += prices[i + 1] - prices[i];
            }
        }
        return count;
    }
}

3.旋转数组

题意:把数组想象成一个环形。每次移动就是转一下位置,输出移动N次之后的数组。
思路:
首先就想到了java里面肯定提供了api,但靠api刷题就没意思。
最简单的想法肯定是新开一个数组,然后遍历原始数组做一下下标转换就可以了。这个需要新开空间。但这种题目往往会有不新开空间的做法。

我起初的思路是,直接计算位置,然后换,但其实不能这么做,假设数组为【1234567】,要移动3次,那么我循环3次,每次都找到对应位置的数字,然后进行交换,这样把567和123进行了交换,但结果就变成了5674123,而不是5671234。

后来又想了一种,也是直接换,把1直接换到4的位置,然后把4换到7的位置,7换到3的位置,依次类推,只要换数组长度次 就可以完成了,但这种遇到成环的就有问题,比如123456 移动3次,1到4,4到1,来回换。这个时候还要在申请空间记录这个数有没有被换过,要是换过了,数组就往下遍历,想到又要开空间,这个方法瞬间就不想做了。于是去看了题解。

题解居然可以先把数组全部反转,然后对于前k做反转,然后对于k到数组结束再做一次反转就好了,这是真的没想到的。

class Solution {
    public void rotate(int[] nums, int k) {
        if (nums.length != 1){
            k = k % nums.length;
            reverse(nums, 0, nums.length - 1);
            reverse(nums, 0, k - 1);
            reverse(nums, k, nums.length - 1);
        }
    }

    public void reverse(int[] nums, int start, int end) {
        while (start < end){
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
    }
}

4.存在重复元素

题意:就是判断数组中是否有重复元素
思路:最开始还想着有没有什么位运算可以做的,但想不到,干脆想着先A了,再看题解,没有要求就很简单了,使用个map或者set存都可以,这里使用的是set,set不能存重复的,add的时候判断一下就好了。简单的很,后来看了下题解,发现位运算其实做不了这种题。还有个方法也比较简单,就是排序下,然后遍历下,判断前一个相不相等就可以了。

class Solution {
    public boolean containsDuplicate(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            if (!set.add(nums[i])){
                return true;
            }
        }
        return false;
    }
}

5.只出现一次的数字

题意:给定一个数组,找出里面只出现过一次的数字。
思路:这题就能用异或做了,利用 a ^ a = 0,0 ^ a = a的性质得到a ^ b ^ a = a ^ a ^ b = b,这样异或一遍,最终的结果就是只出现过一次的数字

6.两个数组的交集 II

题意:两个无序数组找交集
思路:先排序,用两个指针分别对两个数组做遍历,当某个值大的时候就移动另外一个指针,如果相同就记录。如果其中某一个数组的大小非常小,可以使用map,对于遍历大的数组,查看有没有在map中,map的key就是数值,value代表该数值出现的次数,如果value大于1,则记录,并且value的值要-1。

7.加一

题意:把这个数组当成是按照每位隔开的一个数字,对于这个数字+1,然后返回。
思路:从后往前遍历,如果这位不是9,那么可以+1返回了,如果是9,就要变成0,然后继续让前面的一位+1,以此类推,如果是999这种情况,那么只需要在申请一个长度比现在还大1的数组,第一位置1就行。

8.移动零

题意:一个数组,把0都移动到后面,并保持剩余的数字还是有序的,不能申请额外的空间。
思路:遍历,如果不是0就往前挪,同时记录往前挪了多少个,遍历结束后,记录挪的个数就代表非0的个数,再从这个数字开始遍历到数组结束,把这个范围内的数字都置成0,就可以了。

9.两数之和

题意:给定一个数组和一个数字target,问这个数字里面有没有2个数相加之和是给定数字的,有则输出。
思路:遍历,对于数组中的每一个数字,都使用target去做差。差值如果在map中,则证明有,map的key记录差值,value记录下标,这样就可以按照下标输出了。

10.有效的数独

题意:按照规定查看输入的数据是否是一个有效的数独。同列和同行和33的格子内不准有重复的数字。
思路:使用三个二维数组,一维代表的是行列,3
3的格子统一叫作区,二维是0-9,二维数组就代表某个行列区(数独行有9,列有9,区也有9)内是否有0-9的数字,如果没有,该下标二维数组的值就是0,有就是1。遍历输入的数据,判一下就好了。注意获取区的公式为:i / 3 * 3 + j / 3。为什么是i / 3 * 3 而不是i,是因为5 / 3 * 3 = 1而不等于5。那为什么不是i / 3?是因为这样的话,就会有重复的了。试想一下99的格子,分成9个33的格子,如果是i / 3,左上角蓝色的下面和右边的区,值就会相等,都为1,就不能做区域的区分。如果是正确的公式,下面的值为3,右边的值为1。
在这里插入图片描述

class Solution {
    public boolean isValidSudoku(char[][] board) {
        int length = board.length;
        int[][] line = new int[length][length];
        int[][] colunm = new int[length][length];
        int[][] extend = new int[length][length];

        for (int i = 0; i < length; i++) {
            for (int j = 0; j < length; j++) {
                if (board[i][j] == '.'){
                    continue;
                }
                int num = board[i][j] - '0' - 1;
                int k = i / 3 * 3 + j / 3;
                if (line[i][num] > 0 || colunm[j][num] > 0 || extend[k][num] > 0){
                    return false;
                }
                line[i][num] = colunm[j][num] = extend[k][num] = 1;
            }
        }
        return true;
    }
}

11.旋转图像

题意:给定一个二维数组,输出这个数组顺时针旋转90°后的样子,依旧不让申请空间。
思路:最初的思路是直接换,先换最外层的圈,把最外层的圈换完,再换内层的圈,一圈一圈的换下去,最终换长度/2圈换完,发现代码太难实现了,去看了题解。发现只要先上下对折反一下,然后按照对角线再反一下就好了。想想其实简单的,但写的时候又错了,原来是想对角线反一下,我全部遍历的话,岂不是换过去又换回来,于是觉得那只遍历一半就好了,但实际上遍历一半也是有问题的。对于奇数大小的数组,中间那行如果也进行转换,那就会把前1/2个数恢复成原样,因为在开始的时候已经做了一次转换了,如果对中间那行不进行转换,那么对于后1/2个数,就根本没有进行过转换,也是不对的。所以对于按照对角线转换操作,还是直接使用j = i + 1最方便。

class Solution {
    public void rotate(int[][] matrix) {
        int length = matrix.length;
        for (int i = 0; i < length / 2; i++) {
            int[] temp = matrix[i];
            matrix[i] = matrix[length - i - 1];
            matrix[length - i - 1] = temp;
        }

        for (int i = 0; i < length; i++) {
            for (int j = i + 1; j < matrix[i].length; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值