letcode算法题集锦

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指向新链表的头结点
操作:

  1. 如果l1指向的结点值小于等于l2指向的结点值,则将l1指向的结点值链接到cur的next指针,然后l1指向下一个结点值
  2. 否则,让l2指向下一个结点值
  3. 循环步骤1,2,直到l1或者l2为nullptr
  4. 将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. 递归函数一定是缩小递归区间的,那么下一步的递归区间是什么?
    对于问题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]

说明:

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。

代码:

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;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值