数组/字符串类型

合并两个数组

题目说明:

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。
示例 3:
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

方法一:

 /*方法一*/
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int j = 0;
        int i = n;
        int temp = 0;

      //首先判断n的长度,及nums2的长度是否为0,是的话就不需要移位
       if(n!=0){
           //将原始nums1的元素向后移动n位
           for (int k = m - 1; k >= 0; k--) {
               nums1[n + k] = nums1[k];
               nums1[k] = 0;
           }
       }
       //将num1s于num2s数组的元素两两比较
        while (j < n && i < m + n) {
            if (nums2[j] < nums1[i]) {
                nums1[temp] = nums2[j];
                j++;
            } else {
                nums1[temp] = nums1[i];
                i++;
            }
            temp++;
        }
        //判断num2中是否还有元素未添加到num1s中
        if (j < n) {
            for (int k = j; k < n; k++) {
                nums1[temp] = nums2[k];
                temp++;
            }
        }


        System.out.println(Arrays.toString(nums1));

    }

方法二:

/*方法二*/
    public void merge2(int[] nums1, int m, int[] nums2, int n) {
        //将两个数组进行合并
        for (int i = 0; i != n; ++i) {
            nums1[m + i] = nums2[i];
        }
        //利用JavaAPI对数组进行自动排序
        Arrays.sort(nums1);
    }

方法三:

/*第三种方法
    使用双指针方法,将两个数组看作队列,每次从两个数组头部取出比较小的数字放到结果中
    * */
    public void merge三(int[] nums1, int m, int[] nums2, int n) {
        //将nums1,nums2看作是两个链表,用p1,p2分别指向两个链表的第一元素
        int p1 = 0, p2 = 0;
        //新建一个临时数组,用来存放排序完的元素
        int[] sorted = new int[m + n];
        //中间变量
        int cur;
        while (p1 < m || p2 < n) {
            if (p1 == m) {
                cur = nums2[p2++];
            } else if (p2 == n) {
                cur = nums1[p1++];
            } else if (nums1[p1] < nums2[p2]) {
                cur = nums1[p1++];
            } else {
                cur = nums2[p2++];
            }
            //将排序好的元素,添加到临时数组中
            sorted[p1 + p2 - 1] = cur;
        }
        //将临时数组赋值给nums1
        for (int i = 0; i != m + n; ++i) {
            nums1[i] = sorted[i];
        }
    }

方法四:

 /*方法四
    逆向双指针,指针设置为从后向前遍历,每次取两者之中较大者放进nums1的最后面
    * */
    public void merge四(int[] nums1, int m, int[] nums2, int n) {
        //将nums1,nums2看作是两个链表,用p1,p2分别指向两个链表的最后一个元素
        int p1 = m - 1, p2 = n - 1;
        //指向合nums1链表的最后一个元素
        int tail = m + n - 1;
        //中间变量
        int cur;
        while (p1 >= 0 || p2 >= 0) {
            if (p1 == -1) {
                cur = nums2[p2--];
            } else if (p2 == -1) {
                cur = nums1[p1--];
            } else if (nums1[p1] > nums2[p2]) {
                cur = nums1[p1--];
            } else {
                cur = nums2[p2--];
            }
            nums1[tail--] = cur;
        }

移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

方法一:

/*第一种方法*/
    public int removeElement(int[] nums, int val) {
        //创建一个集合,用来存放不等于val的值
      List<Integer> list=new ArrayList<>();

      //遍历数组,将不等于val的元素存放到集合中
        for (int i = 0; i < nums.length; i++) {
            if(nums[i]!=val){
                list.add(nums[i]);
            }
        }

        //将list集合中的元素赋值到sums数组中
        for (int i = 0; i < list.size(); i++) {
            nums[i]=list.get(i);
        }

        System.out.println(Arrays.toString(nums));

        return list.size();
    }

方法二:

/*第二种方法
     使用双指针
    * */
    public int removeElement2(int[] nums, int val) {
        //指向第一个元素的指针
        int left=0;
        //指向第二个元素的指针
        int right=nums.length-1;

        while (left<=right){
            if(nums[left]==val){
                nums[left]=nums[right--];
            }
            else {
                left++;
            }
        }
        System.out.println(Arrays.toString(nums));
        System.out.println(left);
        //由于left一开始的初始值是0,如果用left作为数组的长度的话,要加1
        return left++ ;
    }

方法三:

 /*解法三  通用解法
    * */
    public  int removeElement3(int []nums,int val){
        //作用计算数组长度,以及重新给数组赋值
        int i=0;
        for (int x: nums) {
            if(x!=val) nums[i++]=x;
        }
        System.out.println(Arrays.toString(nums));
        System.out.println(i++);
        return i++;
    }

删除有序数组的重复项

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
返回 k 。
判题标准:
系统会用下面的代码来测试你的题解:

int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案

int k = removeDuplicates(nums); // 调用

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
    assert nums[i] == expectedNums[i];
}
如果所有断言都通过,那么您的题解将被 通过。

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [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]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

