LeetCode--数组专题(1----33, 分开写,不然太长了)

一、数组简介

  数组是存储同一种数据类型(可以是基本数据类型,也可以是引用数据类型)多个元素的集合;

  数据类型补充:

       

  特点:1. 线性的结构;

     2. 数组一旦创建其长度是不可变的;

     3.  数组是引用数据类型;

  分类:一维、二维、三维、多维(二维及以上就是俄罗斯套娃下有实例);

  声明方式:

 1         // 部分基本类型
 2         int[] ints = {1, 2, 3, 4};
 3         double[] doubles = {1.0, 2.0, 3.0};
 4         
 5         // 部分引用数据类型
 6         String[] strings = {"my", "name", "is", "peanut"};
 7         Object object1 = new Object();
 8         Object object2 = new Object();
 9         Object object3 = new Object();
10         
11         Object[] objects = {object1, object2, object3};
View Code

 

二、LeetCode Array Problems:

       number:001

  解析:题目的意思简单要是数组里面有任意两个元素的和等于目标元素 (target),就返回两个数组元素下标;

     选用注意,要声明如果找不到,抛出错误信息提示;

 1 class Solution {
 2     public int[] twoSum(int[] nums, int target) {
 3         // 用 HashMap 来装下找到数组元素及其下标
 4         Map<Integer, Integer> map = new HashMap<>();
 5         
 6         // 遍历数组,将key-->nums[i], value-->i, 装进HashMap中
 7         // 其实不用全部装,可以将循环体内代码放到下一个for-loop中
 8         // for (int i = 0; i < nums.length; i++) {
 9         //     map.put(nums[i], i);
10         // }
11         
12         // 检验是否有符合条件的元素,有就返回
13         for (int i = 0; i < nums.length; i++) {
14             int result = target - nums[i];
15             map.put(nums[i], i);
16             // 要保证重复返回用一个下标的情形,例如 4+4=8,返回两次元素4的下标
17             if (map.containsKey(result) && map.get(result) != i) {
18                 return new int[] {i, map.get(result)};
19             } 
20         }
21         // 如果找不到返回错误
22         throw new IllegalArgumentException("No two sum solution");
23     }
24 }
View Code

=====================================================================

number:004

解析思路:https://www.bilibili.com/video/BV1B4411V7tP?from=search&seid=4209045980180096549 (大佬解析的很好)

 1 class Solution {
 2     public double findMedianSortedArrays(int[] nums1, int[] nums2) {
 3         int m = nums1.length;
 4         int n = nums2.length;
 5         
 6         // 有上面的思路可知,将长度小的数组放到前面,会快一点找到目标元素
 7         if (m > n) {
 8             int[] temp = nums1;
 9             nums1 = nums2;
10             nums2 = temp;
11             
12             int tmp = m;
13             m = n;
14             n = tmp;
15         }
16         
17         int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
18         while (iMin <= iMax) {
19             // 确定划分的长度
20             int i = (iMin + iMax) / 2;
21             int j = halfLen - i;
22             if (i < iMax && nums2[j - 1] > nums1[i]) {
23                  // i小了,将目标值往后面寻找
24                 iMin = i + 1;
25             } else if (i > iMin &&  nums1[i-1] > nums2[j]) {
26                  // i大了,将目标值往前面寻找
27                 iMax = i - 1;
28             } else {
29                 // 找到了合适i,需要知道的是,当i在移动时,j同样在移动,
30                 int maxLeft = 0;
31                 if (i == 0) {
32                     // 极端情况:数组1元素移动到开头后,此时 j=halfLen,则返回nums2[j-1]
33                     maxLeft = nums2[j-1];
34                 } else if (j == 0) {
35                     // 极端情况:i一直往右移,当j=0时,需要达成的条件为i=halfLen,
36                     // 此时需要明白,需要两个数组的长度一样才能达到上述条件,
37                     // 此时说明数组1中的元素均小于数组2的任意元素
38                     maxLeft = nums1[i-1];
39                 } else {
40                     // 在数组1和数组2之间选择一个作为左边最大
41                     maxLeft = Math.max(nums2[j-1], nums1[i-1]);
42                 }
43                 // 校验元素的个数是不是奇数
44                 if ((m+n) % 2 == 1) {
45                     return maxLeft;
46                 }
47                 
48                 int minRight = 0;
49                 if (i == m) {
50                     // 极端情况:数组1元素移到了最右边,说明需要选定的右半部分的最小值为nums2[j]
51                     minRight = nums2[j];
52                 } else if (j == n) {
53                     // 极端情况:因为长度小的数组放在前面,若是j=n,则说明n-m <= 1,
54                     // 此外,需要数组1的任意元素都要大于数组2的元素
55                     minRight = nums1[i];
56                 } else {
57                     // 在数组1和数组2之间选择一个作为右边最小
58                     minRight = Math.min(nums2[j], nums1[i]);
59                 }
60                 return (maxLeft + minRight) / 2.0;
61             }
62         }
63         return 0.0;
64     }
65 }
View Code

