简述:今天是2018/5/20日,距离我大学毕业剩下短短不到一年时间。最近一段时间开始刷LeetCode,希望自己可以加油学习,总结,做一个让自己满意的人。也会慢慢将自己走的一些弯路,应聘以及人际交往的心得写下来。不积跬步,无以至千里。不积小流,无以成江河。时光只是自己的过客,做自己的主人,是一切美好的开始。
数组
目录
从排序数组中删除重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 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。
你不需要考虑数组中超出新长度后面的元素。
解决思路
这道题比较容易思考,由于数组已经排好序。那么就只需要判断元素是否与它下一个元素是否相同,若是相同则跳过这个相同元素,直到找到不同的元素,将其记录到元素的下一个位置。重复上述步骤。
解决要点:用不重复的元素覆盖掉重复的元素。(可以忽略数组中超出新长度的元素)
- 设置两个标记点len和i,i用来查找下一个不同的数字
public int removeDuplicates(int[] nums){
int len = 0;
for(int i=1;i<nums.length;i++){
if(nums[len]!=nums[i]){
len++;
nums[len] = nums[i];
}
}
return len+1;
}
买卖股票的最佳时机II
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)
示例1
输入: [7,1,5,3,6,4] 输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例2
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
★
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
解决思路
我的思路: 这道题可以分为三种情况:
- 给定数组是增序的:MaxProfix = nums[length-1] - nums[0]
- 给定数组是降序的:MaxProfix = 0
- 给定数组是无序的:需要判断,找到数组中增序的片段,通过这些增序的片段来获取利益,再将其相加
public int MaxProfits(int[] prices){
//递增有序数组
int length = prices.length;
//判断是否为空
if(length==0){
return 0;
}
if(isSortedIncrease(prices)){
return prices[length-1]-prices[0];
}
//递减有序数组
if(isSortedDecrease(prices)){
return 0;
}
//无序数组
int number = 0;
int maxprofit=0;
int profit = 0;
for(int i=1;i<length;i++){
number = i-1;
while(prices[i-1]<prices[i]){
//若小于,则继续判断
i++;
//判断是否结束
if(i==length){
break;
}
}
profit = prices[i-1] - prices[number];
maxprofit += profit;
}
return maxprofit;
}
public boolean isSortedIncrease(int[] prices){
for(int i =0;i<prices.length-1;i++){
if(prices[i]>prices[i+1]){
return false;
}
}
return true;
}
public boolean isSortedDecrease(int[] prices){
for(int i =0;i<prices.length-1;i++){
if(prices[i]<prices[i+1]){
return false;
}
}
return true;
}
其他思路:
旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例1
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例2
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
解决思路
- 每次移动一个位置,通过for循环移动所需要移动的次数。设置一个记录点,记录最后一个数。然后将前面的数依次往前移。
public int[] rotate(int nums[],int k){
for(int i=0;i<k;i++){
rotateOne(nums);
}
}
public void rotateOne(int nums[]){
int length = nums.length;
if(length==0){
return;
}
int number = nums[length-1];
for(int i=length-2;i>=0;i--){
nums[i+1] = nums[i];
}
nums[0] = number;
}
- 通过取余运算来完成移动
public int[] rotate(int nums[],int k){
if(nums.length==0 | nums.length==1){
return nums;
}
int[] result = new int[nums.length];
for(int i=0;i<nums.length;i++){
result[(i+k)%nums.length] = nums[i];
}
return result;
存在重复
给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
示例1
输入:[1,2,3,4]
输出:true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
解决思路
利用一个HashMap存储数组,判断是否存在重复,若存在重复,则返回true
public boolean containsDuplicate(int[] nums) {
if(nums.length==0){
return false;
}
Map<Integer,Integer> map = new HashMap<Integer, Integer>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(nums[i])){
return true;
}
map.put(nums[i],i);
}
return false;
}
只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
解决思路
- 这个问题看到之后我首先想的是使用一个HashMap,存储其元素及其出现的次数,然后遍历map,找到value为1的元素。代码如下
public int singleNumber(int[] nums) {
Map<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++){
Integer value = map.get(nums[i]);
map.put(nums[i],(value==null ? 0 : value )+1);
}
int key=0;
for(int getKey : map.keySet()){
if(map.get(getKey) == 1){
key = getKey;
}
}
return key;
}
}
- 在网上搜索之后发现,还有一个非常取巧的方法,那就是对这些元素进行异或运算。由于相同的两个元素异或为0.0 ^ 0=0,0 ^ 1=1 所以不会改变最后一个单独的数。
int SingleNum = 0;
for(int num : nums){
SinggleNum ^= num;
}
两个数组的交集II
给定两个数组,写一个方法来计算它们的交集。
例如:
给定 nums1 = [1, 2, 2, 1], nums2 = [2, 2], 返回 [2, 2].
注意:
- 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
- 我们可以不考虑输出结果的顺序。
解决思路
将其中一个数组放入HashMap中,然后记录其元素以及元素出现的个数。遍历另一个数组,找到对应元素。map中的元素个数减1,直到找到所有的交集。
public int[] interSect(int[] nums1,int [] nums2){
if(nums1.length==0 | nums2.length==0){
return null;
}
//将数组1放入哈希map中
List<Integer> tmp = new ArrayList<>();
Map<Integer,Integer> map = new HashMap<Integer, Integer>();
//将nums2放入map中
for(int i=0;i<nums2.length;i++){
Integer Value = map.get(nums2[i]);
map.put(nums2[i], (Value == null ? 0 : Value) + 1);
}
//遍历nums1
for(int j=0;j<nums1.length;j++){
//若map中存在该数字,且map.getValue>0 则将其放入tmp中,map中的该数字计数 -1
if(map.containsKey(nums1[j]) && map.get(nums1[j])>0){
tmp.add(nums1[j]);
map.put(nums1[j],map.get(nums1[j])-1);
}
}
int[] result = new int[tmp.size()];
int r=0;
for(Integer t : tmp){
result[r++] = t;
}
return result;
}
加一
给定一个非负整数组成的非空数组,在该数的基础上加一,返回一个新的数组。
最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
解决思路
这道题类似于数字逻辑中的加法器,设置进位 C=1 进位初始化为1,若该数字大于9则设该位位0,继续设置进位为1
public int[] plusOne(int[] digits){
LinkedList<Integer> list = new LinkedList<>();
int C=1;
for(int i=0;i<digits.length;i++){
//给每一位加上进位,从最后一位开始
digits[digits.length-i-1] += C;
if(digits[digits.length-i-1]>9){
//若大于9,则设进位,且该位设为0
C=1;
list.addFirst(0);
if((digits.length-i-1) ==0){
list.addFirst(1);
}
}else{
list.addFirst(digits[digits.length-i-1]);
C=0;
}
}
int[] result = new int[list.size()];
for(int j=0;j<list.size();j++){
result[j] = list.get(j);
}
return result;
}
移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
- 必须在原数组上操作,不能拷贝额外的数组。
- 尽量减少操作次数。
解决思路
如果检测到数组元素为0,则将0放到最后一位,其余位依次向前推移,这道题我完成的比较暴力。由于是第一次刷,希望有更好想法的朋友可以一起交流。
public void moveZeroes(int[] nums){
//思路:如果检测到数组元素为0,则将0放到最后一位,其余位依次向前推移
int len = nums.length;
int number = 0;
int i = 0;
while(len>1 && i < len-1){
if(nums[i]==0){
number = nums[i];
for(int j=i;j < len-1;j++) {
nums[j] = nums[j + 1];
}
nums[len-1] = number;
len--;
}else{
i++;
}
}
}
两数之和
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
解决思路
这道题我的解决思路为将其元素放入map中,然后通过循环在该map中寻找target-nums[i]元素,若存在就返回找到的下标。
version:0.1
public int[] twosum(int[] nums,int target){
int [] result = new int[2];
if(nums.length==0){
return nums;
}
Map<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++){
map.put(nums[i],i);
}
for(int i=0;i<nums.length;i++){
if(map.containsKey(target-nums[i]) && map.get(target-nums[i])!=i){
result[0] = i;
result[1] = map.get(target-nums[i]);
}
}
return result;
}
想了想,其实可以将两个for循环放在一起,通过之后添加进来的元素和前面添加进来的进行搜寻,这样可以简化代码,具体如下
version:0.2 简化版
public int[] twosum(int[] nums,int target){
int [] result = new int[2];
if(nums.length==0){
return nums;
}
Map<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(target-nums[i])){
result[0] = i;
result[1] = map.get(target-nums[i]);
}
map.put(nums[i],i);
}
return result;
}
如果要返回多组值的话,我又写了一个可以返回多组值得
public Map twosum(int[] nums,int target){
Map<Integer,Integer> result = new HashMap<Integer,Integer>();
if(nums.length==0){
return result;
}
Map<Integer,Integer> map = new HashMap<>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(target-nums[i])){
result.put(i,map.get(target-nums[i]));
}
map.put(nums[i],i);
}
return result;
}
有效的数独
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
- 数字 1-9 在每一行只能出现一次。
- 数字 1-9 在每一列只能出现一次。
- 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
解决思路
这道题解决方法,题目中其实已经给出来了。只需要我们选择相应的数据结构与解决算法即可。这里需要判断重复,所以我们选择Set集合是最好的。
public boolean isValidSudoku(char[][] board) {
if(board == null || board.length < 9 || board[0].length < 9)
return false;
Set<Character> rowset = new HashSet<Character>();
Set<Character> colset = new HashSet<Character>();
for(int i = 0; i < 9; i++)
{
//清空
rowset.clear();
colset.clear();
for(int j = 0; j < 9; j ++)
{
//i= j= 0,0 0,3 0,6 3,0 3,3 3,6 6,0 6,3 6,6
if((i % 3 == 0 && j%3==0)) // 检查块是否有效
{
if(!checkBlock(board, i, j))
return false;
}
if(board[i][j] != '.') // 检查行是否有效
{
if(rowset.contains(board[i][j]))
return false;
rowset.add(board[i][j]);
}
if(board[j][i] != '.') // 检查列是否有效
{
if(colset.contains(board[j][i]))
return false;
colset.add(board[j][i]);
}
}
}
return true;
}
public boolean checkBlock(char[][] board, int row, int col) // 检查块是否有效
{
Set<Character> blockSet = new HashSet<Character>();
for(int i = row; i < row + 3; i++)
{
for(int j = col; j < col + 3; j++)
{
if(board[i][j] != '.')
{
if(blockSet.contains(board[i][j]))
return false;
blockSet.add(board[i][j]);
}
}
}
return true;
}
}
旋转图像
给定一个 n × n 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
说明:
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
解决思路
经过观察不难发现,我们每次需要旋转的只有一圈中的几个元素,旋转完成即可。
旋转图像 matrix[i][j]
旋转之后的位置为matrix[j][M-i] M为n-1
* 对于n*n的矩阵
* 第一圈需要旋转n-1个元素matrix[0][0] -->matrix[0][n-2]
* 第二圈需要旋转n-3个元素matrix[1][1] -->matrix[1][n-3]
* 第三圈 n-5个元素
于是,我们可以写出相应代码
public void Imagerotate(int[][] nums){
int M = nums.length-1;
int len = M;
int temp;
int i=0;
int j=0;
while(len>0){
//完成一圈的旋转,第一圈i=0 j<=len-1 第二圈 i=1 j <= len-2
for(j= i;j<len+i;j++)
{
temp = nums[j][M-i];
nums[j][M-i] = nums[i][j];
nums[i][j] = nums[M-j][i];
nums[M-j][i] = nums[M-i][M-j];
nums[M-i][M-j] = temp;
}
//i+1
i++;
//len-2
len = len-2;
}
}