方法一:

 /*第一种方法*/
    public int removeDuplicates(int[] nums) {
        //指向nums第二个元素
        int i=1;
        //指向list的第一个元素
        int j=0;
        List<Integer> list=new ArrayList<>();
        //将nums第一个元素添加到集合中
        list.add(nums[0]);
        //筛选不重复元素,添加到list集合中
        while (i<nums.length){
            if(list.get(j)!=nums[i]){
                list.add(nums[i]);
                j++;
            }
            i++;
        }
        //将不重复的元素,赋值到nums中
        for (int k = 0; k < list.size(); k++) {
            nums[k]=list.get(k);
        }
        System.out.println(Arrays.toString(nums));
        System.out.println(list.size());

   return  list.size();

方法二:

 /*第二种方法*/
    public int removeDuplicates2(int[] nums){
        //指向nums的第一元素
        int left=0;
        //指向nums的第二个元素
        int right=1;
        /*判断是否是空数组*/
     if(nums.length==0){
         return 1;
     }
        /*筛选非重复元素*/
        while (right<nums.length){
            if(nums[left]!=nums[right]){
                nums[++left]=nums[right++];
            }
            else {
                right++;
            }
        }
        //因为left是从0开始,数组长度从1开始,所以要加一
        left++;
        System.out.println(left);
        System.out.println(Arrays.toString(nums));
        return left;
    }

删除有序数组重复项||

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。


示例 1:

输入:nums = [1,1,1,2,2,3]
输出:5, nums = [1,1,2,2,3]
解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3。 不需要考虑数组中超出新长度后面的元素。
示例 2:

输入:nums = [0,0,1,1,1,1,2,3,3]
输出:7, nums = [0,0,1,1,2,3,3]
解释:函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3。不需要考虑数组中超出新长度后面的元素。

方法一:

public int removeDuplicates(int[] nums) {
        //指向nums的第一个元素
      int left=0;
      //指向nums的第二个元素
      int right=1;
      //设置中间变量,判断重复值是否小于等于2,如果temp等于1,证明重复值个数大于2
      int temp=0;
      //判断nums数组是否为空或者为1
      if(nums.length==0||nums.length==1){
          return nums.length;
      }

      //进行元素比较,筛选条件合适的元素
      while (right<nums.length) {
          if(nums[left]==nums[right]&&temp==0){
              nums[++left] = nums[right++];
              temp=1;
          }
          else  if (nums[left] != nums[right]  ) {
              nums[++left] = nums[right++];
              temp=0;
          }
          else {
              right++;
          }
      }
        System.out.println(left);
        System.out.println(Arrays.toString(nums));
         return ++left;

    }

方法二:

/*双指针优化*/
    public int removeDuplicates2(int[] nums){
        //指向nums元素的第三个元素
        int slow=2;
        int fast=2;
        //判断nums的长度是否小于等于2
        if(nums.length<=2){
            return nums.length;
        }
        //筛选适合的元素
        while (fast<nums.length){
            if(nums[slow-2]!=nums[fast]){
                nums[slow]=nums[fast];
                slow++;
            }

                fast++;

        }
        //因为执行到最后,slow的值会比原来的数组长度多一个,假设nums[slow]代表符合元素的最后一个元素,此时slow的值就是数组的长度,但在执行过程中
        // slow要加1,确保每个有效元素都被遍历到
        slow--;
        System.out.println(Arrays.toString(nums));
        System.out.println(slow);
        return slow;
    }

多数元素

给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 n/2 (向上取整Math.ceil())的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。



示例 1:

输入:nums = [3,2,3]
输出:3
示例 2:

输入:nums = [2,2,1,1,1,2,2]
输出:2

方法一:

 /*第一种方式*/
    public int majorityElement(int[] nums) {
        //存放结果值
        int result = 0;
        //存放数组长度一半的值
        int n = 0;
        //如果数组长度等于1,则直接返回
        if (nums.length == 1) {
            return nums[0];
        }
        //如果数组长度为奇数,nums.length/2向上取整
        if (nums.length % 2 == 1) {
            n = nums.length / 2 + 1;
        } else {
            n = nums.length / 2;
        }

        //建立一个hashMap  key存储元素,value存储元素在数组中的个数
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; i++) {
            if (!map.containsKey(nums[i])) {
                map.put(nums[i], 1);
            } else {
                map.put(nums[i], map.get(nums[i]) + 1);
                if (map.get(nums[i]) >= n) {
                    result = nums[i];
                    break;
                }
            }
        }
        System.out.println(result);
        return result;


    }

