LeetCode||探索数组类的算法

题目集合出处:LeetCode 网址:https://leetcode-cn.com/explore/orignial/card/all-about-array/230/define-with-good-care/948/

借此集合重新梳理一下有关数组的相关操作ヾ(◍°∇°◍)ノ゙

第一部分  做好初始定义

1.移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。

 思路:设置两个指针lastIndex和temp,由temp从后向前遍历,遇到为零的元素时停止,并将temp到lastIndex之间的元素均向前移动一个单位,结束后,将末尾元素置零,并将lastIndex向前移动一个单位。重复以上操作直至temp=0;

class Solution {
    public void moveZeroes(int[] nums) {
        int lastIndex=nums.length-1;
		int temp=nums.length-1;
		int count=0;
		while(temp>=0) {
			if(nums[temp]==0) {
				count=lastIndex-temp;
				for(int i=temp;i

 2.删除元素

给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

给定 nums = [3,2,2,3], val = 3,

函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,1,2,2,3,0,4,2], val = 2,

函数应该返回新的长度 5 并且 nums 中的前五个元素为 0,1,3,0,4注意这五个元素可为任意顺序。

你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

思路:从前向后遍历,遇到待删元素则不计入数组

8ms

class Solution {
    public int removeElement(int[] a, int val) {
        int sum=0;
        for (int i = 0;i

 3.删除重复元素

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 [1,2]

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 
[0,1,2,3,4]

你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

思路:用指针i来记录最终返回数组元素个数,用j遍历数组。如遇重复项则不计入i中。待后续优化ヾ(◍°∇°◍)ノ゙【12ms】

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length==0)
            return 0;
        int i=0,j;
        for(j=1;j

 4.删除重复元素II

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定 nums = [1,1,1,2,2,3],

函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2,3

你不需要考虑数组中超出新长度后面的元素。

示例 2:

给定 nums = [0,0,1,1,1,1,2,3,3],

函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。

你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

思路:思路同上题,由于有新的限制,则设置一个count来记录一个数字出现的次数,初始化为1。当遇到下一个相同数字时+1,如果count<3 则将nums[j]赋于nums[i]。

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length==0)
            return 0;
        int i=0,count=1,j;
        for(j=1;j

第二部分 运用基础算法思想

1.颜色分类

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

注意:
不能使用代码库中的排序函数来解决这道题。

示例:

输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]

进阶:

 

 

  • 一个直观的解决方案是使用计数排序的两趟扫描算法。
    首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
  • 你能想出一个仅使用常数空间的一趟扫描算法吗?

 

除了题目原有的解题思路,还可以使用各大排序算法进行排序

这里使用的是选择排序

class Solution {
   public static void sortColors(int[]nums) {
		int i=0;
		int j=0;
		int min=nums[0];
		int minIndex=0;
		for(i=0;i

2.数组中的第k大元素

 

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

说明:

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

class Solution {
    public int findKthLargest(int[] nums, int k) {
        Arrays.sort(nums);
        return nums[nums.length-k];
    }
}

这里偷懒了直接使用的排序函数,用快排更优ヾ(◍°∇°◍)ノ゙

3.合并两个有序数组

给定两个有序整数数组 nums1 nums2,将 nums2 合并到 nums1 使得 num1 成为一个有序数组。

说明:

  • 初始化 nums1nums2 的元素数量分别为 mn
  • 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i=0;//nums1
        int j=0;//nums2
        for(i=m;i= last)
			return;
 
		int i = first, j = last;
		int pivot = nums[i];// 选取第一个元素作为基准元素
 
		while (i < j) {
			while (i < j && nums[j] > pivot) { // 从右往左找比基准元素小的元素
				j--;
			}
			if (i < j) {
				nums[i++] = nums[j];
			}
 
			while (i < j && nums[i] <= pivot) { // 从左往右找比基准元素大的元素
				i++;
			}
			if (i < j) {
				nums[j--] = nums[i];
			}
		}
		nums[i] = pivot;
		quickSort(nums, first, i - 1);
		quickSort(nums, i + 1, last);
	}

}

 【另:尾插法:选取两数组中最大元素从后往前放置(nums1有足够大的空间,从k=m+n-1开始)】

 

 第三部分  双索引技巧

1. 两数之和 II - 输入有序数组

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2

说明:

  • 返回的下标值(index1 和 index2)不是从零开始的。
  • 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

示例:

输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

思路:设置双指针,一个从左向右遍历,一个从右向左。如果两数相加等于target则输出数组,如果大于target则将右指针向前移动,减少相加所得数值(此为升序数组)

千万注意题目中的index1和index2不是从零开始的(以下是由着从0开始编写的,故而新生成数组时两指针均+1来确保答案正确ヾ(◍°∇°◍)ノ゙)

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int l=0;
        int r=numbers.length-1;
        while(ltarget)
                r--;//减少数值
            else
                l++;//增大数值
        }
        return numbers;
    }
}

2.验证回文串

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:

输入: "A man, a plan, a canal: Panama"
输出: true

示例 2:

输入: "race a car"
输出: false

思路1:将字符串中除了字母和数字外的字符去除,统一转成小写/大写模式,再使用双指针进行一一比对。错误则输出false

【使用java中的replaceAll方法】

class Solution {
    public boolean isPalindrome(String s) {
		s=s.replaceAll("[^a-zA-Z0-9]", "");//保留字母和数字
		s=s.toLowerCase();
		char[]c=s.toCharArray();
		int i=0,j=s.length()-1;
		while(i<=j) {
			if(c[i]!=c[j])
				return false;
			i++;
			j--;
		}
		return true;
	}
}

思路2:将字符串中除了字母和数字外的字符去除,统一转成大写/小写模式,再将字符串反转,若反转后字符串与原字符串不相等,则输出false

 

3.反转字符串中的元音字母

编写一个函数,以字符串作为输入,反转该字符串中的元音字母。

示例 1:

输入: "hello"
输出: "holle"

示例 2:

输入: "leetcode"
输出: "leotcede"

说明:
元音字母不包含字母"y"。

首先要知道元音字母有:aeiou!

思路:双索引

class Solution {
   public String reverseVowels(String s) {
        if(s==""||s==null)
            return s;
        char c[]=s.toCharArray();//变为数组
        int i=0,j=s.length()-1;
        while(i

4.盛最多水的容量

给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (iai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (iai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

class Solution {
    public int maxArea(int[] height) {
        int maxare=0;//最大水量
        int l=0,r=height.length-1;
        while(l

第四部分 滑动窗口

给定一个含有 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组如果不存在符合条件的连续子数组,返回 0。

示例: 

输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
class Solution {
   public static int minSubArrayLen(int s,int[]nums) {
		int left=0,right=-1;
		int sum=0;
		int min=nums.length+1;
		while(left=s) {
				min=Math.min(min, right-left+1);
			}
		}
       if(min==nums.length+1)
           return 0;
		return min;
	}
}

 官方解题思想:

要求是连续子数组,所以我们必须定义 i,j两个指针,i 向前遍历,j 向后遍历,相当与一个滑块,这样所有的子数组都会在 [i...j] 中出现,如果 nums[i..j] 的和小于目标值 s,那么j向后移一位,再次比较,直到大于目标值 s 之后,i 向前移动一位,缩小数组的长度。遍历到i到数组的最末端,就算结束了,如果不存在符合条件的就返回 0。

LeetCode||探索数组类的算法_第1张图片

 

你可能感兴趣的:(算法学习)