LeetCode算法(一)数组

简述:今天是2018/5/20日,距离我大学毕业剩下短短不到一年时间。最近一段时间开始刷LeetCode,希望自己可以加油学习,总结,做一个让自己满意的人。也会慢慢将自己走的一些弯路,应聘以及人际交往的心得写下来。不积跬步,无以至千里。不积小流,无以成江河。时光只是自己的过客,做自己的主人,是一切美好的开始。

LeetCode中文版

数组

目录

从排序数组中删除重复项

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例1

给定数组 nums = [1,1,2],

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。

你不需要考虑数组中超出新长度后面的元素.

示例2

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。

解决思路

这道题比较容易思考,由于数组已经排好序。那么就只需要判断元素是否与它下一个元素是否相同,若是相同则跳过这个相同元素,直到找到不同的元素,将其记录到元素的下一个位置。重复上述步骤。

解决要点:用不重复的元素覆盖掉重复的元素。(可以忽略数组中超出新长度的元素)

  1. 设置两个标记点len和i,i用来查找下一个不同的数字
public int removeDuplicates(int[] nums){
            int len = 0;
            for(int i=1;i<nums.length;i++){
                if(nums[len]!=nums[i]){
                    len++;
                    nums[len] = nums[i];
                }
            }
            return len+1;
    }

买卖股票的最佳时机II

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)

示例1

输入: [7,1,5,3,6,4] 输出: 7

解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。

随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

示例2

输入: [1,2,3,4,5]
输出: 4

解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。


注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。

因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

解决思路

我的思路: 这道题可以分为三种情况:

  1. 给定数组是增序的:MaxProfix = nums[length-1] - nums[0]
  2. 给定数组是降序的:MaxProfix = 0
  3. 给定数组是无序的:需要判断,找到数组中增序的片段,通过这些增序的片段来获取利益,再将其相加
public int MaxProfits(int[] prices){
        //递增有序数组
        int length = prices.length;
        //判断是否为空
        if(length==0){
            return 0;
        }
        if(isSortedIncrease(prices)){
            return prices[length-1]-prices[0];
        }
        //递减有序数组
        if(isSortedDecrease(prices)){
            return 0;
        }
        //无序数组
        int number = 0;
        int maxprofit=0;
        int profit = 0;
        for(int i=1;i<length;i++){
            number = i-1;
            while(prices[i-1]<prices[i]){
                //若小于,则继续判断
                i++;
                //判断是否结束
                if(i==length){
                    break;
                }
            }
            profit = prices[i-1] - prices[number];
            maxprofit += profit;
        }
        return maxprofit;

    }
    public boolean isSortedIncrease(int[] prices){
        for(int i =0;i<prices.length-1;i++){
            if(prices[i]>prices[i+1]){
                return false;
            }
        }
        return true;
    }
    public boolean isSortedDecrease(int[] prices){
        for(int i =0;i<prices.length-1;i++){
            if(prices[i]<prices[i+1]){
                return false;
            }
        }
        return true;
    }

其他思路:

旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

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

输出: [5,6,7,1,2,3,4]

解释:

向右旋转 1 步: [7,1,2,3,4,5,6]

向右旋转 2 步: [6,7,1,2,3,4,5]

向右旋转 3 步: [5,6,7,1,2,3,4]

示例2

输入: [-1,-100,3,99] 和 k = 2

输出: [3,99,-1,-100]

解释:

向右旋转 1 步: [99,-1,-100,3]

向右旋转 2 步: [3,99,-1,-100]

解决思路

  1. 每次移动一个位置,通过for循环移动所需要移动的次数。设置一个记录点,记录最后一个数。然后将前面的数依次往前移。
public int[] rotate(int nums[],int k){
        for(int i=0;i<k;i++){
            rotateOne(nums);
        }
    }
    public void rotateOne(int nums[]){
        int length = nums.length;
        if(length==0){
            return;
        }
        int number = nums[length-1];
        for(int i=length-2;i>=0;i--){
            nums[i+1] = nums[i];
        }
        nums[0] = number;
    }
  1. 通过取余运算来完成移动
