- 删除重复出现元素
- 合并两个有序数组
- 整体移动数组
- 查找主要元素
- 三数之和
- 最大子序和
- 最长连续序列
- 加1
- 数组全排列
1.删除重复出现元素
题目:
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。
要求:
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
思路:
双指针法
当前指针指向第一个位置
比较指针指向第二个位置
如果arr[cur] == arr[compare]->compare下移
如果arr[cur]!= arr[compare]->arr[compare]与arr[cur]下一位置作交换
public static int removeDuplicates(int[] nums) {
//判空
if (nums == null) {
return 0;
}
//当前指针
int cur = 0;
//比对指针
int compare = 1;
//记录数组长度
int length = 1;
while (compare < nums.length){
if (nums[cur] == nums[compare]){
//如果两个数相等 -> compare指针下移
compare++;
}else{
//如果两个数不相等 -> nums[cur + 1] 与 nums[compare]交换
swap(nums,++cur,compare++);
length++;
}
}
return length;
}
public static void swap(int[]nums,int x,int y){
int tmp = nums[x];
nums[x] = nums[y];
nums[y] = tmp;
}
2.合并两个有序数组
题目:
你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
要求:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。
思路1:
类似于merge排序外排方法
用额外的help[]数组帮助完成
public void merge(int[] nums1, int m, int[] nums2, int n) {
if (nums2 == null){
return;
}
int[] help = new int[m + n];
int cur1 = 0;
int cur2 = 0;
int index = 0;
while (cur1 < m && cur2 < nums2.length){
help[index++] = nums1[cur1] < nums2[cur2] ? nums1[cur1++] : nums2[cur2++];
}
while (cur1 < m){
help[index++] = nums1[cur1++];
}
while (cur2 < nums2.length){
help[index++] = nums2[cur2++];
}
System.arraycopy(help, 0, nums1, 0, nums1.length);
}
思路2:
外排+三指针
两个指针分别指向两个数组最后一个元素的位置
一个指针指向arr1的最后一个位置
从arr1最后一个位置开始存放数据
从两个数组末尾元素开始比较存到数组中
public void merge(int[] nums1, int m, int[] nums2, int n) {
if (nums2 == null){
return;
}
int cur1 = m - 1;
int cur2 = n - 1;
int index = m + n - 1;
while (cur1 != -1 && cur2 != -1){
nums1[index--] = nums1[cur1] < nums2[cur2] ? nums2[cur2--] : nums1[cur1--];
}
while (cur1 != -1){
nums1[index--] = nums1[cur1--];
}
while (cur2 != -1){
nums1[index--] = nums2[cur2--];
}
}
3.整体移动数组
题目:
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
思路1
k%length = 真实位移长度(k有可能大于数组长度)
(i+k)%length = 移动后所在的位置
public static void rotate(int[] nums, int k) {
if (nums == null){
return;
}
int length = nums.length;
int[] help = new int[length];
// k % length = 真实移动长度
//因为mod的是数组长度 -> (0..n)%n
//(i + k)%length = 真实数据
for (int i =0;i < length;i++){
help[(i + k)%length] = nums[i];
}
System.arraycopy(help, 0, nums, 0, length);
}
思路2
翻转数组
k%length = 真实位移长度
1.先将数组对称翻转
2.再将 0 ~ k-1 的位置翻转
3.再将 k ~ length - 1 的位置翻转
public static void rotate(int[] nums, int k) {
//真实位移长度
k = k % nums.length;
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
public static void reverse(int[] nums, int start, int end) {
//数组一一翻转用while
//双指针
while (start < end) {
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start += 1;
end -= 1;
}
}
4.查找主要元素
题目:
数组中占比超过一半的元素称之为主要元素。给你一个 整数 数组,找出其中的主要元素。若没有,返回 -1 。
要求:
请设计时间复杂度为 O(N) 、空间复杂度为 O(1) 的解决方案。
思路:
Boyer-Moore 投票算法
从第一个元素开始遍历并计数
遇到相同数字则+1
遇到不同数字则-1
当数字减到0时则改变计数数字
public static int majorityElement(int[] nums){
if (nums == null){
return 0;
}
int curNumber = Integer.MIN_VALUE;
int count = 0;
for (int i = 0;i < nums.length;i++){
if (count == 0){
curNumber = nums[i];
}
if (curNumber == nums[i]){
count++;
}else {
count--;
}
}
count = 0;
for (int i = 0;i < nums.length;i++){
if (nums[i] == curNumber){
count++;
}
}
return count < (nums.length / 2) + 1 ? -1 : curNumber;
}
5.三数之和
题目:
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
要求:
答案中不可以包含重复的三元组
思路:
三指针(双指针+单指针)
1.先将数组排序
2.从头遍历结点
3.判断当前结点>0?,若>0.则说明后面的数之和肯定大于0
4.判断当前数与前一个数是否相同,相同直接跳下一个循环
5.两个指针分别指向当前数的下一个数 和 数组最后一个数
如果相加=0 -> 则存起来
如果两指针跳的下一个数跟当前数相同 -> 两指针直接下移
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> listList = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0;i < nums.length;i++){
if (nums[i] > 0){
return listList;
}
if (i > 0 && nums[i] == nums[i - 1]){
continue;
}
int L = i + 1;
int R = nums.length - 1;
while (L < R){
if (nums[i] + nums[L] + nums[R] == 0) {
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[L]);
list.add(nums[R]);
listList.add(list);
while (L < R && nums[L] == nums[L + 1]){
L++;
}
while (L < R && nums[R] == nums[R - 1]){
R--;
}
L++;
R--;
}else if (nums[i] + nums[L] + nums[R] < 0){
L++;
}else {
R--;
}
}
}
return listList;
}
6.最大子序和
7.最长连续序列
题目:
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
//时间 96.77
//空间 95.04
public int longestConsecutive_1(int[] nums) {
if (nums == null || nums.length == 0){
return 0;
}
Arrays.sort(nums);
//记录当前最大长度
int curRes = 1;
//记录返回值最大长度
int res = 1;
//记录数组长度
int length = nums.length;
for (int i = 1; i < length; i++) {
//相同的不需要判断
if (nums[i] == nums[i - 1]){
continue;
} else if (nums[i] == nums[i - 1] + 1){
curRes++;
}else {
curRes = 1;
}
res = Math.max(res,curRes);
}
return res;
}
//时间 31
//空间 45.89
public int longestConsecutive_2(int[] nums) {
if (nums == null || nums.length == 0){
return 0;
}
HashSet<Integer> set = new HashSet<>();
for (int i:
nums) {
set.add(i);
}
int res = 0;
for (int i:
nums) {
if (set.contains(i - 1)){
continue;
}
//记录当前数的值 和 当前长度
int cur = i;
while (set.contains(cur + 1)){
cur++;
}
//cur - i + 1 为区间长度
res = Math.max(res,cur - i + 1);
}
return res;
}
8.加1
题目:
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
//时间 95.45%
//空间 96.55%
public String addStrings(String num1, String num2) {
//记录两个指针 和 判断是否有进位
int i = num1.length() - 1, j = num2.length() - 1, add = 0;
StringBuffer ans = new StringBuffer();
while (i >= 0 || j >= 0 || add != 0) {
//当位数不足时,补零
int x = i >= 0 ? num1.charAt(i) - '0' : 0;
int y = j >= 0 ? num2.charAt(j) - '0' : 0;
int result = x + y + add;
//result模10即为位数上的数
ans.append(result % 10);
//add/10即为进位数
add = result / 10;
i--;
j--;
}
// 计算完以后的答案需要翻转过来
ans.reverse();
return ans.toString();
}
9.数组全排列
//时间 20.78
//空间 66.76
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new LinkedList<>();
if (nums == null || nums.length == 0){
return res;
}
allPermute(nums,0,res);
return res;
}
public void allPermute(int[] nums, int curIndex, List<List<Integer>> res) {
if(curIndex == nums.length - 1) {
List<Integer> list = new ArrayList<>();
for (int num:
nums) {
list.add(num);
}
res.add(list); // 添加排列方案
return;
}
//Set判断是否有重复字符
HashSet<Integer> set = new HashSet<>();
int length = nums.length;
for(int i = curIndex; i <= length - 1; i++) {
if(set.contains(nums[i])) continue; // 重复,因此剪枝
set.add(nums[i]);
//先固定第i为
swap(nums,i, curIndex); // 交换,将 c[i] 固定在第 x 位
allPermute(nums, curIndex+ 1,res); // 开启固定第 x + 1 位字符
swap(nums,i, curIndex); // 恢复交换
}
}
public void swap(int[] arr,int a, int b) {
int tmp = arr[a];
arr[a] = arr[b];
arr[b] = tmp;
}