121、 Best Time to Buy and Sell Stock
三十一、题目
给出一个股票的价格序列,买入一次卖出一次,求能获得最大利润。其实就是求一个数组中,后面的数减去前面的数能得到的最大值。最容易想到的肯定是每次选一个数,遍历后面的数,求出直接的差然后和当前最大值进行比较,这样时间复杂度为O(n*n),不推荐。
思路:
找到当前遍历索引(包括当前索引)中的最小值min,然后用当前值减去这个最小值,最后跟我们一开始设置的maxProfit=0 进行比较取大的那一个,那么最终的maxProfit一定大于0,要么等于0。
Example 1:
Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Not 7-1 = 6, as selling price needs to be larger than buying price.
Example 2:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
package cn.hnu.leetcode.easy;
public class _121_MaxProfit {
public int maxProfit(int[] prices) {
//如果数组是空或者数组的长度小于2,说明数组里面只有一个或者根本没有元素,直接返回0
//因为不可能当天买当天就卖掉
if(prices == null||prices.length<2){
return 0;
}
//将最大利润设置成0
int maxProfit = 0;
//整个数组中的最小值姑且就看成数组的第一个元素
int min = prices[0];
int len = prices.length;
//从数组的第二个元素开始遍历
for(int i = 1;i<len;i++){
//找到当前遍历的索引之前的最小的元素
min = Math.min(prices[i] , min);
//用当前的位置的元素减去上面获得的"最小元素",然后跟上一次设置的最大利润比较
//将最大利润重新设置
maxProfit = Math.max(prices[i] - min , maxProfit);
}
//最后返回
return maxProfit;
}
}
122、Best Time to Buy and Sell Stock II
三十二、题目
假设有一个数组,其中数组的第i个元素是第i天给定股票的价格。
设计算法以找到最大利润。 假设可以根据需要完成尽可能多的交易(即,多次买入并卖出一股股票)。
注意:您不得同时进行多笔交易(即,必须在再次购买之前卖出股票)
思路:
因为可以连续买入或者卖出,所以只要卖出点比买出点高,就可以卖出获取利润,将利润累加即可。但给出的例子里面有一个【1,2,3,4,5】获取利润是5-1=4,而不是(2-1)+(3-2)+(4-3)+(5-4)= 4.
Example 1:
Input: [7,1,5,3,6,4]
Output: 7
Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4.
Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3.
Example 2:
Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are
engaging multiple transactions at the same time. You must sell before buying again.
Example 3:
Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
package cn.hnu.leetcode.easy;
public class _122_MaxProfit {
public int maxProfit(int[] prices) {
int len = prices.length;
//如果数组的长度小于等于1,那么也就没有所谓的买入与卖出
if(len<=1){
return 0;
}
int i = 0;
int total = 0;
//使用局部最低点作为买入点,局部最高点为卖出点
while(i<len){
int buy,sell;
while(i+1<len&&prices[i+1]<prices[i]){
i++;
}
//设置买入点
buy = i;
//买入与卖出不能是同一天
i++;
//上面做了i++,那么下面这个循环首先要判断的是是否已经到了数组的最后
//这里用数组【1 2 3 4】想
while(i<len&&prices[i]>=prices[i-1]){
i++;
}
//设置卖出点
sell = i-1;
//总利润累加
total += prices[sell] - prices[buy];
}
return total;
}
}
125、Valid Palindrome
二十三、题目
给定一个字符串,确定它是否是回文,本题只考虑字母和数字字符并忽略字母大小写。
注意:空字符串定义为有效的回文。
思路:
将字符串中的字符全转换成小写,并将字符串转换成字符数组,然后定义左右指针分别指向头和尾,忽略字符数组中间不是0到1和a到z的字符,比较指针指向的字符是否相等,当left>right循环结束,还没返回false,那么就是一个回文字符串。
Example 1:
Input: "A man, a plan, a canal: Panama"
Output: true
Example 2:
Input: "race a car"
Output: false
package cn.hnu.leetcode.easy;
public class _125_IsPalindrome {
public boolean isPalindrome(String s) {
//将给定字符串中的所有字母都改成小写
char[] chars = s.toLowerCase().toCharArray();
//如果字符串为空或者字符串的长度等于1,则一定是回文字符串
if(s==null||s.length()==1){
return true;
}
//双指针问题,一个指向头,一个指向尾
int left = 0;
int right = chars.length-1;
//只要左边的指针位置小于右边,那么一直遍历
while(left<right){
//看看左边,如果不在a-z或者0-9之间,左指针右移
if(!((chars[left]>='a'&&chars[left]<='z')||(chars[left]>='0'&&chars[left]<='9'))){
left++;
//看看右边,如果不在a-z或者0-9之间,右指针左移
}else if(!((chars[right]>='a'&&chars[right]<='z')||(chars[right]>='0'&&chars[right]<='9'))){
right--;
//如果左指针指向的值等于右指针指向的值,左右指针分别右移和左移
}else if(chars[left]==chars[right]){
left++;
right--;
//否则的话就返回false
}else{
return false;
}
}//end while
//while结束都没返回false,那么就是回文字符串了
return true;
}
}
136、Single Number
二十四、题目
给定一个非空的整数数组,除了一个元素外,每个元素都会出现两次。找到这个单一元素
注意:
算法应具有线性运行时间复杂度,能不用额外的内存来实现吗?
思路:
思路一:将数组中的元素以键值对的形式存在map里面,然后数组中每出现相同的数,那么键所对应的值就+1,最终只要返回map集合中值为1的那个key即可
思路二:利用异或操作,n ^ 0 = n ; n ^ n = 0 ;也就是说如果数组中出现同样的数,那么就返回0,如果不存在那就返回这个数;其实就是位运算,而且这个数组指明了除了其中一个数,其它数字都出现两次,实际上就是让用异或运算。而且异或运算满足:
交换律:a ^ b = b ^ a
结合律:a ^ b ^ c = (a ^ b) ^ c = a ^ (b ^ c)
其它:a ^ b ^ a = b ; a ^ a = 0 ; a ^ 0 = 0; (就是因为只有一个单数字,其它都是两两相同,那么就可以利用上面的法则转换成这种形式,得到那个最终的result)
Example 1:
Input: [2,2,1]
Output: 1
Example 2:
Input: [4,1,2,1,2]
Output: 4
方法一:
package cn.hnu.leetcode.easy;
import java.util.HashMap;
import java.util.Map;
public class _136_SingleNumber {
public int singleNumber(int[] nums) {
//创建一个map存储键值对,键表示数组中出现的数字
HashMap<Integer, Integer> map = new HashMap<>();
//因为数组非空,所以如果是数组长度是1,直接返回第一个元素即可
if(nums.length==1){
return nums[0];
}
int len = nums.length;
//遍历数组放入map集合中
for(int i = 0;i<len;i++){
Integer count = map.get(nums[i]);
if(count==null){
count = 0;
}
map.put(nums[i], count+1);
}
int result = -1;
//最终只要获取map中值为1的元素就完成了本题
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if(entry.getValue()==1){
result = entry.getKey();
break;
}
}
return result;
}
}
方法二:
package cn.hnu.leetcode.easy;
public class _136_SingleNumber {
public static void main(String[] args) {
int[] arr = new int[]{4,2,1,2,1};
System.out.println(singleNumber(arr));
}
public static int singleNumber(int[] nums) {
//如果数组为空或者数组长度小于1,返回-1
if(nums==null||nums.length<1){
return -1;
}
int result = 0;
//利用异或操作,0 ^ n = n ; n ^ n = 0
for(int i = 0;i<nums.length;i++){
//System.out.println(result);
result = result ^ nums[i];
}
return result;
}
}
141、Linked List Cycle
二十五、题目
给定一个链表,确定它是否有一个循环。
思路:
本题看半天没看懂题目意思,其实就是给定一个链表,也不知道这个链表长啥样,问这个链表是不是有环,可以把链表想象成操场,有两个孩子在围着操场跑,一个速度慢一个速度快,如果操场是飞机场,那是直道,假设无限长,两个小孩跑死也不会相遇,如果是环,那么不管这个换多大,一个速度快,一个速度慢,总有一天会相遇,本题就是利用这种思想,快慢指针完成本题。详见代码。
Example 1:
Input: head = [3,2,0,-4], pos = 1
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the second node.
Example 2:
Input: head = [1,2], pos = 0
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the first node.
Example 3:
Input: head = [1], pos = -1
Output: false
Explanation: There is no cycle in the linked list.
package cn.hnu.leetcode.easy;
import cn.hnu.leetcode.easy.use_Class.ListNode;
public class _140_HasCycle {
public boolean hasCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
//前兩個条件好理解,最后一个是因为快指针每次移动两步,我得知道是否为空
//如果为空,说明就是一个直链,没有环路啦
while(fast!=null&&slow!=null&&fast.next!=null){
//快指针每次移动两步(可以是任意,但是不同需要改变while循环条件)
fast = fast.next.next;
//慢指针每次移动一步
slow = slow.next;
//如果两个指针相遇,说明有环,返回true
if(fast == slow){
return true;
}
}//end while
//真个while结束无欢就无欢了,返回false
return false;
}
}