LeetCode刷题:数组

以下采用【Java的解法】
参考了LeetCode的题解

题目26: 删除排序数组中的重复项

  • 我的解答:
// 目的是循环一次,删除重复的元素,返回新的列表(无重复元素的)的长度
class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length==0) return 0;
        else if(nums.length==1) return 1;
        else{
            int n=1; //用来记录长度
            int l=0; //双指针
            for(int r=1;r<nums.length;r++){
                if(nums[r]!=nums[l]){
                    l++;
                    nums[l]=nums[r];
                    n++;
                }
            }
            return n;
        }
    }
}
  • 官方解答:
  • 解法: 双指针
    在这里插入图片描述

题目48: 旋转图像

  • 思路1: 先对矩阵进行转置,然后一头一尾两列两列交换。
class Solution {
    public void rotate(int[][] matrix) {
        //对矩阵进行转置,这里假设n>=1,实际上我们对角线元素不需要考虑转置
        int n=matrix.length;
        int tmp;
        for(int i=0;i<n;i++){
            //注意这里是从i+1开始
            for(int j=i+1;j<n;j++){
                tmp=matrix[i][j];
                matrix[i][j]=matrix[j][i];
                matrix[j][i]=tmp;
            }
        }
        //一头一尾两列两列交换
        for(int i=0,j=n-1;i<j;i++,j--){
            for(int l=0;l<n;l++){
                tmp=matrix[l][i];
                matrix[l][i]=matrix[l][j];
                matrix[l][j]=tmp;
            }
        }
    }
}
  • 思路2: 找规律
    关键在于确定循环的边界,以及交换的顺序。
    在这里插入图片描述
class Solution {
    public void rotate(int[][] matrix) {
        int n=matrix.length;
        for(int i=0;i<n/2;i++){
            for(int j=i;j<n-1-i;j++){
                //对选出来的四个数进行交换,这样也就可以轻松改变旋转的方向
                int tmp=matrix[i][j];
                matrix[i][j]=matrix[n-1-j][i];
                matrix[n-1-j][i]=matrix[n-1-i][n-1-j];
                matrix[n-1-i][n-1-j]=matrix[j][n-1-i];
                matrix[j][n-1-i]=tmp;
            }
        }
    }
}

题目1: 两数之和

  • 收获:
    • 小心数据相等的情况!
    • ⚠️ 因为如果只有在if中return会编译不通过,所以可以在外部throw new IllegalArgumentException("No two sum solution");
    • ⚠️可以不需要构造int tmp,直接通过return new int[]{i,j}
    • 哈希表
  • 方法一:暴力法
class Solution {
    public int[] twoSum(int[] nums, int target) {
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[j] == target - nums[i]) {
                    return new int[] { i, j };
                }
            }
        }
        throw new IllegalArgumentException("No two sum solution");
    }
}
- 时间复杂度: O(n^2)
- 空间复杂度: O(1)
  • 方法二:排序+双指针

哈希表

  • 方法三:哈希表
    具体思路大致是通过将每个元素的Key,Value映射到一个数,然后把这个数当作是它自己的编码。所以查找的时候可以只查找1次就可以直到所查找的数字是否在里面。
元素序号12
Key23
Value01

Hash表在Java中的使用:

.put(KEY,VALUE),.get(KEY),.containsKey(KEY),.getOrDefault(...,...)

import java.util.HashMap;
import java.util.Map;

Map<Integer,String> map=new HashMap<Integer,String>();
		map.put(1,"hi");
		if(map.containsKey(1)) //通过这个来查找
			System.out.println(map.get(1));
		System.out.println(map.getOrDefault(1,"Don't contain the input key")); //如果存在input的key则输出对应的值
		System.out.println(map.getOrDefault(3,"Don't contain the input key")); //否则输出后面自定义的值,因为这里的map是从integer到String所以填的是字符串。

此题Hash解法:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (map.containsKey(complement)) {
                return new int[] { map.get(complement), i }; 
                //因为之前的元素在map里,所以这里的顺序是i在后面
            }
            //如果要找的元素不在里面,就把当前元素的值当作key,index作为值存储起来
            map.put(nums[i], i);         
        }
        throw new IllegalArgumentException("No two sum solution");
    }
}
- 时间复杂度: O(n),因为要对n个元素进行查找,每次查找的开销为O(1)
- 空间复杂度: O(n),因为该表最多存储n个元素