方法二:

/*第二种方式

    如果将数组nums中的的所有元素按照单调递增或单调递减的顺序排序,那么下标[n/2-1]的元素(下表从0开始)一定是众数
    * */

    public  int majorityElement2(int [] nums){
        Arrays.sort(nums);
        System.out.println(nums[nums.length / 2-1]);
        return nums[nums.length / 2-1];
    }

方法三:

 /*方法三
    摩尔投票算法
    设输入数组nums的众数为x,数组长度为n
    推论一:若记众数的票数为+1,非众数的票数为减一,则一定所有的票数合大于零
    推论二:若数组的前a个数字的票数和=0,则数组剩(n-a)个数字的票数和一定仍>0,即后(n-a)个数字的众数仍为x

    根据以上推论,记数组首个元素为n1,众数为x,遍历并统计票数,当发生票数和=0时,剩余数组的众数一定不变,这是由于:
    当n1=x:抵消的所有数字中,有一半时众数
    当n1不等于x:抵消得所有数字中,众数x的数量最少为0个,最多为一半
    利用此特性,每轮假设发生票数和=0都可以缩小剩余数组区间,当遍历完成时,最后一轮假设的数字即为众数。



    * */
    public  int majorityElement3(int [] nums){
        int x=0;
        int count=0;
        int i=0;
        while (i<nums.length){
            if(count==0){
                x=nums[i];
            }
            count=x==nums[i]?count+1:count-1;
            i++;
        }
        System.out.println(x);
        return x;

    }

轮转数组

轮转数组

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。



示例 1:

输入: nums = [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:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

方法一:

 //方法一
    public void rotate(int[] nums, int k) {

        //判断移动的长度是不是大于等于数组的长度
        if(k>=nums.length){
            //判断移动的长度时数组长度的倍数是奇数还是偶数,奇数的话,数组反转,偶数的话数组不变
            if((k/ nums.length)%2==1){
                 nums=reverse(nums);
                 //判断移动的长度是否恰好等于数组长度的倍数,不是的话移动k%nums.length位
                 if(k%nums.length!=0){
                     int n=k%nums.length;
                     nums=move(n,nums);
                 }

            }
            //偶数不反转
            else {
                if((k%nums.length!=0)){
                    //判断移动的长度是否恰好等于数组长度的倍数,不是的话移动k%nums.length位
                    int n=k%nums.length;
                    nums=move(n,nums);
                }
            }

        }
        //移动位数小于数组长度的情况
        else {
            nums=move(k,nums);
        }






    }

    // 此方法用来进行数组移动
    public  int [] move(int k,int [] nums){

        int [] tempArray=new int[k];

        for (int i = 0; i < k; i++) {
            tempArray[i]=nums[nums.length-k+i];
        }


        for (int i = nums.length-k-1; i >=0; i--) {
            nums[i+k]=nums[i];
        }


        for (int i = 0; i < k; i++) {
            nums[i]=tempArray[i];
        }
        return nums;
    }

    //此方法用来数组倒转
    public  int[] reverse(int [] nums){
       int left=0;
       int right=nums.length-1;
       int temp;
       while (left>right){
           temp=nums[left];
           nums[left]=nums[right];
           nums[right]=temp;
           left++;
           right--;
       }
       return nums;
    }

方法一优化:

    //代码优化
    public  void rotate2 (int[] nums,int k){
        int n=k%nums.length;
        move(n,nums);

    }

 // 此方法用来进行数组移动
    public  int [] move(int k,int [] nums){

        int [] tempArray=new int[k];

        for (int i = 0; i < k; i++) {
            tempArray[i]=nums[nums.length-k+i];
        }


        for (int i = nums.length-k-1; i >=0; i--) {
            nums[i+k]=nums[i];
        }


        for (int i = 0; i < k; i++) {
            nums[i]=tempArray[i];
        }
        return nums;
    }

方法二:

/*方法二*/

    public  void  rotate3(int[] nums,int k){
        int [] tempArray=new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            tempArray[(i+k)% nums.length]=nums[i];
        }

        System.arraycopy(tempArray,0,nums,0,nums.length);


    }

买卖股票的最佳时机

最大利润
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:

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

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

方法一:

/*第一种 暴力破解  超出时间限制*/
    public int maxProfit(int[] prices) {
        int max=0;
        for (int i = 0; i < prices.length; i++) {
            for (int j = i+1; j < prices.length; j++) {
             int   middle=prices[j]-prices[i];
             if(middle>max){
                 max=middle;
             }
            }
        }
        if(max<=0){
            return 0;
        }
        return max;
    }

方法二:

   /*一次遍历
    *
    * 第二种解析其实是一种动态的变化,在遍历向前推进时,找到一个最小买入价格minprice,然后,在没有找到下一个更小的买入价格时,
    * 计算接下来每一天的利润,记录其中最大利润。如果找到下一个最小买入价格minprice,继续计算接下来未找到下一个更小买入价格时的利润最大值,
    * 直到遍历完prices数组,maxProfit就是历史最大差值!
    *
    * */
    public  int maxProfit2(int []prices){
        int minPrice=Integer.MAX_VALUE;
        int maxProfit=0;
        for (int i = 0; i < prices.length; i++) {
            if(prices[i]<minPrice){
                minPrice=prices[i];
            }
            else if(prices[i]-minPrice>maxProfit){
                maxProfit=prices[i]-minPrice;
            }
        }
        return maxProfit;
    }

买卖股票的最佳时机||

买卖股票时机||
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例 1:
输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
     总利润为 4 + 3 = 7 。
示例 2:
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     总利润为 4 。
示例 3:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。

 方法一:

/*一次遍历
    获取每次最低的股票价格进行购买,然后看第二天股票是否比前一天高,是的话,股票卖出,然后获取的利润
    ,不是的话,第二天作为最低股票
    然后重复之前操作。

    这种方式,只要每天比第二天价格低,就出售,然后每次利润与之前获取的利润相叠加,就是最大利润,这种股票依次递增情况下,也就是整个股票是依次递增的,获取的利润与,
    整个股票历史天数最低,和整个股票历史天数最高的之间的差相等。因此就不必考虑历史最低股票,与历史最高股票问题。只要考虑
    有利润就出售。

    属于贪心算法
    * */
    public int maxProfit(int[] prices) {
        //表示最低股票买入
     int  minPrice=Integer.MAX_VALUE;
     //最大利润
     int  maxProfit=0;
        for (int i = 0; i < prices.length; i++) {
            //判断第二天的股票价格是否比前一天低,使得话,第二天为最低股票价格
            if(prices[i]<minPrice){
                minPrice=prices[i];
            }
            //不是的话,出售第二天股票,与之间获取的利润相叠加
            else {
                maxProfit=maxProfit+prices[i]-minPrice;
                minPrice=prices[i];
            }
        }
        return  maxProfit;
    }

