LeetCode刷题笔记-线性表

93 阅读29分钟

数组

26 Remove Duplicates from Sorted Array Easy

Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length. Do not allocate extra space for another array, you must do this in place with constant memory.

For example, Given input array nums = [1,1,2], Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn't matter what you leave beyond the new length.

笔记:JS在数组任意位置添加/移除元素 JavaScript Array splice() Method

array.splice(index,howmany,item1,.....,itemX)

参数描述
index必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
howmany必需。要删除的项目数量。如果设置为 0,则不会删除项目。
item1, ..., itemX可选。向数组添加的新项目。
/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    var index=0;
    var i=1;
    while(i<=nums.length)
    {
        if(nums[i]===nums[index]) {
            nums.splice(i,1);
        } else {
            i++;
            index++;
        }
    }
    return index+1;
};

80 Remove Duplicates from Sorted Array II Medium

Follow up for "Remove Duplicates": What if duplicates are allowed at most twice?

For example, Given sorted array nums = [1,1,1,2,2,3],

Your function should return length = 5, with the first five elements of nums being 1, 1, 2, 2 and 3. It doesn't matter what you leave beyond the new length.

For example, Given input array nums = [1,1,2], Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn't matter what you leave beyond the new length.

idea:添加一个变量记录每个数字出现的次数

/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    var index=0;    //指向当前记录的数字
    var i=1;        //遍历
    var cnt=1;      //计数
    var del=0;      //需删除的重复数
    while(i<=nums.length)
    {
        if(nums[i]===nums[index]) {
            cnt++;
            i++;
        } else {
            if(cnt>2) {
                del=cnt-2;
                nums.splice(i-del,del);
            }
            index+=cnt-del;
            i=index+1;
            cnt=1;
            del=0;
        }
    }
    return index+1;
};

33 Search in Rotated Sorted Array Hard

Suppose a sorted array is rotated at some pivot unknown to you beforehand.

(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).

You are given a target value to search. If found in the array return its index, otherwise return -1.

You may assume no duplicate exists in the array.

关键:二分查找升级版

image.png

笔记:JS语法

parseInt(7/2) 丢弃小数部分,保留整数部分

Math.ceil(7/2) 向上取整

Math.round(7/2) 四舍五入

Math.floor(7/2) 向下取整

注意:一定要有>= <=,而不是单纯>或<

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    var first = 0;
    var last = nums.length-1;
    while(first<=last)
    {
        var mid = Math.floor((first+last)/2);
        if(nums[mid]===target) {
            return mid;
        }else if(nums[first]<=nums[mid]) {
            if(target<=nums[mid] && target>=nums[first]) {
                last=mid;
            } else {
                first=mid+1;
            }
        }else {
            if(target>=nums[mid] && target<=nums[last]) {
                first=mid;
            }else {
                last=mid-1;
            }
        }
    }
    return -1;
};

4 Median of Two Sorted Arrays Hard

There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

Example 1: nums1 = [1, 3] nums2 = [2] The median is 2.0

Example 2: nums1 = [1, 2] nums2 = [3, 4] The median is (2 + 3)/2 = 2.5

思路:用归并的方法同步扫描两个数组,边比较边移动i,j并记录扫描过的数字个数,用两个临时变量保存扫描过的最后两个数以供计算中位数。

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */
var findMedianSortedArrays = function(nums1, nums2) {
    len = nums1.length + nums2.length;
    if(len%2 == 1) {
        pos = (len+1)/2;
    } else {
        pos = len/2+1;
    }

    var tmp1=-1,tmp2=-1,cnt = 0;
    var i=0, j=0;
    var find = false;
    while(i<nums1.length && j<nums2.length) {
        if(nums1[i]<=nums2[j]) {
            tmp1=tmp2;
            tmp2=nums1[i];
            i++;
            cnt++;
            if(cnt == pos) {
                find = true;
                break;
            }
        } else {
            tmp1=tmp2;
            tmp2=nums2[j];
            j++;
            cnt++;
            if(cnt == pos) {
                find = true;
                break;
            }
        }
    }
    if(!find) {
        while(i<nums1.length) {
            tmp1=tmp2;
            tmp2=nums1[i];
            i++;
            cnt++;
            if(cnt == pos) {
                find = true;
                break;
            }
        }
        while(j<nums2.length) {
            tmp1=tmp2;
            tmp2=nums2[j];
            j++;
            cnt++;
            if(cnt == pos) {
                find = true;
                break;
            }
        }
    }
    if(len%2 == 1) {
        return tmp2;
    } else {
        return (tmp1+tmp2)/2;
    }
};

通用形式:给定两个有序数组,找所有元素中第k大的元素。 递归的方案:从k入手,每次删除k/2个一定在第k大元素之前的元素。

1 Two Sum Easy

Given an array of integers, return indices of the two numbers such that they add up to a specific target. You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example: Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].

方法1:暴力方法:两层for循环遍历,复杂度O(n^2),会超时。

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    for(var i = 0; i < nums.length; i++ ) {
        for(var j=i+1; j<nums.length; j++ ) {
            if(nums[i]+nums[j] == target) {
                return [i,j]
            }
        }

    }

};

方法2:Hash。用一个哈希表存储每个数对应的下标,复杂度O(n)。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] result = new int[2];
        Map<Integer, Integer> hashMap = new HashMap<>();
        for(int i=0; i<nums.length; i++) {
            if(hashMap.containsKey(target-nums[i])) {
                result[0] = i;
                result[1] = hashMap.get(target-nums[i]);
                return result;
            } else {
                hashMap.put(nums[i],i);
            }
        }
        return null;
    }
}