============================================================================

11. Container With Most Water    难度:Medium

解析思路:题目意思找出两条线,与x轴一起构成一个容器,使容器中的水最多。

    第一种思路就是双层for循环找出最大的容器,时间复杂度为O(n^2);

    第二种思路是由两头向中间移动,每次要么移左边,要么移右边,选出最大的,时间复杂度为O(n).

    

 1 class Solution {
 2     public int maxArea(int[] height) {
 3         
 4 //         int maxArea = 0;
 5 //         for (int i = 0; i < height.length; i++) {
 6 //             for (int j=i+1; j < height.length; j++) {
 7                    保留每次容器比较的最大值
 8 //                 maxArea = Math.max(maxArea, Math.min(height[i], height[j]) * (j - i));
 9 //             }
10 //         }
11         
12 //         return maxArea;
13         
14         
15         // 定义左右两边,以及最大值
16         int left = 0, right = height.length - 1;
17         int maxArea = 0;
18         while (left < right) {
19             // 保留每次容器比较的最大值
20             maxArea = Math.max(maxArea, Math.min(height[left], height[right]) * (right - left));
21             if (height[left] < height[right]) {
22                 left++;
23             } else {
24                 right--;
25             }
26         }
27         return maxArea;
28     }
29 }
View Code

=======================================================================================

15. 3Sum  难度:Medium

解析思路:手写,将就看吧

 1 /**
 2  * 其实此题正好衔接上面一题的思路,由两边向中间,
 3  * 不同的是需要确定一个元素作为基点;
 4  * 算法的时间复杂度为:O(n * n),最坏情况,第一个n是基点的for循环,第二个是个前一题一样
 5  */
 6 class Solution {
 7     public List<List<Integer>> threeSum(int[] nums) {
 8         // 创建接受结果的列表
 9         List<List<Integer>> result = new ArrayList<>();
10         
11         // 校验元素长度
12         if (nums.length < 3) {
13             return result;
14         }
15         // 先排序,有序数组更好操作,这一思想很重要
16         Arrays.sort(nums);
17         for (int i = 0; i < nums.length-2; i++) { // 基点的 for-loop
18             if (i > 0 && nums[i] == nums[i - 1]) { //碰到一样的就接着跳
19                 continue;
20             }
21             
22             // 其实这边可以拓展为三元素这和不仅仅为 0 的状况
23             int target = -nums[i];
24             // 定义左边元素,右边元素,寻找两元素之和==target
25             int left = i + 1;
26             int right = nums.length - 1;
27             while (left < right) {
28                 int sum = nums[left] + nums[right];
29             
30                 if (sum  == target) {  // 找到答案
31                     result.add(Arrays.asList(nums[i], nums[left], nums[right]));
32                     // 和上面一样, 遇到一样的就跳过
33                     while (left < right && nums[left] == nums[left+1]) {
34                         left++;
35                     }
36                     while (left < right && nums[right] == nums[right-1]) {
37                         right--;
38                     }
39                     // 因为答案已找到,而且同样元素也已跳过,这里直接left,right两边齐头并进
40                     left++;
41                     right--;
42                 } else if(sum < target) {  // 按情况,左元素或者右元素移动
43                     left++;
44                 } else {
45                     right--;
46                 }
47             }
48         }
49         return result;  // 返回答案
50     }
51 }
View Code

==========================================================================

16. 3Sum Closest   难度:Medium

