数组及排序LeetCode刷题记录
2、数组_排序
刷题总结:一般数组逃不过这些方法方法
- 双指针:一个从头遍历,一个从尾遍历
- 三指针:一个从头遍历,一个从尾遍历,一个遍历数组本身,找满足条件的进行交换
- 从后向前遍历,从后向前填充!
75、颜色分类
方法:三指针
- 为什么用多指针?
- 题目说遍历一次数组解决问题,一般都是用多指针!
三个指针各司其职:
- 第一个在左边确定 0 的位置
- 第二个在右边确定2的位置
- 第三个遍历数组:寻找 0 和 2 进行交换,把它与第一个指针或者第二个指针交换!
package 数组排序; import java.util.Arrays; /** * @author zhangbingbing * @version 1.0 * @date 2020/4/26 9:02 * 给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 * * 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 * * 注意: * 不能使用代码库中的排序函数来解决这道题。 * * 示例: * * 输入: [2,0,2,1,1,0] * 输出: [0,0,1,1,2,2] * 进阶: * * 一个直观的解决方案是使用计数排序的两趟扫描算法。 * 首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 * 你能想出一个仅使用常数空间的一趟扫描算法吗? * * 来源:力扣(LeetCode) * 链接:https://leetcode-cn.com/problems/sort-colors * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 */ public class _75_颜色分类 { /** 分析:1、原地排序、常数空间都说明:空间复杂度:O(1) 2、一趟扫描多数使用 双指针或者 三指针 本题采用三指针! */ public void sortColors(int[] nums) { int left = 0; int right = nums.length - 1; int p = 0; while (p < right) { if (nums[p] == 0) { swap(nums, left, p); left++; p++; } else if (nums[p] == 2) { swap(nums, right, p); right--; } else { p++; } } } private void swap(int[] nums, int x, int y) { int temp = nums[x]; nums[x] = nums[y]; nums[y] = temp; } public static void main(String[] args) { int[] nums = new int[]{2,0,2,1,1,0}; new _75_颜色分类().sortColors(nums); System.out.println(Arrays.toString(nums)); } }
88、合并两个有序数组
- 分析:
方法:从后向前遍历,满足条件填充
其实这一题和合并两个有序链表类似,不过这一题是 从右向左遍历,说白了就是先给大的值找到适当的位置!
因为如果找小的会被覆盖,导致值丢失,这时候换个角度岂不美哉!
- 自己写的
给你两个有序整数数组?nums1 和 nums2,请你将 nums2 合并到?nums1?中,使 nums1 成为一个有序 ? 说明: 初始化?nums1 和 nums2 的元素数量分别为?m 和 n 。 你可以假设?nums1?有足够的空间(空间大小大于或等于?m + n)来保存 nums2 中的元素。 ? 示例: 输入: nums1 = [1,2,3,0,0,0], m = 3 nums2 = [2,5,6], n = 3 输出:?[1,2,2,3,5,6] 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/merge-sorted-array 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 代码: int i = m - 1; int j = n - 1; int k = m + n - 1; while (i >= 0 && j >= 0) { if (nums1[i] >= nums2[j]) { nums1[k--] = nums1[i--]; } else { nums1[k--] = nums2[j--]; } } while (i >= 0) { nums1[k--] = nums1[i--]; } while (j >= 0) { nums1[k--] = nums2[j--]; }
- 最优解,相当于用归并排序优化!
我们要做的就是把第二个数组归并到第一个数组中,所以第二个数组必须扫描完,第一个退出直接将第二个接到后面!
- 帅气的代码!
int i = m - 1; int j = n - 1; int k = m + n - 1; while (j >= 0) { if (i >= 0 && nums1[i] >= nums2[j]) { nums1[k--] = nums1[i--]; } else { nums1[k--] = nums2[j--]; } }
面试题16.16部分排序
方法:本质上还是双指针
- 两次遍历一次从头开始,一次从末尾开始,找到 m 和 n
- 找到最大逆序对,用到了数学知识,逆序对就是 本来从小到大的,结果后面的比前面最大的小
实现:
package 数组排序; /** * @author zhangbingbing * @version 1.0 * @date 2020/4/26 10:22 * 给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。 * * 示例: * * 输入: [1,2,4,7,10,11,7,12,6,7,16,18,19] * 输出: [3,9] * * 来源:力扣(LeetCode) * 链接:https://leetcode-cn.com/problems/sub-sort-lcci * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 */ public class _面试题16_16_部分排序 { public int[] subSort(int[] array) { if (array == null || array.length == 0) { return new int[]{-1, -1}; } int n = findN(array); int m = findM(array); return new int[]{m, n}; } private int findN(int[] array) { //找最大逆序对的位置 int n = -1; int max = array[0]; for (int i = 1; i < array.length; i++) { if (array[i] >= max) { max = array[i]; } else { n = i; } } return n; } private int findM(int[] array) { //找最大逆序对的位置 int m = -1; int min = array[array.length - 1]; for (int i = array.length - 1; i >= 0; i--) { if (array[i] <= min) { min = array[i]; } else { m = i; } } return m; } }
复杂度:O(n)
可以优化成一次遍历 : 不过复杂度一样,因为for循环中执行的总代码数一样!
if (array == null || array.length == 0) { return new int[]{-1, -1}; } //优化成遍历一次 int m = -1, n = -1; int len = array.length; int max = array[0],min = array[len - 1]; for (int i = 1; i < len; i++) { if (array[i] >= max) { max = array[i]; } else { n = i; } if (array[len - i - 1] <= min) { min = array[len - i - 1]; } else { m = len - i - 1; } } return new int[]{m, n};
很强,不过要注意边界问题!要保证两边都得遍历完
977、有序数组的平方
方法:
又是双指针,所以说,数组逃不掉指针!
package 数组排序; import java.util.Arrays; /** * @author zhangbingbing * @version 1.0 * @date 2020/4/26 11:48 * 给定一个按非递减顺序排序的整数数组 A,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。 * * * * 示例 1: * * 输入:[-4,-1,0,3,10] * 输出:[0,1,9,16,100] * 示例 2: * * 输入:[-7,-3,2,3,11] * 输出:[4,9,9,49,121] * * 来源:力扣(LeetCode) * 链接:https://leetcode-cn.com/problems/squares-of-a-sorted-array * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 */ public class _977_有序数组的平法 { public int[] sortedSquares(int[] A) { /* //1、low的方法 for (int i = 0; i < A.length; i++) { A[i] = A[i]*A[i]; } Arrays.sort(A); return A; */ //1、本来的方法 int len = A.length; int[] ans = new int[len]; int r = 0, j = len - 1, cur = j; while (j > r) { ans[cur--] = (A[j]*A[j] >= A[r]*A[r]) ? (A[j]*A[j--]) : (A[r]*A[r++]); } return ans; } public static void main(String[] args) { int[] arr = new int[]{-4,-1,0,3,10}; int[] ints = new _977_有序数组的平法().sortedSquares(arr); System.out.println(Arrays.toString(ints)); } }
数组还有个喜欢考的,就是二分查找,就是要求数组必须有序
但是这一题做到了无序问题,我不知道意义何在?
package 数组排序; /** * @author zhangbingbing * @version 1.0 * @date 2020/4/27 15:31 * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 * * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 * * 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 * * 你可以假设数组中不存在重复的元素。 * * 你的算法时间复杂度必须是 O(log n) 级别。 * * 示例 1: * * 输入: nums = [4,5,6,7,0,1,2], target = 0 * 输出: 4 * 示例 2: * * 输入: nums = [4,5,6,7,0,1,2], target = 3 * 输出: -1 * * 来源:力扣(LeetCode) * 链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 */ public class _33_搜索旋转排序链表 { public int search(int[] nums, int target) { if (nums == null || nums.length == 0) return -1; int begin = 0; int end = nums.length - 1; while (begin <= end) { int mid = (begin + end) >> 1; int res = nums[mid]; if (target == res) return mid; if (nums[0] <= res) { //说明左边有序,在左边找 if (target < res && target >= nums[0]) { end = mid - 1; } else { begin = mid + 1; } } else { //右边有序 if (target > res && target <= nums[nums.length - 1]) { begin = mid + 1; } else { end = mid - 1; } } } return -1; } public static void main(String[] args) { System.out.println(new _33_搜索旋转排序链表().search(new int[]{4,5,6,7,0,1,2}, 3)); } }
560、和为K的子数组数
没用到数组的经典算法,算是简单的题
- 按照我的思路,题解写在代码上
package 数组排序; import java.util.HashMap; import java.util.Map; /** * @author zhangbingbing * @version 1.0 * @date 2020/5/15 10:19 * 给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。 * <p> * 示例 1 : * <p> * 输入:nums = [1,1,1], k = 2 * 输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。 * <p> * 来源:力扣(LeetCode) * 链接:https://leetcode-cn.com/problems/subarray-sum-equals-k * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 */ public class _560_和为K的子数组数 { //枚举算法的想法也是遍历的时候想到每一步要干嘛,在进行暴力枚举! public int subarraySum(int[] nums, int k) { if (nums == null) return 0; int count = 0; //枚举枚举肯定要遍历关键是每次遍历我们要干什么:**求出它的前面的所有子数组和** for (int i = 0; i < nums.length; i++) { int sum = 0; //明确了目的 暴力枚举就简单了 //这个循环干的就是那个目的 for (int j = i; j >= 0; j--) { sum += nums[j]; if (sum == k) { count++; } } } return count; } //优化成哈希表,难,像是动态规划的递推 public int subarraySum2(int[] nums, int k) { int count = 0, sum = 0; //我们用map来存储 Map<Integer, Integer> map = new HashMap<>(); map.put(0,1); for (int i = 0; i < nums.length; i++) { //用sum来存储前i项的和,把每一个和都存储在map中 sum += nums[i]; if (map.containsKey(sum - k)) { //如果两个和相差恰好是k,则肯定有一串连续子数组之和是这个 count += map.get(sum - k); } map.put(sum, map.getOrDefault(sum, 0) + 1); } return count; } public static void main(String[] args) { System.out.println(new _560_和为K的子数组数().subarraySum(new int[]{1, 2, 3, 4, 2, 1, 7}, 3)); } }
都是用心的总结,望点赞,评论讨论!!
相关推荐
randy0 2020-11-17
lixiaotao 2020-10-07
美丽的泡沫 2020-09-08
nongfusanquan0 2020-08-18
hang0 2020-08-16
earthhouge 2020-08-15
算法改变人生 2020-07-28
troysps 2020-07-19
Broadview 2020-07-19
chenfei0 2020-07-18
风吹夏天 2020-07-07
yangjingdong00 2020-07-05
数据与算法之美 2020-07-05
shawsun 2020-07-04
数据与算法之美 2020-07-04
要知道时间复杂度只是描述一个增长趋势,复杂度为O的排序算法执行时间不一定比复杂度为O长,因为在计算O时省略了系数、常数、低阶。实际上,在对小规模数据进行排序时,n2的值实际比 knlogn+c还要小。
Evankaka 2020-07-04
田有朋 2020-06-28