15 3Sum Medium

Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero. Note: The solution set must not contain duplicate triplets.

For example, given array S = [-1, 0, 1, 2, -1, -4], A solution set is: [ [-1, 0, 1], [-1, -1, 2] ]

方法1:暴力方法:三层for循环遍历,复杂度O(n^3)。

方法2:先排序,然后左右夹逼。

复杂度:排序O(nlogn),外层循环加内层循环O(n^2),O(nlogn)+O(n^2),即O(n^2)。

推广:k-Sum 先排序,然后做k-2次循环,在最内层循环左右夹逼,时间复杂度为O(n^{k-1})

坑:

  • 最外层循环要跳过重复数字
  • 内层循环的结果不唯一,需要全部罗列出
  • 内层循环也需要跳过重复数字,但是跳过时需要注意访问比较时不能越界!
class Solution {
  public List<List<Integer>> threeSum(int[] nums) {
      List<List<Integer>> allResult = new ArrayList<>();
      List<List<Integer>> curResult = new ArrayList<>();
      Arrays.sort(nums);
      for(int i=0; i<nums.length-2; i++) {     
          if (i>0 && nums[i] == nums[i-1]) {
              continue;
          }
          curResult = twoSum(nums, i+1, nums.length-1, 0-nums[i]);
          if(curResult.size() > 0) {
              allResult.addAll(curResult);                
          }
      }
      return allResult;
  }

  public List<List<Integer>> twoSum(int[] nums, int start, int end, int target) {
      List<List<Integer>> result = new ArrayList<>();
      int l = start;
      int r = end;
      while(l < r) {
          if (nums[l] + nums[r] == target) {
              List<Integer> curResult = new ArrayList<>();
              curResult.add(0-target);
              curResult.add(nums[l]);
              curResult.add(nums[r]);
              result.add(curResult);
              while(l+1 <= end && nums[l] == nums[l+1]) {
                  l++;
              } 
              l++;
          } else if (nums[l] + nums[r] < target) {        
              while(l+1 <= end && nums[l] == nums[l+1]) {
                  l++;
              }                               
              l++;                
          } else {               
              while(r-1 >= start && nums[r] == nums[r-1]) {
                  r--;
              }                            
              r--; 
          }
      }
      return result;
  }
}

16 3Sum Closest Medium

Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.

For example, given array S = {-1 2 1 -4}, and target = 1. The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

方法1:暴力方法:三层for循环遍历,复杂度O(n^3)。

方法2:先排序,然后左右夹逼。

与上一题的主要区别:三个数之和没有一个目标数,方法是求当前三个数之和以及与target的距离,每次保留距离最小的一对。

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        int sum = nums[0] + nums[1] + nums[nums.length - 1];
        int distance = Math.abs(target - sum);
        for(int p = 0; p < nums.length-2; p++) {
            int i = p + 1;
            int j = nums.length - 1;            
            while(i < j) {
                int newSum = nums[p] + nums[i] + nums[j];
                int newDistance = Math.abs(target - newSum);
                if (newDistance < distance) {
                    sum = newSum;
                    distance = newDistance;
                }
                if (distance == 0) {
                    return sum;
                } else if (newSum < target) {
                    i++;
                } else {
                    j--;
                }                            
            }

        }
        return sum;
    }
}

27 Remove Element Easy

Given an array and a value, remove all instances of that value in-place and return the new length. Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory. The order of elements can be changed. It doesn't matter what you leave beyond the new length.

Example: Given nums = [3,2,2,3], val = 3, Your function should return length = 2, with the first two elements of nums being 2.

题意:非目标数有k个,数组前k个放着所有非目标数。 原地计算:扫描到非目标数的时候计数,并且把它放在原数组计数-1的位置上。覆盖不会出问题的原因在于下一个待放的坑位永远在扫描到的index之前。

class Solution {
    public int removeElement(int[] nums, int val) {
        int length = 0;
        for (int i = 0; i < nums.length; i++) {
            if(nums[i] != val) {
                nums[length++] = nums[i];
            }
        }
        return length;
    }
}

31 Next Permutation Medium

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers. If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order). The replacement must be in-place, do not allocate extra memory.

Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column. 1,2,3 → 1,3,2 3,2,1 → 1,2,3 1,1,5 → 1,5,1

题意:寻找下一个更大的数字排列。 如果已经是最大,就输出第一个排列,即从小到大排列。 一个例子:7,2,5,3,2,1 -> 7,3,1,2,2,5

思路:

  1. 从后向前找第一个变小的数。如果不存在,则表明该permutation已经最大,next permutation为当前序列的逆序。
  2. 如果存在,在它后面找到比它大的数中最小的一个。
  3. 这两个数字交换,然后将这个数后面的所有数从小到大排序。
