day01 letcode9.买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 104
Code
class Solution {
public int maxProfit(int[] prices) {
if (prices.length <= 1) {
return 0;
}
int minPrices=prices[0];
int maxValue=0;
for (int i = 1; i <prices.length ; i++) {
maxValue=Math.max(maxValue,prices[i]-minPrices);
minPrices=Math.min(minPrices,prices[i]);
}
return maxValue;
}
}
day02 JZ42 连续子数组的最大和
描述
输入一个长度为n的整型数组a,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为 O(n).
提示:1 <= n <= 10^5
-100 <= a[i] <= 100
示例1
输入:[1,-2,3,10,-4,7,2,-5]
复制返回值:18
复制说明:经分析可知,输入数组的子数组[3,10,-4,7,2]可以求得最大和为18
示例2
输入: [2]
复制返回值:2
图解:动态规划
code (C++)
public:
int FindGreatestSumOfSubArray(vector<int> array) {
int now, ans;
now = 0, ans = INT_MIN;
for (int i = 0; i < array.size(); i++){
if (now < 0)//now<0,则舍去前面的
now = array[i];
else
{
now += array[i];//比0大则直接加上去
}
ans = max(now, ans);//更新ans
}
return ans;
}
day03 LC第547省份问题
描述
示例
解题思路
遍历所有城市,对于每个城市,如果该城市尚未被访问过,则从该城市开始深度优先搜索,通过矩阵 isConnected 得到与该城市直接相连的城市有哪些,这些城市和该城市属于同一个连通分量,然后对这些城市继续深度优先搜索,直到同一个连通分量的所有城市都被访问到,即可得到一个省份。遍历完全部城市以后,即可得到连通分量的总数,即省份的总数
代码
class Solution {
public int findCircleNum(int[][] isConnected) {
//城市数量
int length=isConnected.length;
//表示哪个城市被访问过
boolean[]visited=new boolean[length];//开始都为0,false
int count=0;//省份数量
for(int i=0;i<length;i++){
//如果当前城市未被访问过,就说明是个新省分,count+1,
//并且和这个城市相连的都标记为访问过的,也就是统一省分的
if(!visited[i]){//先是第i个城市未访问,然后进入递归,去找下一个
dfs(isConnected,visited,i);
count++;
}
}
return count;
}
public void dfs(int[][] isConnected,boolean[]visited,int i){
for(int j=0;j<isConnected.length;j++){
if(isConnected[i][j]==1&&!visited[j]){//循环条件
//如果第i和第j个城市是相连的且第j个城市未访问,说明他们是同一个省份,把它标记为已访问过
visited[j]=true;
//然后继续查找与第j个城市相连的城市
dfs(isConnected,visited, i); //结合for循环就是等价表达式了
}
}
}
}
day04 JZ25 合并两个排序的链表
描述
输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围: 0≤ n ≤ 1000,-1000 ≤节点值 ≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
示例1
输入:{1,3,5},{2,4,6}
复制返回值:{1,2,3,4,5,6}
思路
方法一:迭代版本求解
初始化:定义cur指向新链表的头结点
操作:
- 如果l1指向的结点值小于等于l2指向的结点值,则将l1指向的结点值链接到cur的next指针,然后l1指向下一个结点值
- 否则,让l2指向下一个结点值
- 循环步骤1,2,直到l1或者l2为nullptr
- 将l1或者l2剩下的部分链接到cur的后面
代码:
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
ListNode *vhead = new ListNode(-1);
ListNode *cur = vhead;
while (pHead1 && pHead2) {
if (pHead1->val <= pHead2->val) {
cur->next = pHead1;
pHead1 = pHead1->next;
}
else {
cur->next = pHead2;
pHead2 = pHead2->next;
}
cur = cur->next;
}
cur->next = pHead1 ? pHead1 : pHead2;
return vhead->next;
}
};t;
方法二:递归版本
写递归代码,最重要的要明白递归函数的功能。可以不必关心递归函数的具体实现。
比如这个ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
函数功能:合并两个单链表,返回两个单链表头结点值小的那个节点。
如果知道了这个函数功能,那么接下来需要考虑2个问题:
- 递归函数结束的条件是什么?
- 递归函数一定是缩小递归区间的,那么下一步的递归区间是什么?
对于问题1.对于链表就是,如果为空,返回什么
对于问题2,跟迭代方法中的一样,如果PHead1的所指节点值小于等于pHead2所指的结点值,那么phead1后续节点和pHead节点继续递归
代码:
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if (!pHead1) return pHead2;
if (!pHead2) return pHead1;
if (pHead1->val <= pHead2->val) {
pHead1->next = Merge(pHead1->next, pHead2);
return pHead1;
}
else {
pHead2->next = Merge(pHead1, pHead2->next);
return pHead2;
}
}
};
方法三:非递归
代码:
public class Solution {
public ListNode Merge(ListNode list1, ListNode list2) {
if (list1 == null) { // 特判
return list2;
} else if (list2 == null) {
return list1;
}
ListNode list = null; // 使list1指向头结点为最小值的链表,list1也是最终要返回的链表
if (list1.val > list2.val) {
list = list1;
list1 = list2;
list2 = list;
}
list = list1;
while (list1.next != null && list2 != null) { // list1.next!=null 很巧妙,而不是list1!=null
if (list1.next.val <= list2.val) {
list1 = list1.next;
} else {
// 图解12345
ListNode nex = list1.next; // 1
list1.next = list2; // 2
list2 = list2.next; // 3
list1 = list1.next; // 4
list1.next = nex; // 5
}
}
if (list2 != null) { // 最后只关心list2不为空
list1.next = list2;
}
return list;
}
}
day 4 只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1: 输入: [2,2,1] 输出: 1
思路:
方法1,通过双层for循环+计数器,得到count为初始值的数,返回对应的数值
方法2:使用Set集合存储,Set集合不存储重复值,add()方法返回值为boolean类型,这一特点可以利用。当要添加的数与集合中已存在的数重复时,不会再进行添加操作,返回false,这时再进行remove操作,将集合中已存在的那个与要添加的数相同的元素移除
代码实例:
方法1:
public static Integer Find_Num_1(int[] arr){
for(int i = 0; i < arr.length; i++) {
int count = 1;//计数器
for(int j = 0; j < arr.length; j++) {
if(i == j)
continue;//下标相等则直接进入下一次循环
if(arr[i] == arr[j])
count++;//如果不同下标的数组元素相等,则计数器加1
}
if(count == 1)
return arr[i];//返回只出现了一次的元素
}
return null;//找不到则返回null
}
方法2:
public static Integer Find_Num_2(int[] arr) {
Set<Integer> set = new HashSet<Integer>();//创建Set集合
for(int i : arr) {//增强for循环遍历数组
if(!set.add(i))//添加不成功返回false,前加上!运算符变为true
set.remove(i);//移除集合中与这个要添加的数重复的元素
}
if(set.size() == 0) return null;
//如果Set集合长度为0,返回null表示没找到
return set.toArray(new Integer[set.size()])[0];
}
day 5 加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
题解:
- 如果数组中的所有元素都是9,类似9999,加1之后肯定会变为10000,也就是数组长度会增加1位。
- 如果数组的元素只要有一个不是9,加1之后直接返回即可
代码:
class Solution {
public int[] plusOne(int[] digits) {
int length = digits.length;
for(int i =length-1;i>=0;i--){
if(digits[i] != 9){
digits[i]++;
return digits;
}else{
digits[i] =0;
}
}
int temp[] = new int [length +1];
temp[0] = 1;
return temp;
}
}
移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入:[0,1,0,3,12]
输出:[1,3,12,0,0]
说明:
- 必须在原数组上操作,不能拷贝额外的数组。
- 尽量减少操作次数。
代码:
class Solution {
public void moveZeroes(int[] nums) {
if(nums == null || nums.length == 0)
return;
int index = 0;
for(int i =0;i< nums.length;i++){
if( nums[i] != 0){
nums[index++] = nums[i];
}
}
//后边均是0
while(index < nums.length){
nums[index++] = 0;
}
}
}