public int[] rotate(int nums[],int k){
        if(nums.length==0 | nums.length==1){
            return nums;
        }
        int[] result = new int[nums.length];
        for(int i=0;i<nums.length;i++){
            result[(i+k)%nums.length] = nums[i];
        }
        return result;

存在重复

给定一个整数数组,判断是否存在重复元素。

如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。

示例1

输入:[1,2,3,4]

输出:true

示例 2:

输入: [1,2,3,4]

输出: false

示例 3:

输入: [1,1,1,3,3,4,3,2,4,2]

输出: true

解决思路

利用一个HashMap存储数组,判断是否存在重复,若存在重复,则返回true

public boolean containsDuplicate(int[] nums) {
        if(nums.length==0){
            return false;
        }
        Map<Integer,Integer> map = new HashMap<Integer, Integer>();
        for(int i=0;i<nums.length;i++){
            if(map.containsKey(nums[i])){
                return true;
            }
            map.put(nums[i],i);
        }
        return false;
    }

只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

入: [2,2,1]

输出: 1

示例 2:

输入: [4,1,2,1,2]

输出: 4

解决思路

  1. 这个问题看到之后我首先想的是使用一个HashMap,存储其元素及其出现的次数,然后遍历map,找到value为1的元素。代码如下
public int singleNumber(int[] nums) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<nums.length;i++){
            Integer value = map.get(nums[i]);
            map.put(nums[i],(value==null ? 0 : value )+1);
        }
        int key=0;
        for(int getKey : map.keySet()){
            if(map.get(getKey) == 1){
                key = getKey;
            }
        }
        return key;
    }
}
  1. 在网上搜索之后发现,还有一个非常取巧的方法,那就是对这些元素进行异或运算。由于相同的两个元素异或为0.0 ^ 0=0,0 ^ 1=1 所以不会改变最后一个单独的数。
int SingleNum = 0;
for(int num : nums){
    SinggleNum ^= num;
}

两个数组的交集II

给定两个数组,写一个方法来计算它们的交集。

例如:
给定 nums1 = [1, 2, 2, 1], nums2 = [2, 2], 返回 [2, 2].

注意

  • 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
  • 我们可以不考虑输出结果的顺序。

解决思路

将其中一个数组放入HashMap中,然后记录其元素以及元素出现的个数。遍历另一个数组,找到对应元素。map中的元素个数减1,直到找到所有的交集。

public int[] interSect(int[] nums1,int [] nums2){
        if(nums1.length==0 | nums2.length==0){
            return null;
        }
        //将数组1放入哈希map中
        List<Integer> tmp = new ArrayList<>();

        Map<Integer,Integer> map = new HashMap<Integer, Integer>();

        //将nums2放入mapfor(int i=0;i<nums2.length;i++){
            Integer Value = map.get(nums2[i]);
            map.put(nums2[i], (Value == null ? 0 : Value) + 1);
        }
        //遍历nums1
        for(int j=0;j<nums1.length;j++){
            //map中存在该数字,且map.getValue>0 则将其放入tmp中,map中的该数字计数 -1
            if(map.containsKey(nums1[j]) && map.get(nums1[j])>0){
                tmp.add(nums1[j]);
                map.put(nums1[j],map.get(nums1[j])-1);
            }
        }
        int[] result = new int[tmp.size()];
        int r=0;
        for(Integer t : tmp){
            result[r++] = t;
        }
        return result;
    }

加一

给定一个非负整数组成的非空数组,在该数的基础上加一,返回一个新的数组。

最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入: [1,2,3]

输出: [1,2,4]

解释: 输入数组表示数字 123。

示例 2:

输入: [4,3,2,1]

输出: [4,3,2,2]

解释: 输入数组表示数字 4321。

解决思路

这道题类似于数字逻辑中的加法器,设置进位 C=1 进位初始化为1,若该数字大于9则设该位位0,继续设置进位为1

public int[] plusOne(int[] digits){
        LinkedList<Integer> list = new LinkedList<>();
        int C=1;
        for(int i=0;i<digits.length;i++){
            //给每一位加上进位,从最后一位开始
            digits[digits.length-i-1] += C;
            if(digits[digits.length-i-1]>9){
                //若大于9,则设进位,且该位设为0
                C=1;
                list.addFirst(0);
                if((digits.length-i-1) ==0){
                    list.addFirst(1);
                }
            }else{
                list.addFirst(digits[digits.length-i-1]);
                C=0;
            }
        }
        int[] result = new int[list.size()];
        for(int j=0;j<list.size();j++){
            result[j] = list.get(j);
        }
        return result;
    }

移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

说明:

  • 必须在原数组上操作,不能拷贝额外的数组。
  • 尽量减少操作次数。

解决思路

如果检测到数组元素为0,则将0放到最后一位,其余位依次向前推移,这道题我完成的比较暴力。由于是第一次刷,希望有更好想法的朋友可以一起交流。

public void moveZeroes(int[]  nums){
        //思路:如果检测到数组元素为0,则将0放到最后一位,其余位依次向前推移
        int len = nums.length;
        int number = 0;
        int i = 0;
        while(len>1 && i < len-1){
            if(nums[i]==0){
                number = nums[i];
                for(int j=i;j < len-1;j++) {
                    nums[j] = nums[j + 1];
                }
                nums[len-1] = number;
                len--;
            }else{
                i++;
            }
        }
    }

两数之和

给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。

你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9

所以返回 [0, 1]

解决思路