class Solution {
    public void nextPermutation(int[] nums) {
        int p = -1;
        int q = -1;
        for (int i = nums.length -1; i > 0; i--) {
            if(nums[i-1] < nums[i]) {
                p = i-1;
                break;
            }
        }
        if(p == -1) {
            // 已经是最大情况,反转数组为最小情况
            int i = 0;
            int j = nums.length -1;
            while(i<j) {
                // 交换
                int tmp = nums[i];
                nums[i] = nums[j];
                nums[j] = tmp;
                i++;
                j--;
            }            
        } else {
            for(int i = p+1; i<=nums.length -1; i++) {
                int min_bigger = Integer.MAX_VALUE;
                if (nums[i] > nums[p] && nums[i] < min_bigger) {
                    q = i;
                    min_bigger = nums[i];
                }
            }
            // p q 交换
            int tmp = nums[p];
            nums[p] = nums[q];
            nums[q] = tmp;
            // p后面的从小到大排序
            Arrays.sort(nums,p+1,nums.length);
        }
    }
}

46 Permutations Medium

Given a collection of distinct numbers, return all possible permutations. For example, [1,2,3] have the following permutations:

[ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]

题意:列出全排列。

[1,2,3]全排的生成过程:

  1. 首先只有[1]
  2. 加入2:[2,1] [1,2]
  3. 在此基础上加3 [3,2,1] [2,3,1] [2,1,3] [3,1,2] [1,3,2] [1,2,3]

思路:

  1. 一个个数字加进去。
  2. 对于上一步生成的排列中的每一个,差缝法加入数字,n个数字的一个排列派生出n+1个新的排列。
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> preList = new ArrayList<>();
        preList.add(new ArrayList<>());
        List<List<Integer>> curList = new ArrayList<>();

        for (int i=0; i<nums.length; i++) {
            // 每个循环加入一个数字
            for (List list : preList) {                
                for(int index=0; index<=list.size(); index++) {
                    List<Integer> temp = new ArrayList<>(list);
                    temp.add(index, new Integer(nums[i]));
                    curList.add(temp);
                }                
            }
            preList = curList;
            curList = new ArrayList<>();
        }
        return preList;        
    }
}

递归方法:

  1. 第一个元素与每个元素交换
  2. 求后面所有元素的全排
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    public List<List<Integer>> permute(int[] nums) {        
        if(nums == null || nums.length == 0) {
            return result;
        }       
        permutation(nums, 0);
        return result;
    }
    private void permutation(int[] array, int index) {
        // 将数组array索引index往后的部分全排
        if(index == array.length) {
            List<Integer> list = new ArrayList<>(array.length);
            for(int num:array) {
                list.add(num);
            }
            result.add(list);
        } else {
            for(int i=index; i<array.length; i++) {
                // 第一个元素(索引index)与每个元素交换
                int temp = array[i];
                array[i] = array[index];
                array[index] = temp;
                // 求后面所有元素的全排
                permutation(array, index+1);
                // 将第一个元素换回来
                temp = array[i];
                array[i] = array[index];
                array[index] = temp;
            }
        }
    }
}

47 Permutations II Medium

Given a collection of numbers that might contain duplicates, return all possible unique permutations. For example, [1,1,2] have the following unique permutations:

[ [1,1,2], [1,2,1], [2,1,1] ] ]

题意:列出不重复的全排列。 思路:在上一题的基础上,在加入新的排列之前添加查重的步骤。

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {

        List<List<Integer>> result = new ArrayList<>();
        List<List<Integer>> preList = new ArrayList<>();
        preList.add(new ArrayList<>());
        List<List<Integer>> curList = new ArrayList<>();

        for (int i=0; i<nums.length; i++) {
            // 每个循环加入一个数字
            for (List list : preList) {                
                for(int index=0; index<=list.size(); index++) {
                    List<Integer> temp = new ArrayList<>(list);
                    temp.add(index, new Integer(nums[i]));
                    // 查重
                    if(!curList.contains(temp)) {
                        curList.add(temp);
                    }                    
                }                
            }
            preList = curList;
            curList = new ArrayList<>();
        }
        result = preList;

        return result;
    }
}

60 Permutation Sequence Medium

The set [1,2,3,…,n] contains a total of n! unique permutations. By listing and labeling all of the permutations in order, We get the following sequence (ie, for n = 3): "123" "132" "213" "231" "312" "321" Given n and k, return the kth permutation sequence. Note: Given n will be between 1 and 9 inclusive.

题意:n个数逐个递增排列,输出第k个排列。

思路:寻找规律,逐个计算出每个数字

image.png

class Solution {
    public String getPermutation(int n, int k) {

        String result = "";        
        List<Integer> setList = new ArrayList<>();
        for(int i=1; i<=n; i++) {
            setList.add(i);
        }

        int rest = k;
        for(int i=n-1; i>=0; i--) {
            if (rest == 0) {
                Integer num = setList.remove(setList.size()-1);
                result += num.toString();
                continue;
            }            
            int index = rest/Ann(i);
            rest = rest%Ann(i);
            if (rest == 0) {
                index--;
            }
            Integer num = setList.remove(index);
            result += num.toString();
        }     
        return result;
    }

    public int Ann(int n) {
        int fac = 1;
        for (int i = n; i>1; i--) {
            fac = fac*i;
        }
        return fac;
    }
}

42 Trapping Rain Water Hard

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining. For example, Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

image.png

思路:对于每个位置,找到它的左侧最高的柱子高度,右侧最高的柱子高度,以两个数中的较小值为基准画一条线,基准高于这个位置的高度,即可盛水,可计算。

方法:

  1. 从左往右扫描一遍,对每个位置,计算左边数中的最大值
  2. 从右往左扫描一遍,对每个位置,计算右边数中的最大值
  3. 再遍历一遍,做累加
