数组及排序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));
}
}
都是用心的总结,望点赞,评论讨论!!