巩固练习
题目一
存在重复元素
本题暴力思路就是首先排好序,然后前一位与后一位比较,(两个for循环)。
class Solution {
public boolean containsDuplicate(int[] nums) {
Arrays.sort(nums);
for(int i=0;i<nums.length-1;i++){
if(nums[i]==nums[i+1])
{
return true;
}
}
return false;
}
}
第二种方法是使用到哈希表,用到set.add判断输出元素是否存在,如果存在,就是重复,返回true,否则false。(set.add(num),如果num存在,不需要添加,返回false,num不存在,即添加进set,返回True。)
class Solution {
public boolean containsDuplicate(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int i:nums){
if(!set.add(i))
{
return true;
}
}
return false;
}
}
题目二
最大子数组和
没有搞出来呢,难住我了,看懂了一个暴力法,也是超时了,后续再刷。
class Solution {
public int maxSubArray(int[] nums) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++)
{
int sum = 0;
for (int j = i; j < nums.length; j++)
{
sum += nums[j];
if (sum > max)
{
max = sum;
}
}
}
return max;
}
}
题目三
两数之和
两数之和,看到题目,首先想到的还是两个for循环,暴力解决问题。
class Solution {
public int[] twoSum(int[] nums, int target) {
int n = nums.length;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
return new int[0];
}
}
其次再想到的就是哈希函数了,使用了map.containsKey来判断map中是否包含此元素的key,如果包含,就返回当前元素的value和map中的value,其中map.put(nums[i],i);最为重要,位置的错误会导致重复的使用。
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(target-nums[i])){
return new int[]{map.get(target-nums[i]),i};
}
map.put(nums[i],i);
}
return new int[0];
}
}
题目四
合并两个有序数组
暴力法就是直接把nums1后面的空位直接赋值nums2中的元素,最后排序即可。
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
for (int i = 0; i != n; ++i) {
nums1[m + i] = nums2[i];
}
Arrays.sort(nums1);
}
}
第二种就是双指针,让数组从头开始,排除掉nums1或者num2长度为0情况,按照大小顺序一次插入新数组中。其中出现了个错误,提示 solution merge,翻译为解决方案合并,原因是因为全都写成if语句了,没有使用else if,就一直归并了。(愚蠢哟)
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int x = 0;
int y = 0;
int[] a = new int[m + n];
int t;
while(x<m||y<n)
{
if(x==m){
t = nums2[y++];
}
else if(y==n){
t = nums1[x++];
}
else if(nums1[x]<nums2[y])
{
t=nums1[x++];
}
else{
t=nums2[y++];
}
a[x+y-1]=t;
}
for(int i = 0;i<m+n;i++)
{
nums1[i]=a[i];
}
}
}
第三种就是双指针逆序,与上一种方法,时间不变,空间减小,不需要新创空间。从后往前,不需要考虑会不会覆盖num1的元素。
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int x = m - 1;
int y = n - 1;
int len = m + n - 1;
int cur;
while(x >= 0 || y >= 0){
if(x == -1){
cur = nums2[y--];
}
else if(y == -1){
cur = nums1[x--];
}
else if(nums1[x] > nums2[y]){
cur = nums1[x--];
}
else{
cur = nums2[y--];
}
nums1[len--] = cur;
}
}
}
题目五
两个数组的交集 II
第一种方法就是先把两个数组排序,然后依次比较,找到相等的值保存。
用到了Arrays.copyOfRange(nums,index1,index2),意思为从nums中提取下标为index1到下标为index2-1的元素,如果nums中没有元素,则返回0;(学到了)
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
int length1 = nums1.length, length2 = nums2.length;
int[] intersection = new int[Math.min(length1, length2)];
int x = 0,y = 0,t = 0;
while(x<length1&&y<length2){
if(nums1[x]>nums2[y]){
y++;
}
else if(nums1[x]<nums2[y]){
x++;
}
else {
intersection[t] = nums1[x];
x++;
y++;
t++;
}
}
return Arrays.copyOfRange(intersection,0,t);
}
}
第二种方法用到了哈希表,其中一个定义map.getOrDefaulte(num,0) 意思为提取map中key为num的值,如果没有num,则返回0,如果有,则返回其本身的value。这个确实巧妙,当时看了题解,也一直思考怎么去增加删除。第二种思路就是先把长度小的数组放到哈希表中,并且把每个数出现的个数为value,然后与第二个数组比较,相同的添加到新的数组中,并且哈希中其value减1,value为0的直接删除,即再比较到相同值,不会添加,因为num1中已经用完。然后还是用copyOfRange来输出。
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
if(nums1.length>nums2.length){
return intersect(nums2,nums1);
}
Map<Integer,Integer> map = new HashMap<>();
for(int num:nums1){
int count = map.getOrDefault(num,0)+1;
map.put(num,count);
}
int[] intersection = new int[nums1.length];
int index = 0;
for(int num:nums2){
int count = map.getOrDefault(num,0);
if(count>0){
intersection[index++] = num;
count--;
if(count>0){
map.put(num,count);
}
else{
map.remove(num);
}
}
}
return Arrays.copyOfRange(intersection,0,index);
}
}
题目六
买卖股票的最佳时机
首选暴力解决,但是这次超时了(糟糕)
public class Solution {
public int maxProfit(int[] prices) {
int maxprofit = 0;
for (int i = 0; i < prices.length - 1; i++) {
for (int j = i + 1; j < prices.length; j++) {
int profit = prices[j] - prices[i];
if (profit > maxprofit) {
maxprofit = profit;
}
}
}
return maxprofit;
}
}
于是开始改善,这个对于我来说理解有点晕,每次都是看了就忘,通过循环,找到最低价格买入股票,然后最大利润就是第i天之后的某一天,然后看了很多人的评论,也确实是求最大差的问题。也看到有人用到了动态规划,等学到了再刷本题。
public class Solution {
public int maxProfit(int prices[]) {
int minprice = Integer.MAX_VALUE;
int maxprofit = 0;
for (int i = 0; i < prices.length; i++) {
if (prices[i] < minprice) {
minprice = prices[i];
} else if (prices[i] - minprice > maxprofit) {
maxprofit = prices[i] - minprice;
}
}
return maxprofit;
}
}
还有一种相对来说比较容易理解,实时更新min和max的值,并且不会遗漏之类的,感觉这个很优。
class Solution {
public int maxProfit(int[] prices) {
int min = prices[0]; // 初始化最小值为第一天的价格
int max = 0; // 初始最大收益为0
for (int i = 1; i < prices.length; i++) {
min = Math.min(prices[i], min);
max = Math.max(max, prices[i] - min);
}
return max;
}
}
感觉到树和栈有了瓶颈,刷一刷前面的,缓一缓。
还刷了一天offer_11,并不是多难。
剑指 Offer 11. 旋转数组的最小数字
![](https://img-blog.csdnimg.cn/img_convert/212bbcd59356b48f4a37b3b1790daae5.png)
因为number本身升序排序,所以可以直接一遍比较元素大小。也是自己理解出来的,因为后面的那几个元素是小于等于第一个元素的,所以如果小于直接替换,如果不小于,就是和第一个相等,就直接输出target。
class Solution {
public int minArray(int[] numbers) {
int target=0;
for(int i=0;i<numbers.length;i++){
if(numbers[i]<numbers[target]){
target = i;
//当然,当找到之后,可以直接break,因为找到就是唯一。
}
}
return numbers[target];
}
}
当然最简单的就是排序,输出第一个元素了,取巧方法。
class Solution {
public int minArray(int[] numbers) {
Arrays.sort(numbers);
return numbers[0];
}
}
又学到了二分法,二分法当时确实没有想到,然后去理解,发现确实巧妙,让范围依次缩小,最后找到最小值。
class Solution {
public int minArray(int[] numbers) {
int i=0;
int j=numbers.length-1;
while(i<j){
int m = (i + j)/2;
if(numbers[m]>numbers[j]){i = m+1;}
else if(numbers[m]<numbers[j]){
j = m;
}
else {j--;}
}
return numbers[i];
}
}
然后又闲来无事,展开了else{j--;},这一展开倒是有了很多错误,然后修改,遇到了好多特殊情况,但是改来改去,还是和原代码差不多,所幸就这样了吧(懒)。
else if(numbers[j] == numbers[m])
{
j--;
}
else if(numbers[i] == numbers[m])
{
i++;
}