class Solution {
    public int trap(int[] height) {
        int[] left = new int[height.length];
        int[] right = new int[height.length];
        int result = 0;

        int max = 0;
        for(int i=0; i<height.length; i++) {
            left[i] = max;
            if(height[i] > max) {
                max = height[i];
            }
        }
        max = 0;
        for(int i=height.length-1; i>0; i--) {
            right[i] = max;
            if(height[i] > max) {
                max = height[i];
            }
        }
        for(int i=0; i<height.length; i++) {
            int line = Math.min(left[i],right[i]);
            if(line > height[i]) {
                result += line - height[i];
            }

        }
        return result;
    }
}

48 Rotate Image Medium

You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees (clockwise). Note: You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.

Example 1: Given input matrix = [ [1,2,3], [4,5,6], [7,8,9] ], rotate the input matrix in-place such that it becomes: [ [7,4,1], [8,5,2], [9,6,3] ]

Example 2: Given input matrix = [ [ 5, 1, 9,11], [ 2, 4, 8,10], [13, 3, 6, 7], [15,14,12,16] ], rotate the input matrix in-place such that it becomes: [ [15,13, 2, 5], [14, 3, 4, 1], [12, 6, 8, 9], [16, 7,10,11] ]

朴素的想法:从外向内一圈圈地转,每圈的位置连成一个数列的话,每个数都是向后移n格。但这种方法实现起来其实是复杂的。

方法:应该抛去抠细节,转而整体地去看整个矩阵的变化,然后发现: 第一步,矩阵按主对角线翻转;(\) 第二步,矩阵按中线左右翻转;(|) 得到结果。

class Solution {
    public void rotate(int[][] matrix) {        
        int n = matrix.length;
        // 按主对角线翻转\
        for(int i=1; i<n; i++) {
            for(int j=0; j<i; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
        // 按中线左右翻转|
        for(int i=0; i<n; i++) {
            for(int j=0; j<n/2; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[i][n-1-j];
                matrix[i][n-1-j] = temp;
            }
        }
    }
}

66 Plus One Easy

Given a non-negative integer represented as a non-empty array of digits, plus one to the integer. You may assume the integer do not contain any leading zero, except the number 0 itself. The digits are stored such that the most significant digit is at the head of the list. Example : Given input = [1,2,3], output = [1,2,4].

题意:一个数组,里面的数字分别是一个非负整数的每一位,把这个数加一,还用数组表示。

思路:

分为三种情况:

123:直接末位加1

199:变为0,变为0,直到找到一个小于9的数,加1

999:变为0,变为0……找到头也没有小于9的数,所有数往后移一位,最前面空出一位放1

class Solution {
    public int[] plusOne(int[] digits) {
        int n = digits.length;
        if(digits[n-1] < 9) {
            digits[n-1]++;
            return digits;
        } else {
            digits[n-1] = 0;            
            int i = n-2;
            while(i>=0) {
                if(digits[i] < 9) {
                    digits[i]++;
                    return digits;
                } else {
                    digits[i] = 0;
                    i--;
                }
            }
            int[] newDigits = new int[n+1];
            for(int j=0; j<n; j++) {
                newDigits[j+1] = digits[j];
            }
            newDigits[0] = 1;
            return newDigits;
        }
    }
}

70 Climbing Stairs Easy

You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? Note: Given n will be a positive integer. Example 1: Input: 2 Output: 2 Explanation: There are two ways to climb to the top.

  1. 1 step + 1 step
  2. 2 steps Example 2: Input: 3 Output: 3 Explanation: There are three ways to climb to the top.
  3. 1 step + 1 step + 1 step
  4. 1 step + 2 steps
  5. 2 steps + 1 step

我的思路:从包括0个2,1个2,计算到n/2个2,用排列组合计算。

(Runtime Error:因为排列组合的计算中包括很大的阶乘,这时整数溢出)

class Solution {
    public int climbStairs(int n) {
        int wayCount = 1;
        for(int i=1; i<=n/2; i++) {
            // i个2
            int ones = n-2*i;
            wayCount += C(ones+i, i);            
        }
        return wayCount;
    }
    public int C(int m, int n) {
        return A(m,n)/factorial(n);
    }
    public int A(int m, int n) {
        return factorial(m)/factorial(m-n);
    }
    public int factorial(int n) {
        int fac = 1;
        for(int i=n; i>1; i--) {
            fac *= i;
        }
        return fac;
    }
}

更好的方法:递归!(但是Time Limit Exceeded) 为了爬到第n阶楼梯,有两种选择:

  1. 从第n-1阶前进1步
  2. 从第n-2阶前进2步
class Solution {
    public int climbStairs(int n) {
        if(n == 1) {
            return 1;
        } else if(n == 2) {
            return 2;
        } else {
            return climbStairs(n-1) + climbStairs(n-2);
        }
    }
}

递归会超时,所以我们要用迭代来实现递归:

class Solution {
    public int climbStairs(int n) {
        int n_2 = 1;
        int n_1 = 2;
        if(n == 1) {
            return n_2;
        }
        if(n == 2) {
            return n_1;
        }
        for(int i=3; i<=n; i++) {
            int temp = n_1;
            n_1 = n_1 + n_2;
            n_2 = temp;
        }
        return n_1;
    }    
}

134 Gas Station Medium

There are N gas stations along a circular route, where the amount of gas at station i is gas[i]. You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations. Return the starting gas station's index if you can travel around the circuit once, otherwise return -1. Note: The solution is guaranteed to be unique.

有两个属性:

  1. 如果总的 gas-cost 小于零,肯定没有解,返回-1
  2. 如果前面所有的 gas-cost 加起来小于零,那么前面所有的点都不能作为出发点
  3. 如果有解,答案唯一

思路:当total>=0时,有解,那么出现负值的后一个点就是答案。

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n = gas.length;
        int sum = 0, total = 0;
        int pos = -1;
        for(int i=0; i<n; i++) {
            total += gas[i]-cost[i];
            sum += gas[i]-cost[i];
            if(sum < 0) {
                pos = i;
                sum = 0;
            }
        }
        if(total >= 0) {
            return (pos+1)%n;
        } else {
            return -1;
        }        
    }
}