方法二:

/*动态规划
    实际是获取每一天的最大利润,然后相互叠加。
    * */
    public int maxProfit2(int[] prices){
       int n =prices.length;
       int [][] dp=new int[n][2];
       dp[0][0]=0;
       dp[0][1]=-prices[0];
        for (int i = 1; i < n; i++) {
            //看第二天的股票是否比第一天,是卖出
            dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
            //看第二天的股票是否比第一天低,是买入
            dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
        }
        return  dp[n-1][0];
    }

跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

 方法一:

/*
    使用贪心算法
    每次寻找能到达右边的最大距离
    * */
    public boolean canJump(int[] nums) {
        //能到达右边的最大距离
        int rightMost=0;

        //遍历所有元素,找到每次
        for (int i = 0; i < nums.length; i++) {
            if(i<=rightMost){
                rightMost=Math.max(rightMost,i+nums[i]);
                if(rightMost>=nums.length-1){
                    return true;
                }
            }
        }
           return false;
    }

方法二:

 /*
    第二种 方法  回溯法
    将所有可能的跳跃的路径都找到,只要有一条路径可以走的通,就算成功
    * */
    public  boolean  canJump2(int [] nums){
      //开始跳跃的起始位置
       int position=0;
       //起始位置的元素值
       int step=nums[0];
       //存放能走同的路径,1代表可以走通
        List<Integer> list=new ArrayList<>();
        //该函数判读是否可以走通
        isArrive(nums,position,step,list);
       return list.contains(1);
    }

    public  void isArrive(int[] nums,int position,int step,List<Integer> list){


        //如果当前位置+跳跃的距离大于等于数组长度减一,则在集合中存入1,代表有跳跃路径可以跳到最后一个元素位置
        if(position+step>=nums.length-1){
            list.add(1);
        }
        else {
            //遍历每一元素,的每一个跳跃路径
            for (int i = 1; i <=step; i++) {
             isArrive(nums,i,nums[i],list);
            }
        }

    }

跳跃游戏二

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i] 
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

示例 2:

输入: nums = [2,3,0,1,4]
输出: 2

方法一:

public class Jump {

    // 记录最好移动次数
    int count=Integer.MAX_VALUE;

    /*
    方法一:
      递归回溯法    超出时间限制
    * */
    public int jump(int[] nums) {

        //记录每次路径所需要的移动次数
      int temp=0;
      // 判断数组是否只有一个,是的话,直接返回
      if (nums.length-1==0){
          count=temp;
          return count;
      }
      //获取最少移动次数的函数    通过不断递归和回溯
      canJump(nums,0,nums[0],temp);
      return count;
    }
             //寻找最优移动次数

    public  void    canJump(int[] nums,int position,int step,int temp){

       if(position+nums[position]>=nums.length-1){
           ++temp;
           if(temp<count){
               count=temp;

           }
       }
       else {
           for (int i = 1; i <=step; i++) {

               canJump(nums,position+i,nums[position+i],++temp);
               --temp;


           }
       }



    }

方法二:

 /*
    方法二:
    使用贪心算法
    反向查找出发位置
我们的目标是到达数组的最后一个位置,因此我们可以考虑最后一步跳跃前所在的位置,该位置通过跳跃能够到达最后一个位置。
如果有多个位置通过跳跃都能够到达最后一个位置,那么我们应该如何进行选择呢?直观上来看,我们可以「贪心」
地选择距离最后一个位置最远的那个位置,
也就是对应下标最小的那个位置。因此,我们可以从左到右遍历数组,选择第一个满足要求的位置。
找到最后一步跳跃前所在的位置之后,我们继续贪心地寻找倒数第二步跳跃前所在的位置,以此类推,直到找到数组的开始位置。
    * */
    public  int jump2(int [] nums){
        //指向最后一个元素
        int position=nums.length-1;
        //移动的次数
        int step=0;

        while (position>0){
            for (int i = 0; i < position; i++) {
                //找离position位置最远且可达到position的上一个位置  反向查找
                if(i+nums[i]>=position){
                    position=i;
                    step++;
                }
            }
        }
        return step;
    }

方法三:

/*
    方法三
    正向查找可到达的最大位置

    如果我们「贪心」地进行正向查找,每次找到可到达的最远位置,就可以在线性时间内得到最少的跳跃次数。

    * */
    public   int jump3(int [] nums) {
        //从初始位置开始
        int position = 0;
        //记录移动次数
        int step = 0;
        //记录position下一个可达的最远位置  ,说明因为最后position会>=nums.length 所以一次i永远没办法等于end,就会跳出循环,
        //直接可以省略了 判断position+nums[position]>=nums.length
        int end = 0;
          //直接省略
          /*  if (position + nums[position] >= nums.length - 1) {
                step++;
                return step;
            }*/
            for (int i = 0; i < nums.length - 1; i++) {
                position = Math.max(position, i + nums[i]);

                if (i == end) {
                    end = position;
                    step++;
                }
            }

         return step;
    }

 H指数

给你一个整数数组 citations ,其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数

根据维基百科上 h 指数的定义h 代表“高引用次数” ,一名科研人员的 h 指数 是指他(她)至少发表了 h 篇论文,并且每篇论文 至少 被引用 h 次。如果 h 有多种可能的值,h 指数 是其中最大的那个。

示例 1:

输入:citations = [3,0,6,1,5]
输出:3 
解释:给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 3, 0, 6, 1, 5 次。
     由于研究者有 3 篇论文每篇 至少 被引用了 3 次,其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3

示例 2:

输入:citations = [1,3,1]
输出:1

方法一:

 /*
    方法一:
    首先将将数据以递增的方式排序成有序
    * */
    public int hIndex(int[] citations) {
        //临时变量
        int temp=0;
        //对数组进行排序
        for (int i = 0; i < citations.length-1 ; i++) {
            for (int j = 0; j <citations.length - i-1; j++) {
                if(citations[j]>citations[j+1]){
                    temp= citations[j];
                    citations[j]=citations[j+1];
                    citations[j+1]=temp;
                }
            }
        }
        //获取数组中的最大值
        int max=citations[citations.length-1];
        //记录 数组中比max值大的个数
        int count=0;


        int h=0;
        for (int i = max; i >=0; i--) {
            //每次循环,count重新归零
            count=0;
            for (int j = 0; j < citations.length; j++) {
                if(citations[j]>=i){
                    count++;
                }
            }
            if(count>=i){
                h=i;
                return h;
            }
        }
        return h;


    }

 方法二:

 /*方法二:
    二分查找法

   H就是被引用数,一定是在0,citations.length之间
   采用二分查找法,每次去0,到citations.length的中间mid ,然后遍历数组,看有多少值大于等于mid,改值记位count
   比较count的值是否大于等于mid,如果是的话,H就是在区间的右边,然后在右边进行二分查找,不断缩小区间范围,最终找到H。
   如果不是的话,H在左边,然后在左边进行二分查找,不断缩小区间范围,最终找到H。

    * */
    public int hIndex2(int[] citations){
        //表示最左边界
     int left=0;
     //最右边界
     int right=citations.length;
     //中间值
     int min=0;
     while (left<right){
             int count=0;
             //加1防止死循环
             min=(left+right+1)/2;
         for (int i = 0; i < citations.length; i++) {
             if(citations[i]>=min){
                 count++;
             }
         }
         if(count>=min){
             left=min;
             min=(left+right)/2;
         }
         else {
             right=min-1;
             min=(right+left)/2;
         }
     }
     return left;
    }

O(1) 时间插入、删除和获取随机元素