题目36: 有效的数独

  • 我的思路:
  • 直接开三个数组用来存储数据
class Solution {
    public boolean isValidSudoku(char[][] board) {
        int[][] h = new int[9][9]; // 用来记录每列中1-9是否出现过,第一个数字表示第几列
		int[][] v = new int[9][9];
		int[][] s = new int[9][9];
		for (int i = 0; i < 9; i++) {
			for (int j = 0; j < 9; j++) {
				if (board[i][j] != '.') {
					int k = board[i][j] - '0';
					if (h[i][k - 1] == 0)
						h[i][k - 1] += 1;
					else
						return false;
					if (v[j][k - 1] == 0)
						v[j][k - 1] += 1;
					else
						return false;
					if (s[3 * (i / 3) + j / 3 ][k - 1] == 0)
						s[3 * (i / 3) + j / 3 ][k - 1] += 1;
					else
						return false;
				}
			}
		}
		return true;
    }
}

题目66:加一

  • 收获:
    • 如果直接初始化int [] tmp=new int[10];则tmp中的所有元素均为0。
  • 别人的题解:
class Solution {
    public int[] plusOne(int[] digits) {
        for (int i = digits.length - 1; i >= 0; i--) {
            digits[i]++;
            digits[i] = digits[i] % 10;
            if (digits[i] != 0) return digits;
        }
        digits = new int[digits.length + 1];
        digits[0] = 1;
        return digits;
    }
}
  • 因为这道题目巧在只可能在99,999等情况下才需要扩大数组的长度。其他情况下加完(即包括进位之后)函数的值都不为0。

题目283:移动零

  • 收获:
    • 双指针
  • 比较慢的解法:
public void moveZeroes(int[] nums) {
    	int l = nums.length;
    	int k =0;
    	int[] tmp = new int[l]; //刚好用上初始化的默认填充元素是0
    	for(int i=0;i<l;i++) {
    		if(nums[i]!=0) {
    			tmp[k]=nums[i];
    			k++;}
    	}
    	for(int i=0;i<l;i++) {
    		nums[i]=tmp[i];
    	}
    }
//我根据我的理解码的
public void moveZeroes(int[] nums) {
    	int l=nums.length;
    	int j=0;
    	int i,tmp;
    	//j指向第一个0元素,换言之j记录了列表中非0元素的个数
    	while(j<l){
    		if(nums[j]==0) {
    			//寻找第一个非0元素
    			for(i=j+1;i<l;i++) {
    				if(nums[i]!=0) {
    					tmp=nums[i];
    					nums[i]=nums[j];
    					nums[j]=tmp;
    					break;
    				}
    			}
    			//如果i走到尽头了,则把剩下元素补充成为0
    			if(i==l) {
    				for(;j<l;j++)
    					nums[j]=0;
    			}
    		}
    		j++;
    	}
  • 其实这个while可以去掉:
//https://leetcode-cn.com/problems/move-zeroes/solution/dong-hua-yan-shi-283yi-dong-ling-by-wang_ni_ma/
//一次遍历的解法,用i和j两个指针。
class Solution {
    public void moveZeroes(int[] nums) {
        if(nums == null) return;

        //指针j,遍历开始后,在遇到0元素之前,随i同步移动,自从遇到第一个0元素,永远代表数组内从左到右的第一个0元素
        int j = 0;
        for(int i=0; i<nums.length; i++){
            //指针i,从左到右逐个遍历,若不为0,与nums[j++]交换
            if(nums[i] != 0){
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j++] = temp;
            }
        }
        
    }
}

⚠️ 题目136:只出现一次的数字

  • 收获:
    • 哈希集
    • 异或运算

哈希集

有两种不同类型的哈希表:哈希集合哈希映射

  • 哈希集合是集合数据结构的实现之一,用于存储非重复值
  • 哈希映射是映射数据结构的实现之一,用于存储(key, value)键值对
    哈希表的关键思想是使用哈希函数将键映射到存储桶。