135 Candy Hard

There are N children standing in a line. Each child is assigned a rating value. You are giving candies to these children subjected to the following requirements:

  • Each child must have at least one candy.
  • Children with a higher rating get more candies than their neighbors. What is the minimum candies you must give?

思路:从左向右遍历一遍,再从右向左遍历一遍。

从左向右:右边的和左边的比

从右向左:左边的和右边的比 原则是和前面已经确定的比

只需要左右来回一趟就能得出结果。

一开始的思路是:

从左向右:左边的和右边的比

从右向左:右边的和左边的比

这样只左右来回一趟,因为被拿来比较的对象后面有可能会改变,这样比就是会有问题的。

class Solution {
    public int candy(int[] ratings) {        
        int n = ratings.length;
        int[] result = new int[n];
        int sum = 0;
        Arrays.fill(result,1);        

        for(int i=1; i<n; i++) {
            if(ratings[i]>ratings[i-1] && result[i]<=result[i-1]) {
                result[i] = result[i-1]+1;                
            }
        }
        for(int i=n-2; i>=0; i--) {
            if(ratings[i]>ratings[i+1] && result[i]<=result[i+1]) {
                result[i] = result[i+1]+1;                
            }
        }

        for(int num:result) {
            sum += num;
        }
        return sum;
    }
}

163 Single Number Easy

Given an array of integers, every element appears twice except for one. Find that single one. Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

题意:在一个数组中除一个数字只出现一次之外,其它数字都出现了两次。找出那个只出现一次的数字。

朴素想法:依次对每一个元素尝试查找是否有重,如果没有重,就返回。O(n2)

限制:线性时间 O(n) 只能扫描一遍 不能开辟额外空间

关键:异或运算(两个值不同,异或结果为真。反之,为假。)

  1. 异或运算可交换,即 a ^ b = b ^ a
  2. 特点:0 ^ a = a 对所有元素做异或运算,结果就是那个出现一次的元素!

理解:a1 ^ a2 ^ ....,可以将所有相同元素交换至相邻位置,首先运算相同元素,得0。0可去掉,因为0 ^ a = a。最后剩余一个单一元素,得解。

class Solution {
    public int singleNumber(int[] nums) {
        int result = 0;
        for(int num:nums) {
            result ^= num;
        }
        return result;
    }
}

137 Single Number II Medium

Given a non-empty array of integers, every element appears three times except for one, which appears exactly once. Find that single one.

Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Example 1: Input: [2,2,3,2] Output: 3

Example 2: Input: [0,1,0,1,0,1,99] Output: 99

题意:在一个数组中除一个数字只出现一次之外,其它数字都出现了三次。找出那个只出现一次的数字。

思路:如果把所有出现三次的数字的二进制表示的每一位都分别加起来,每一位的和都能被3整除。那么把所有数字的二进制表示的每一位都加起来,如果某一位的和能被3整除,那么只出现一次的数字二进制表示中对应的那一位是0,否则是1。

涉及位操作的测试数据:-1,2147483647

class Solution {
    public int singleNumber(int[] nums) {
        int[] bits = new int[32];
        for(int i=0; i<nums.length; i++) {
            int num = nums[i];
            int j=0;
            while(num!=0) {
                if((num&1) == 1) {
                    bits[j]++;
                }
                num >>>= 1;
                j++;
            }
        }
        int result = 0;
        for(int i=bits.length-1; i>=0; i--) {
            result <<= 1;
            if(bits[i]%3 != 0) {
                result |= 1;
            }            
        }
        return result;
    }
}

260 Single Number III Medium

Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.

Example: Input: [1,2,1,3,2,5] Output: [3,5]

Note: The order of the result is not important. So in the above example, [5, 3] is also correct. Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?

题意:在一个数组中除2个数字只出现一次之外,其它数字都出现了两次。找出那2个只出现一次的数字。

思路:想要把原数组分成两个子数组,每个子数组包含一个只出现一次的数字,其它数字都成对出现。

方法:先异或所有数字,得到的结果是两个只出现一次的数字的异或结果。找到其中一个为1的位,以这一位是0还是1将所有数字分为两个子数组。将这两个子数组分别异或所有数,得到的就是要求的两个数。

class Solution {
    public int[] singleNumber(int[] nums) {
        if(nums == null | nums.length <2) {
            return null;
        }
        int[] result = new int[2];

        int xorResult = 0;
        for(int num:nums) {
            xorResult ^= num;
        }
        int loc = 0;
        while(xorResult!=0) {
            if((xorResult&1)==1) {
                break;
            } else {
                xorResult >>>= 1;
                loc++;
            }
        }

        for(int num:nums) {
            int temp = num;
            temp >>>= loc;
            if((temp&1)==1) {
                result[0] ^= num;
            } else {
                result[1] ^= num;
            }
        }
        return result;
    }
}