 哈希表查找元素 时间复杂度为O(1)
实现RandomizedSet 类:
RandomizedSet() 初始化 RandomizedSet 对象
bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。
bool remove(int val) 当元素 val 存在时,从集合中移除该项,并返回 true ;否则,返回 false 。
int getRandom() 随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。
你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1) 。
示例:
输入
["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"]
[[], [1], [2], [2], [], [1], [2], []]
输出
[null, true, false, true, 2, true, false, 2]
解释
RandomizedSet randomizedSet = new RandomizedSet();
randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。
randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。
randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。
randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2
     主要利用哈希表  +可变长数组(可以List集合)
public class RandomizedSet {
    Random random ;
    Set<Integer> set;
    List<Integer> list;


    //成员变量初始化
    public RandomizedSet() {
        set = new HashSet<>();
        random=new Random();
        list=new ArrayList<>();
    }

    //元素插入操作
    public boolean insert(int val) {
        boolean flag=set.add(val);
        if(flag){
            list.add(val);
        }
        return flag ;
    }

    //元素删除操作
    public boolean remove(int val) {
        boolean flag=set.remove(val);
        if(flag){
            list.remove(Integer.valueOf(val));
        }
        return flag;
    }

    // 随机获取一个元素
    public int getRandom() {
         return   list.get( random.nextInt(list.size()));
    }

    public static void main(String[] args) {

        RandomizedSet randomizedSet=new RandomizedSet();
        randomizedSet.insert(1);
        randomizedSet.remove(2);
        randomizedSet.insert(2);
        System.out.println(randomizedSet.getRandom());
        randomizedSet.remove(1);
        randomizedSet.insert(2);
        System.out.println(randomizedSet.getRandom());

    }

}

 除自身以外数组的乘积

除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在  32 位 整数范围内。
请 不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