解析思路:参照上面吧,都差不多。。。

 1 /**
 2  * 三元素求和的衍生题,但记住是离答案最近的选择,
 3  * 所以说,绝对值不能忘
 4  */
 5 class Solution {
 6     public int threeSumClosest(int[] nums, int target) {
 7         int n = nums.length;
 8         // 排序对于数组的求和算法题很重要
 9         Arrays.sort(nums);  
10         int result = nums[0] + nums[1] + nums[2];
11         for (int i = 0; i < n-2; i++) {
12             // 和前两题类似的场景
13             int left = i + 1;
14             int right = n - 1;
15             while (left < right) {
16                 int sum = nums[i] +nums[left] +nums[right];
17                 // 记得加上绝对值
18                 if (Math.abs(sum-target) < Math.abs(result-target)) {
19                     result = sum;   // 把小的存在result
20                 }
21                 if (sum == target) {
22                     return target;  //如果等于,直接返回
23                 } else if (sum < target) { 
24                     left++;
25                 } else {
26                     right--;
27                 }
28             }
29         }
30         return result;
31     }
32 }
View Code

 =========================================================================

18. 4Sum    难度:Medium

 

 

解析思路:

 

 1 /**
 2  * 经典的4元素求和问题,和前面的3元素求和不一样的是:需要定两个基点,
 3  * 一个基点开始定在最左边,一个基点在最右边,然后向中间压缩
 4  * 时间复杂度O(n^2)
 5  */
 6 class Solution {
 7     public List<List<Integer>> fourSum(int[] nums, int target) {
 8         List<List<Integer>> list = new ArrayList<List<Integer>>();
 9         
10         // **数组排序
11         Arrays.sort(nums);
12         // 如实数组长度为零,直接返回
13         if (nums.length == 0) {
14             return list;
15         }
16         // 如果数组最小值*4 大于 target,或者数组最大值*4 小于target,直接返回
17         if (4 * nums[0] > target || 4 * nums[nums.length-1] < target) {
18             return list;
19         }
20         // 定义左基点,他需要留下三个空位置,left,right,end(右基点)
21         for (int start = 0; start < nums.length -2; start++) {
22             if (start-1 >= 0 && nums[start-1] == nums[start]) { // 遇到一样的就跳过
23                 continue;
24             }
25              // 定义右基点end,他需要留下三个空位置,start,left,right
26             for (int end = nums.length-1; end > start+2; end--) {
27                 if (end+1 < nums.length-1 && nums[end+1] == nums[end]) { // 遇到一样的就跳过
28                     continue;
29                 }
30                 
31                 if (4 * nums[start] > target || 4 * nums[end] < target) { // 如果数组最小值*4 大于 target,或者数组最大值*4 小于target,直接返回
32                     break;
33                 }
34                 int left = start+1;
35                 int right = end-1;
36                 // 将收尾两端放在一起,类似于前面三元素和的单个基点
37                 int fixedSum = nums[start] + nums[end];
38                 int remSum = target - fixedSum;
39                 
40                 while (left < right) {
41                     if (2 * nums[left] > remSum || 2*nums[right] < remSum) {  // 如果数组最小值*4 大于 target,或者数组最大值*4 小于target,直接返回
42                         break;
43                     }
44                     
45                     int sum = nums[start] + nums[left] + nums[right] + nums[end];
46                     if (sum == target) {  // 终于找到你,于是去领证了
47                         list.add(Arrays.asList(nums[start], nums[left], nums[right], nums[end]));
48                     
49                     
50                         while (left+1 < right && nums[left+1] == nums[left]) { // 跳过一样的
51                             left++;
52                         }
53                         while (left < right-1 && nums[right-1] == nums[right]){  // 跳过一样的
54                             right--;
55                         }
56                         left++;
57                         right--;
58                     } else if (sum > target) { // 元素和大了,往左移
59                         right--;
60                     } else {    // 元素和小了,往右移
61                         left++;
62                     }
63                 }
64             }
65         }
66         return list;
67     }
68 }
View Code

=========================================================================

26. Remove Duplicates from Sorted Array    难度:Easy

 