单链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
// 第一步,添加头结点
ListNode dummy = new ListNode(-1);
dummy.next = head;
// 返回值需注意
return dummy.next;

2 Add Two Numbers Medium

You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. You may assume the two numbers do not contain any leading zero, except the number 0 itself.

Example

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)

Output: 7 -> 0 -> 8

Explanation: 342 + 465 = 807.

既然是倒序,倒正好符合计算时的低位对齐,对应位相加,记录进位1,在下一次相加时加入。

考虑两个数位数有长短的情况,以及相同位数的两个数加完还有进位1的情况。如:9+9 = 18

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode l3 = null;
        // 设置指针用于遍历
        ListNode p = l1;
        ListNode q = l2;
        ListNode r = l3;
        int res = 0;
        int carry = 0;  // 进位
        while(p!=null && q!=null) {
            res = p.val+q.val+carry;
            if(res>9) {
                res = res%10;
                carry = 1;
            }else{
                carry = 0;
            }
            if(l3 == null) {
                l3 = new ListNode(res);    
                r = l3;
            } else {
                ListNode node = new ListNode(res);            
                r.next = node; 
                r = r.next;
            }                        
            p = p.next;
            q = q.next;                 
        }
        if(p!=null) {
            while(p!=null) {
                res = p.val+carry;
                if(res>9) {
                    res = res%10;
                    carry = 1;
                }else{
                    carry = 0;
                }
                ListNode node = new ListNode(res);            
                r.next = node; 
                r = r.next;
                p = p.next;  
            }                        

        }else if(q!=null) {
            while(q!=null) {
                res = q.val+carry;
                if(res>9) {
                    res = res%10;
                    carry = 1;
                }else{
                    carry = 0;
                }
                ListNode node = new ListNode(res);            
                r.next = node; 
                r = r.next;
                q = q.next;     
            }
        }
        // 处理全部加完仍有进位的情况
        if(carry==1) {
            ListNode node = new ListNode(1);            
            r.next = node;                 
        }
        return l3;
    }
}

445 Add Two Numbers II Medium

You are given two non-empty linked lists representing two non-negative integers. The most significant digit comes first and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. You may assume the two numbers do not contain any leading zero, except the number 0 itself.

Follow up: What if you cannot modify the input lists? In other words, reversing the lists is not allowed.

Example:

Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)

Output: 7 -> 8 -> 0 -> 7

思路:扫描链表时将数字压入栈,利用栈后进先出的特点把数字变成倒序,然后从栈中取数字开始计算求和,其余同上题Add Two Numbers。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode result = null;
        Stack<Integer> s1 = getReverseDigits(l1);
        Stack<Integer> s2 = getReverseDigits(l2);
        int carry = 0;
        while(!s1.empty() && !s2.empty()) {
            int res = s1.pop() + s2.pop() + carry;
            if(res>9) {
                res = res%10;
                carry = 1;
            } else {
                carry = 0;
            }
            ListNode node = new ListNode(res);
            node.next = result;
            result = node;
        }
        if(!s1.empty()) {
            while(!s1.empty()) {
                int res = s1.pop() + carry;
                if(res>9) {
                    res = res%10;
                    carry = 1;
                } else {
                    carry = 0;
                }
                ListNode node = new ListNode(res);
                node.next = result;
                result = node;
            }            
        } else if(!s2.empty()) {
            while(!s2.empty()) {
                int res = s2.pop() + carry;
                if(res>9) {
                    res = res%10;
                    carry = 1;
                } else {
                    carry = 0;
                }
                ListNode node = new ListNode(res);
                node.next = result;
                result = node;
            }            
        }
        if(carry == 1) {
            ListNode node = new ListNode(1);
            node.next = result;
            result = node;
        }
        return result;
    }

    public Stack<Integer> getReverseDigits(ListNode list) {
        ListNode p = list;
        Stack<Integer> s = new Stack<>();
        while(p!=null) {
            int digit = p.val;
            s.push(digit);
            p = p.next;
        }
        return s;
    }
}

234 Palindrome Linked List Easy

Given a singly linked list, determine if it is a palindrome. Follow up: Could you do it in O(n) time and O(1) space?

Palindrome:回文

题意:判断一个链表是否为回文链表

思路:

  1. 遍历一遍链表,用一个计数器count记录总节点个数
  2. 移动一个指针half到链表的中点(5个节点时half指向第3个,4个节点时half指向第2个)
  3. 将half节点后面的所有节点原地倒置(头插法)
  4. 从前半段(head开始)和后半段(half.next开始)同步扫描,判断节点的值是否对应相同
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null) {
            return true;
        }
        int count = 0;
        ListNode p = head;
        while(p!=null) {
            count++;
            p = p.next;
        }

        int middle = count/2 + count%2;
        ListNode half = head;
        middle--;
        while(middle>0) {
            half = half.next;
            middle--;
        }
        ListNode right = reverseList(half.next);
        ListNode left = head;
        while(right!=null) {
            if(left.val != right.val) {
                return false;
            }
            left = left.next;
            right = right.next;
        }
        return true;
    }
    public ListNode reverseList(ListNode head) {        
        if(head == null || head.next == null) {
            return head;
        }
        ListNode pre = head;
        ListNode p = pre.next;
        while(p!=null) {
            pre.next = p.next;
            p.next = head;
            head = p;
            p = pre.next;            
        }
        return head;
    }
}