方法一

  /*  方法一  使用了除法,不符合题目要求*/
    public int[] productExceptSelf(int[] nums) {
        //最终要输出的结果
      int []  answer=new int[nums.length];
      //记录连乘最大值   分为count=1,和count=0的情况
      int sum=1;
      //记录数组中,有几个元素值为0
      int count=0;
      //记录数组元素中值为0的下标
      int indexZero=0;
        for (int i = 0; i < nums.length; i++) {
            if(nums[i]==0){
                count++;
                indexZero=i;
            }
        }
        //count值大于等于2的情况
        if(count>=2){
            return answer;
        }
        //count值等于1的情况
        else if(count==1){
            for (int i = 0; i < nums.length; i++) {
                if(i!=indexZero){
                   sum=sum*nums[i];
                }
                answer[indexZero]=sum;
            }
            return answer;
        }
        //count值等于0等情况
        else {
            for (int i = 0; i < nums.length; i++) {
                sum=sum*nums[i];
            }
            for (int i = 0; i < nums.length; i++) {
                answer[i]=sum/nums[i];
            }
            return answer;
        }

方法二:

/*方法二
    左右乘积列表
     将i左侧乘积的结果  放进 left[i] 中,将i右侧的结果放进 right[i]中
     当left,right  数组填充完成后
     对nums 进行遍历 answer=left[i]*right[i]
    * */
    public int[] productExceptSelf2(int[] nums) {
        int []  answer=new int[nums.length];

        //存放每个 i 的左乘积值
        int []left=new int[nums.length];
        //存放每个i的右乘积值
        int []right=new int[nums.length];

        left[0]=1;
        for (int i = 1; i < nums.length; i++) {
            left[i]=nums[i-1]*left[i-1];
        }
        right[nums.length-1]=1;
        for (int i = nums.length-2; i >=0 ; i--) {
            right[i]=right[i+1]*nums[i+1];
        }

        for (int i = 0; i < nums.length; i++) {
            answer[i]=right[i]*left[i];
        }

        return answer;
    }

方法三:

/*
  左右乘积列表优化     不需要新建左,右数组,直接把answer,和nums 当作左右数组
* */
    public int[] productExceptSelf3(int[] nums){
        int []  answer=new int[nums.length];
        //先对answer 数组全部值赋值为1,方便后面乘积  ,因为未初始化的数组元素值为0,会影响乘积结果
        for (int i = 0; i < answer.length; i++) {
            answer[i]=1;
        }
        //右乘积时,最后一个元素的右乘积的值为1
        answer[nums.length-1]=1;
        //左乘积时,第一个元素的左乘积的值为1
        answer[0]=1;
        //从左往右  进行乘积   获取每个 i的左乘积值
        for (int i = 1; i < nums.length; i++) {
            answer[i]=answer[i-1]*nums[i-1];
        }
        //从右 往左进行乘积   获取每个i 的右乘积值, 右因为answer已经是左乘积表了,所以往右乘积实际就是求 i的最终乘积
        for (int i = nums.length-2; i >=0; i--) {
             answer[i]=answer[i]*nums[i+1];
             nums[i]=nums[i]*nums[i+1];
        }
        return answer;
    }

加油站

在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
示例 1:
输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
示例 2:
输入: gas = [2,3,4], cost = [3,4,3]
输出: -1
解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。

方法一:

/*一次遍历
       总结:如果x到不了y+1(但能到y),那么从x到y的任一点出发都不可能到达y+1。因为从其中任一点出发的话,
       相当于从0开始加油,而如果从x出发到该点则不一定是从0开始加油,可能还有剩余的油。既然不从0开始都到不了y+1,
       那么从0开始就更不可能到达y+1了...

       如果从x到不了y+1,那就从y+1,开始继续遍历。
       * */
        public int canCompleteCircuit(int[] gas, int[] cost) {
            //数组的长度
            int n = gas.length;
            //从0开始遍历
            int i = 0;
            //如果i等于n 证明从哪一个加油站都绕不了一圈
            while (i < n) {
                int sumOfGas = 0, sumOfCost = 0;
                int cnt = 0;
                //能到达那个加油站,if cnt等于n 代表发现可达路径,返回i
                while (cnt < n) {
                    int j = (i + cnt) % n;
                    sumOfGas += gas[j];
                    sumOfCost += cost[j];
                    if (sumOfCost > sumOfGas) {
                        break;
                    }
                    cnt++;
                }
                if (cnt == n) {
                    return i;
                } else {
                    i = i + cnt + 1;
                }
            }
            return -1;
        }

 分发糖果

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
示例 1:
输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
示例 2:
输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
     第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。
/*
   相邻两个孩子评分高的会获得更多的糖果。
   意思就是说,每个元素必须与他们的左右之间的相邻的元素比较,
   只要满足,x元素大于左边,或者大与右边相邻元素。即x元素必定比相邻左边元素或右边元素获得的糖果数量加一
   int left[]数组 存放满足左规则元素获得的糖果数
   将这个比较方式分为从左往右遍历,比较ratings[i]>ratings[i-1] 称为左规则
   int right[] 数组存放满足右规则元素获得的糖果数
   将这个比较方式分别从右往左遍历,比较ratings[i]>ratings[i+1] 称为右规则

   每个人分到的糖果数一定是left[i]和right[i] 的最大值。

   在实际代码中,我们先计算出左规则 left 数组,在计算右规则的时候只需要用单个变量记录当前位置的右规则,同时计算答案即可


* */
    public int candy(int[] ratings) {
       int [] left=new int[ratings.length];
        for (int i = 0; i < ratings.length; i++) {
            if(i>0&&ratings[i]>ratings[i-1]){
                left[i]=left[i-1]+1;
            }else {
                left[i]=1;
            }
        }
        int right=0;
        int sum=0;
        for (int i = ratings.length-1; i >=0; i--) {
            if(i<ratings.length-1&&ratings[i]>ratings[i+1]){
                right++;
            }
            else {
                right=1;
            }
            sum=sum+Math.max(right,left[i]);
        }
        return  sum;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值