解析思路:

 

 1 /**
 2  * 时间复杂度:O(n)
 3  */
 4 class Solution {
 5     public int removeDuplicates(int[] nums) {
 6         if (nums.length==0) {  // 校验数组长度
 7             return 0;
 8         }
 9         int i = 0;
10         for (int j=1; j < nums.length; j++) {
11             if (nums[j] != nums[i]) {
12                 // 一样的时候,j++,i不变,不一样时,后一个覆盖不一样的前一个,参照思路解析
13                 i++;
14                 nums[i] = nums[j];
15             }
16         }
17         // 不用纠结数组会变成什么样,返回正确答案就行
18         return i+1;
19     }
20 }
View Code

==============================================================================

27. Remove Element   难度:Easy

解析思路参照前面一题的;

 1 class Solution {
 2     public int removeElement(int[] nums, int val) {
 3         // 定义数组中含有与目标值不一样的元素个数
 4         int count = 0;
 5         int n = nums.length;
 6         for (int i = 0; i < n; i++) {
 7             if (nums[i] != val) {  // 和上一题很像,一样时i++,count不变,不一样时将覆盖前面
 8                 nums[count] = nums[i];
 9                 count++;
10             }
11         }
12         // 和上一题一样,不要在意数组变成什么样,打哪正确就好
13         return count;
14     }
15 }
View Code

 

===============================================================================

31. Next Permutation   难度:Medium

解析思路:题目比较有意思,说的是下一个数组刚好比前一个数组大一点点,例如:1, 2, 3, 4--->1, 2, 4, 3, 在下面就是1, 3, 2, 4.......当然答案是第一个,后面只是演示

 1 /**
 2  * 时间复杂度:O(n)
 3  */
 4 class Solution {
 5     public void nextPermutation(int[] nums) {
 6         // 从最后的两个数入手,所以 i=nums.length-2;
 7         int i = nums.length-2;
 8         while (i >= 0 && nums[i+1] <= nums[i]) {
 9             // 如果最后一个数大于前一个数,跳过,放在后面交换,不然i--,再往前找一位
10             i--;
11         }
12         if (i >= 0) {
13             int j = nums.length -1;
14             // 若是上面的i没有减:下面这一步的作用主要是看倒数第三个能不能和倒数第一个位置的元素交换
15             // 若是上面的i减了:下面这一步的作用主要是看倒数第二个能不能和倒数第三个位置的元素交换
16             while (j >= 0 && nums[j] <= nums[i]) {
17             //如果最后一个数小于前一个数,j--,交换两者位置
18                 j--;
19             }
20             swap(nums, i, j);
21         }
22         /**
23          * 如果最后两个交换不了,就往前推一位,若可以,将倒数第2位和倒数第3位交换,
24          * 然后将观察现在的倒数第2位和倒数第一位是否需要交换
25          */
26         reverse(nums, i+1);
27     }
28     
29     /**
30      * 交换过后,检查后面元素是否可以再次交换,保证符合题意
31      */
32     private void reverse(int[] nums, int start) {
33         int i = start, j = nums.length-1;
34         while (i < j) {
35             swap(nums, i, j);
36             i++;
37             j--;
38         }
39     }
40     
41     /**
42      * 元素交换函数
43      */
44     private void swap(int[] nums, int i, int j) {
45         int temp = nums[i];
46         nums[i] = nums[j];
47         nums[j] = temp;
48     }
49 }
View Code

==============================================================================================

 33. Search in Rotated Sorted Array   难度:Medium

解析思路:根据不同情况可分开使用二分查找

 1          int start = 0;
 2          int end = nums.length-1;
 3          
 4          while(start<=end){
 5              
 6              int mid = start+(end-start)/2;
 7 
 8              if(nums[mid] == target)
 9                  return mid;
10              
11              // Check ont the left Side
12              if(nums[start] <= nums[mid]){
13                  if(target >= nums[start] && target <= nums[mid])
14                      end = mid-1;
15                  else
16                      start =mid+1;
17              }
18              // Check ont the Right Side
19              else if(nums[mid] <= nums[end]){
20                  if(target>=nums[mid] && target<= nums[end])
21                      start = mid+1;
22                  else
23                      end =mid-1;
24              }
25                   
26          }
27          return -1;
View Code

 

posted @ 2020-05-09 20:55  巧克力可不是用来添的  阅读(242)  评论(0)    收藏  举报