92 Reverse Linked List II Medium

Reverse a linked list from position m to n. Do it in-place and in one-pass.

For example: Given 1->2->3->4->5->NULL, m = 2 and n = 4, return 1->4->3->2->5->NULL.

Note: Given m, n satisfy the following condition: 1 ≤ m ≤ n ≤ length of list.

题意:将链表中的指定片段原地倒置

与整个链表倒置的区别在于:

  1. 首先需要定位开始节点
  2. 结束条件不是遍历到最后一个节点,而是用计数控制

注意:从第一个节点开始需要倒置时要做特殊处理

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */

class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        if(head.next == null) {
            return head;
        }
        if(m == 1) {
            // 特殊情况
            return reverseListOfCount(head,n-m);
        }
        ListNode loc = head;
        int count = 1;
        while(count<m-1) {
            loc = loc.next;
            count++;
        }        
        loc.next = reverseListOfCount(loc.next,n-m);        
        return head;        
    }
    public ListNode reverseListOfCount(ListNode head, int count) { 
        if(head == null || head.next == null) {
            return head;
        }
        ListNode pre = head;
        ListNode p = pre.next;
        while(count > 0) {
            pre.next = p.next;
            p.next = head;
            head = p;
            p = pre.next;   
            count--;
        }
        return head;
    }
}

86 Partition List Medium

Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x. You should preserve the original relative order of the nodes in each of the two partitions.

For example, Given 1->4->3->2->5->2 and x = 3, return 1->2->2->4->3->5.

题意:小于x的数|大于或等于x的数(两部分内部顺序不变)

思路:new两个新链表,small用来链接所有小于x的节点,big用来链接所有大于等于x的节点。 遍历整个链表时,当node的val小于x时,接在小链表上,反之,接在大链表上,这样保证了相对顺序没有改变,而仅仅对链表做了与x的比较判断。 最后还要把小链表接在大链表上,大链表的结尾赋成null。

需要考虑的特殊情况:

  1. 给的链表为空
  2. small为空
  3. big为空
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode partition(ListNode head, int x) {
        ListNode l = head;
        ListNode small = null;
        ListNode big = null;
        ListNode p = small;
        ListNode q = big;
        while(l!=null) {
            if(l.val<x) {
                if(small == null) {
                    small = l;
                    p = small;
                } else {
                    p.next = l;
                    p = p.next;
                }                
            } else {
                if(big == null) {
                    big = l;
                    q = big;
                } else {
                    q.next = l;
                    q = q.next;
                }
            }
            l = l.next;
        }
        if(p!=null) {
            p.next = big;
        }
        if(q!=null) {
            q.next = null;
        }
        if(small!=null) {
            return small;   
        } else {
            return big;
        }
    }
}

83 Remove Duplicates from Sorted List Easy

Given a sorted linked list, delete all duplicates such that each element appear only once.

Example 1:

Input: 1->1->2

Output: 1->2

Example 2:

Input: 1->1->2->3->3

Output: 1->2->3

题意:删除重复元素的副本

思路:两个指针(前驱和当前)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy.next, p = pre.next;
        while(p!=null) {
            if(pre.val == p.val) {
                pre.next = p.next;
                p = p.next;
            } else {
                p = p.next;
                pre = pre.next;
            }
        }
        return dummy.next;
    }
}

82 Remove Duplicates from Sorted List II Medium

Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.

Example 1:

Input: 1->2->3->3->4->4->5

Output: 1->2->5

Example 2:

Input: 1->1->1->2->3

Output: 2->3

题意:删除所有有重复的元素

思路:三个指针(前驱和当前和后驱)

考虑特殊情况:末尾几个结点是重复的(post没有找到和cur不一样的就走到了null),从cur到post都是重复的结点,所以要把prev.next置为null作为链表的结束。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null) {
            return head;
        }
        ListNode dummy = new ListNode(-1);
        dummy.next = head;

        ListNode prev = dummy, cur = prev.next, post = cur.next;        
        while(post != null) {
            if(cur.val == post.val) {                
                while(post != null && cur.val == post.val) {
                    post = post.next;
                }
                if(post == null) {
                    prev.next = null;
                    return dummy.next;
                }
                prev.next = post;
                cur = post;
                post = cur.next;

            } else {
                post = post.next;
                cur = cur.next;
                prev = prev.next;
            }
        }
        return dummy.next;
    }
}

61 Rotate List Medium

Given a list, rotate the list to the right by k places, where k is non-negative.

Example: Given 1->2->3->4->5->NULL and k = 2, return 4->5->1->2->3->NULL.

题意:链表向右移k位

思路:

n个节点的链表,移n位就是它本身,所以先用%将k缩小至0~k-1的范围,且其中,k=0时链表不变。

然后,链表的后k个元素整体搬到链表头。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if(head==null || head.next==null) {
            return head;
        }
        ListNode first = head;
        ListNode p = head;
        int n = 0;
        while(p!=null) {
            n++;
            p = p.next;
        }
        k = k%n;
        if(k==0) {
            return head;
        }
        int m = n-k;
        p = head;
        int count = 0;
        while(p!=null) {
            count++;
            if(count == m) {                
                head = p.next;
                p.next = null;
                p = head;
                continue;
            }
            if(count == n) {
                p.next = first;
            }
            p=p.next;
        }
        return head;
    }
}

19 Remove Nth Node From End of List Medium

Given a linked list, remove the nth node from the end of list and return its head.