这道题我的解决思路为将其元素放入map中,然后通过循环在该map中寻找target-nums[i]元素,若存在就返回找到的下标。

version:0.1
public int[] twosum(int[] nums,int target){
        int [] result = new int[2];
        if(nums.length==0){
            return nums;
        }
        Map<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<nums.length;i++){
            map.put(nums[i],i);
        }
        for(int i=0;i<nums.length;i++){
            if(map.containsKey(target-nums[i]) && map.get(target-nums[i])!=i){
                result[0] = i;
                result[1] = map.get(target-nums[i]);
            }
        }
        return result;
        }

想了想,其实可以将两个for循环放在一起,通过之后添加进来的元素和前面添加进来的进行搜寻,这样可以简化代码,具体如下

version:0.2 简化版
public int[] twosum(int[] nums,int target){
        int [] result = new int[2];
        if(nums.length==0){
            return nums;
        }
        Map<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<nums.length;i++){
            if(map.containsKey(target-nums[i])){
                result[0] = i;
                result[1] = map.get(target-nums[i]);
            }
            map.put(nums[i],i);
        }
        return result;
        }

如果要返回多组值的话,我又写了一个可以返回多组值得

public Map twosum(int[] nums,int target){
Map<Integer,Integer> result = new HashMap<Integer,Integer>();
        if(nums.length==0){
            return result;
        }
        Map<Integer,Integer> map = new HashMap<>();
        for(int i=0;i<nums.length;i++){
            if(map.containsKey(target-nums[i])){
                result.put(i,map.get(target-nums[i]));
            }
            map.put(nums[i],i);
        }
        return result;
        }

有效的数独

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

  • 数字 1-9 在每一行只能出现一次。
  • 数字 1-9 在每一列只能出现一次。
  • 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

解决思路

这道题解决方法,题目中其实已经给出来了。只需要我们选择相应的数据结构与解决算法即可。这里需要判断重复,所以我们选择Set集合是最好的。

public boolean isValidSudoku(char[][] board) {
                if(board == null || board.length < 9 || board[0].length < 9)
                    return false;
                Set<Character> rowset = new HashSet<Character>();
                Set<Character> colset = new HashSet<Character>();

                for(int i = 0; i < 9; i++)
                {
                    //清空
                    rowset.clear();
                    colset.clear();
                    for(int j = 0; j < 9; j ++)
                    {
                        //i=  j=   0,0 0,3 0,6  3,0 3,3 3,6 6,0 6,3 6,6
                        if((i % 3 == 0 && j%3==0)) // 检查块是否有效
                        {
                            if(!checkBlock(board, i, j))
                                return false;
                        }
                        if(board[i][j] != '.')  // 检查行是否有效
                        {
                            if(rowset.contains(board[i][j]))
                                return false;
                            rowset.add(board[i][j]);
                        }
                        if(board[j][i] != '.')  // 检查列是否有效
                        {
                            if(colset.contains(board[j][i]))
                                return false;
                            colset.add(board[j][i]);
                        }
                    }
                }
                return true;

            }

            public boolean checkBlock(char[][] board, int row, int col)  // 检查块是否有效
            {
                Set<Character> blockSet = new HashSet<Character>();
                for(int i = row; i < row + 3; i++)
                {
                    for(int j = col; j < col + 3; j++)
                    {
                        if(board[i][j] != '.')
                        {
                            if(blockSet.contains(board[i][j]))
                                return false;
                            blockSet.add(board[i][j]);
                        }
                    }
                }
                return true;
            }
        }

旋转图像

给定一个 n × n 的二维矩阵表示一个图像。

将图像顺时针旋转 90 度。

说明

你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

解决思路

经过观察不难发现,我们每次需要旋转的只有一圈中的几个元素,旋转完成即可。

旋转图像 matrix[i][j]

旋转之后的位置为matrix[j][M-i] M为n-1

 * 对于n*n的矩阵
 * 第一圈需要旋转n-1个元素matrix[0][0] -->matrix[0][n-2]
 * 第二圈需要旋转n-3个元素matrix[1][1] -->matrix[1][n-3]
 * 第三圈 n-5个元素

于是,我们可以写出相应代码

public void Imagerotate(int[][] nums){
        int M = nums.length-1;
        int len = M;
        int temp;
        int i=0;
        int j=0;
        while(len>0){
            //完成一圈的旋转,第一圈i=0 j<=len-1 第二圈 i=1 j <= len-2
            for(j= i;j<len+i;j++)
            {
                temp = nums[j][M-i];
                nums[j][M-i] = nums[i][j];
                nums[i][j] = nums[M-j][i];
                nums[M-j][i] = nums[M-i][M-j];
                nums[M-i][M-j] = temp;
            }
            //i+1
            i++;
            //len-2
            len = len-2;
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_34211771/article/details/80381118