例子
我们使用 y = x % 5 作为哈希函数。让我们使用这个例子来完成插入和搜索策略:

  • 插入:我们通过哈希函数解析键,将它们映射到相应的桶中。例如,1987 分配给桶 2,而 24 分配给桶 4。
  • 搜索:我们通过相同的哈希函数解析键,并仅在特定存储桶中搜索。
    • 如果我们搜索1987,我们将使用相同的哈希函数将1987 映射到 2。因此我们在桶 2 中搜索,我们在那个桶中成功找到了 1987。
    • 例如,如果我们搜索 23,将映射 23 到 3,并在桶 3 中搜索。我们发现 23 不在桶 3 中,这意味着 23 不在哈希表中。

哈希集在Java中的使用

import java.util.HashSet;
import java.util.Set;
Set<Integer> set = new HashSet<>(); //创建一个只存储Int的HashSet
		
set.add(1);
set.add(2);
set.add(3);
TC.println(set.remove(3));
TC.println(set.remove(10)); //如果移除失败就会返回false
		
//对于HashSet的遍历:但不直到为什么这里每次值会变动...
Iterator<Integer> i = set.iterator();
while(i.hasNext()) {
	TC.println(i.next());
}
// 输出是 1,2
  • 思路一:HashSet
public int singleNumber(int[] nums) {
	Set<Integer> set = new HashSet<Integer>();
	int l = nums.length;
	for(int i=0;i<l;i++) {
		if(!set.remove(nums[i]))
			set.add(nums[i]);
	}
	//因为最后只剩下唯一一个元素了,即i.next();
	return set.iterator().next();
}
  • 线性时间复杂度 O(n)
  • 空间复杂度上会达到 O(n),因为使用 Hash 映射来进行计算,遍历一次后结束得到结果,

异或运算(XOR)

异或运算的一些性质
  1. a ⊕ 0 = a a\oplus0=a a0=a
  2. a ⊕ a = 0 a\oplus a=0 aa=0
  3. XOR运算满足交换率和结合律: a ⊕ b ⊕ a = a ⊕ a ⊕ b = 0 ⊕ b = b a\oplus b \oplus a = a \oplus a \oplus b =0 \oplus b=b aba=aab=0b=b
一个实际的例子
  • 其实类似列竖式,但计算规则是:
    1 ⊕ 1 = 0 1 \oplus 1 = 0 11=0
    1 ⊕ 0 = 1 1 \oplus 0 = 1 10=1
    0 ⊕ 1 = 1 0 \oplus 1 = 1 01=1
    0 ⊕ 0 = 0 0 \oplus 0 = 0 00=0
    但没有进位。

a = 10 = 101 0 ( 2 ) a = 10 = 1010_{(2)} a=10=1010(2) b = 7 = 011 1 ( 2 ) b=7=0111_{(2)} b=7=0111(2)
a ⊕ 0 = 101 0 ( 2 ) ⊕ 000 0 ( 2 ) = 101 0 ( 2 ) = a a \oplus 0=1010_{(2)} \oplus 0000_{(2)} =1010_{(2)}=a a0=1010(2)0000(2)=1010(2)=a
a ⊕ a = 101 0 ( 2 ) ⊕ 101 0 ( 2 ) = 000 0 ( 2 ) = a a \oplus a=1010_{(2)} \oplus 1010_{(2)} =0000_{(2)}=a aa=1010(2)1010(2)=0000(2)=a
a ⊕ b ⊕ a = 101 0 ( 2 ) ⊕ 011 1 ( 2 ) ⊕ 101 0 ( 2 ) = 011 1 ( 2 ) = b a \oplus b \oplus a = 1010_{(2)} \oplus 0111_{(2)} \oplus 1010_{(2)} = 0111_{(2)} = b aba=1010(2)0111(2)1010(2)=0111(2)=b

  • 思路二:异或运算(XOR)
    由前面的性质,这里很凑巧,所有重复的元素只重复两次,所以我们可通过异或运算将这些重复的元素变成0。也正因此,所有元素异或下来之后的结果就是最终的答案。
public int singleNumber(int[] nums) {
	int l = nums.length;
	int s = 0;	//因为s跟任何元素的异或运算最后的值都是另一个元素
	for(int i=0;i<l;i++) {
		s^=nums[i];
	}
	return s;
}