For example, Given linked list: 1->2->3->4->5, and n = 2. After removing the second node from the end, the linked list becomes 1->2->3->5.

Note: Given n will always be valid. Try to do this in one pass.

题意:删除倒数第n个结点

思路:设置两个指针p,q,让p先走n步,然后p和q一起走,直到p走到尾节点,p指向的就是要删除节点的前一个。

注意:要删除的节点是第一个结点时需要特殊处理一下。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head == null) {
            return head;
        }
        ListNode p = head;
        while(n>0) {
            p = p.next;
            n--;
        }
        ListNode q = head;
        if(p == null) {
            /*
            **p已经移动到了最后
            **说明要删除的节点就是第一个结点
            */
            head = q.next;
            return head;
        }
        while(p.next!=null) {
            p = p.next;
            q = q.next;
        }
        q.next = q.next.next;
        return head;         
    }
}

138 Copy List with Random Pointer Medium

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null. Return a deep copy of the list.

题意:复杂链表的深度复制(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点)

注意:最后一步,剥离出复制结点时,不能只剥离复制结点,原结点也要一同剥离,否则破坏了原链表,会报错。

/**
 * Definition for singly-linked list with a random pointer.
 * class RandomListNode {
 *     int label;
 *     RandomListNode next, random;
 *     RandomListNode(int x) { this.label = x; }
 * };
 */
public class Solution {
    public RandomListNode copyRandomList(RandomListNode head) {
        if(head == null) {
            return head;
        }
        // 复制每一个节点,放在每一个原结点后面
        RandomListNode p = head;
        while(p!=null) {
            RandomListNode copy = new RandomListNode(p.label);
            copy.next = p.next;
            p.next = copy;
            p = p.next.next;
        }
        // 为每一个复制节点添加random
        p = head;
        while(p!=null) {
            if(p.random!=null) {
                p.next.random = p.random.next;
            } else {
                p.next.random = null;
            }            
            p = p.next.next;
        }
        // 将所有复制节点剥离出来
        RandomListNode pOrigin = head;
        RandomListNode pCopy = head.next;
        p = pOrigin;
        RandomListNode q = pCopy;
        while(p!=null) {
            p.next = p.next.next;
            p = p.next;
            if(q.next != null) {
                q.next = q.next.next;
                q = q.next;
            }
        }
        return pCopy;
    }
}

141 Linked List Cycle Easy

Given a linked list, determine if it has a cycle in it.

Follow up: Can you solve it without using extra space?

题意:判断一个链表是否包含环

思路:快慢指针,一个每次走一步,一个每次走两步。如果两个指针相遇,说明有环;如果快指针走到链表末尾(指向null),也没有相遇,说明没有环。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null || head.next == null) {
            return false;
        }
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode lower = dummy, faster = dummy;
        while(faster.next != null && faster.next.next != null) {
            lower = lower.next;
            faster = faster.next.next;
            if(lower == faster) {
                return true;
            }
        }
        return false;
    }
}

142 Linked List Cycle II Medium

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Note: Do not modify the linked list.

Follow up: Can you solve it without using extra space?

题意:找到链表中环的入口节点,如果没有环,返回null。

  1. 判断是否有环(快慢指针)
  2. 计算环的节点数n(快慢指针一定在环内相遇)
  3. 定位环的入口(前后指针,第一个先走n步,两个指针一定在入口处相遇)
/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head == null || head.next == null) {
            return null;
        }
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode lower = dummy, faster = dummy;    
        boolean isCircled = false;
        while(faster.next != null && faster.next.next != null) {
            lower = lower.next;
            faster = faster.next.next;
            if(lower == faster) {   // 快慢指针相遇
                isCircled = true;
                break;
            }
        }
        if(!isCircled) {
            return null;
        }

        // 计算环的大小n
        int circle = 0;
        ListNode temp = lower;
        do {
            lower = lower.next;            
            circle++;
        } while(lower!=temp);

        ListNode first = dummy, second = dummy;
        // 第一个指针先走n步
        for(int i=0; i<circle; i++) {
            first = first.next;
        }
        // 两个指针一起走,相遇的地方就是环的入口处
        while(first != second) {
            first = first.next;
            second = second.next;
        }
        return first;
    }
}

来自《程序员面试金典》的更简便的方法:

  1. 判断是否有环(快慢指针)

  2. 慢指针回到开头,快指针不动

  3. 快慢指针同步走,每次1步,两个指针一定在入口处相遇

/**  
* Definition for singly-linked list.  
* class ListNode {  
* int val;  
* ListNode next;  
* ListNode(int x) {  
* val = x;  
* next = null;  
* }  
* }  
*/  
public class Solution {  
    public ListNode detectCycle(ListNode head) {  
        if (head == null || head.next == null) {  
            return null;  
        }  
        ListNode dummy = new ListNode(-1);  
        dummy.next = head;  
        ListNode lower = dummy, faster = dummy;  
        boolean isCircled = false;  
        while (faster.next != null && faster.next.next != null) {  
            lower = lower.next;  
            faster = faster.next.next;  
            if (lower == faster) { // 快慢指针相遇  
                isCircled = true;  
                break;  
            }  
        }  
        if (!isCircled) {  
            return null;  
        }  

        ListNode first = dummy, second = faster;  
        // 两个指针一起走,相遇的地方就是环的入口处  
        while (first != second) {  
            first = first.next;  
            second = second.next;  
        }  
        return first;  
    }  
}