⚠️ 题目189: 旋转数组 (Permutation的解法)

  • 收获:
    • permutation的写法:
  • 思路一:根据数学观察得到的结果:
  • 思路二:
 	public static void rotate(int[] nums, int k) {
		int n = nums.length;
		k %= n; //确保置换的位数小于数组长度,否则后面的reverse就会出现负数的情况ß
		reverse(nums,0,n-1); //倒着输出数组nums[0,n-1]
		reverse(nums,0,k-1); //因为第一次倒换最后的元素放到了前面。
		reverse(nums,k,n-1);
    }
	
	public static void reverse(int[] nums, int l, int r) {
		//通过对称两两交换实现倒着输出的目的
		while(l<r) {
			int tmp=nums[l];
			nums[l]=nums[r];
			nums[r]=tmp;
			l++;
			r--;
		}
	}

题目350: 两个数组的交集 II

  • 收获:
    • 利用Java的包java.util.Arrays
    • 排序:
    	import java.util.Arrays;
    	int[] a={1,4,-1,5,0};
    	Arrays.sort(a); 
    	//直接在a原地修改。
    	//数组a[]的内容变为{-1,0,1,4,5}
    
    • 对数组进行切片:
    Arrays.copyOfRange(a, 0, 3); //以tableau的形式返回a中前3个元素
    
  • 思路一:排序+双指针:
public int[] intersect(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
		int l1 = nums1.length, l2 = nums2.length;
		int[] tmp = new int[Math.min(l1, l2)];
		int i = 0, j = 0, k = 0;
		while (i < l1 && j < l2) {
			if (nums1[i] < nums2[j])
				i++;
			else if (nums2[j] < nums1[i])
				j++;
			else {
				tmp[k++] = nums1[i];
				i++;
				j++;
			}
		}
		int[] res = new int[k];
		for(i=0;i<k;i++) {
			res[i]=tmp[i];
		}
		return res;
	}
  • 思路二:HashMap
public int[] intersect(int[] nums1, int[] nums2) {
        if(nums1.length>nums2.length)
        	return intersect(nums2,nums1); //为了保证节省空间
        else {
        	//对含有较少元素的数组构建HashMap,Key是nums列表中元素,value是对应的值(这里是对应元素出现的次数)。
        	HashMap<Integer,Integer> m = new HashMap<Integer,Integer>();
        	for(int num:nums1) {
        		int cnt = m.getOrDefault(num,0)+1; //如果之前在map中有这个元素,则取之前的值+1,反之,若之前没有即为0+1。
        		m.put(num, cnt);
        	}
        	
        	// 构建最后返回的数组
        	int[] res = new int[nums1.length];
        	// 遍历nums2
        	int k=0;
        	for(int num:nums2) {
        		//如果map中含有这个元素
        		if(m.getOrDefault(num,0)>0) {
        			res[k++]=num;
        			m.put(num,m.get(num)-1);
        		}
        	}
        	return Arrays.copyOfRange(res, 0, k);
        }	
	}

题目:217. 存在重复元素

  • 思路一:HashMap
public static boolean containsDuplicate(int[] nums) {
        HashMap<Integer,Integer> m = new HashMap<Integer,Integer>();
        for(int num:nums){
            if(!m.containsKey(num))
                m.put(num,1);
            else
                return true;
        }
        return false;
    }
  • 时间复杂度 : O(n)。 search() 和 insert() 各自使用 n 次,每个操作耗费常数时间。
  • 空间复杂度 : O(n)。哈希表占用的空间与元素数量是线性关系。
  • 思路二:排序
  • 时间复杂度 : O(nlogn)。 排序的复杂度是 O(nlogn),扫描的复杂度是 O(n)。整个算法主要由排序过程决定,因此是 O(nlogn)。
  • 空间复杂度 : O(1)。 这取决于具体的排序算法实现,通常而言,使用 堆排序 的话,是 O(1)。

题目:122. 买卖股票的最佳时机 II

  • 收获:java.util.ArrayList的使用
import java.util.ArrayList;
ArrayList<Integer> list = new ArrayList<Integer>(); //链表的使用
list.add(4);
list.add(5);
list.add(9);
System.out.println(list.size());
System.out.println(list.get(2));
for(int num:list) {
System.out.println(num);
}
  • 思路:看转折点
  • 实际上只要看总共上升的值即可(贪心)每次只加增长的。
public int maxProfit(int[] prices) {
		int res=0;
		int l=prices.length;
		for(int i=0;i<l-1;i++) {
			if(prices[i+1]>prices[i])
				res+=prices[i+1]-prices[i];
		}
		return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值