元某人也元某人也

LeetcodeHot100

LeetcodeHot100学习心得会有动画更新

本文目录

背景

正文

题70、爬楼梯

题目

假设你正在爬楼梯。需要爬 n 阶才能到达楼顶。每次只能爬 1 或 2 个台阶。有多少种不同的爬楼方法?

思路

根据爬楼梯的个数可以列出来公式

示意图:

解法

递归的解法

1
2
3
4
5
6
7
public class Solution {
    public int ClimbStairs(int n) {
        if (n == 1) return 1;//这两条为终止条件
        if (n == 2) return 2;
        return ClimbStairs(n - 1) + ClimbStairs(n - 2);
    }
}

时间复杂度为O(2^n),由于每一个值都要进行计算,会产生多余的计算

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Solution {
    private Dictionary<int, int> cache = new Dictionary<int, int>();
    
    public int ClimbStairs(int n) {
        if (n == 1) return 1;
        if (n == 2) return 2;
        if (cache.ContainsKey(n))
            return cache[n];
        
        int res = ClimbStairs(n - 1) + ClimbStairs(n - 2);
        cache[n] = res;
        return res;
    }
}
时间复杂度

O(n),使用字典将计算得到的值存入字典中,下一次的时候直接拿取想要的值

非递归解法:循环解法,自下而上的累加

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class Solution {
    public int ClimbStairs(int n) {
        if(n == 1) return 1;
        if(n == 2) return 2;
        int result = 0;
        int pre = 2;
        int prePre = 1;
        //循环从3开始,结果为前两个结果相加的集合
        for (int i = 3; i <= n; ++i) {
            result = pre + prePre;
            //更新值,每次向上移动一位
            prePre = pre;
            pre = result;
        }
        return result;
    }
}
时间复杂度

O(n)

题1、两数之和

题目

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

思路

暴力法解题思路

将数组中的所有元素的和都算一遍,

字典一遍遍历

先去寻找这个数字对于目标数所需要的数字,去字典中寻找一遍,如果没有则把当前的数字,键为数字,值为下标存入字典中,既保证了字典中到最后需要输出的数组元素,按照顺序排列,值即为数组下标,又保证了不会出现一个元素使用两次的情况

解法

暴力双层循环遍历

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Solution {
    public int[] TwoSum(int[] nums, int target) {
        for (int i = 0; i < nums.Length; i++)
        {
            for (int j = i + 1; j < nums.Length; j++)
            {
                if (nums[i] + nums[j] == target)
                {
                    return new int[] {i, j};
                }
            }
        }
        // 题目保证一定有解,此行不会执行
        return new int[0];
    }
}
时间复杂度

O(n^2)

字典一遍遍历

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Solution {
    public int[] TwoSum(int[] nums, int target)
    {
        // key:数组数值,value:数值对应的下标
        Dictionary<int, int> hashDict = new Dictionary<int, int>();

        for (int i = 0; i < nums.Length; i++)
        {
            int current = nums[i];
            // 需要寻找的互补数字
            int complement = target - current;

            // 如果字典里存在互补数,直接返回两个下标
            if (hashDict.ContainsKey(complement))
            {
                return new int[] { hashDict[complement], i };
            }

            // 当前数字存入字典,供后续元素查找
            hashDict[current] = i;
        }

        // 题目保证必有一组解,理论不会走到这里
        return new int[0];
    }
}
时间复杂度

O(n),每个元素最多一次插入,一次查询

使用数组的值作为Key的原因:

我们要用的是比较谁为合适的相加为目标值的元素,到最后需要返回的是下标,也就是知道了数组的值去找下标,所以应当以数组的值作为Key

补充:ContainsKey()平均时间复杂度为O(1),

ContainsValue ()需要遍历整个数组,时间复杂度为O(n)

题88、合并两个有序数组

题目

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意: 最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了适应这种情况,nums1 的初始长度是 m + n,其中前 m 个元素是需要合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n。

思路

直接将数组2合并到数组1后面,然后进行排序

Array.sort(),的根本是快速排序,时间复杂度为O(n)

顺序双指针,递增排列

逆序双指针遍历,递减排列

解法

先合并后排序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class Solution {
    public void Merge(int[] nums1, int m, int[] nums2, int n) {
        // 第一步:将nums2全部复制到nums1尾部
        for (int i = 0; i < n; i++)
        {
            nums1[m + i] = nums2[i];
        }
        // 第二步:整体排序
        Array.Sort(nums1);
    }
}

时间复杂度是O(M+N)

双指针遍历

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Solution {
    public void Merge(int[] nums1, int m, int[] nums2, int n)
    {
        int k = m + n;
        int[] temp = new int[k];

        for (int index = 0, nums1Index = 0, nums2Index = 0; index < k; index++)
        {
            if (nums1Index >= m)
            {
                // nums1有效元素取完,直接取nums2
                temp[index] = nums2[nums2Index++];
            }
            else if (nums2Index >= n)
            {
                // nums2取完,直接取nums1
                temp[index] = nums1[nums1Index++];
            }
            else if (nums1[nums1Index] < nums2[nums2Index])
            {
                temp[index] = nums1[nums1Index++];
            }
            else
            {
                temp[index] = nums2[nums2Index++];
            }
        }

        // 将临时数组拷贝回nums1
        for (int i = 0; i < k; i++)
        {
            nums1[i] = temp[i];
        }
    }
}

时间复杂度为O(M+N)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Solution {
    public void Merge(int[] nums1, int m, int[] nums2, int n)
    {
        int p1 = m - 1;
        int p2 = n - 1;
        int tail = m + n - 1;

        // 两个数组都还有元素时
        while (p1 >= 0 && p2 >= 0)
        {
            if (nums1[p1] > nums2[p2])
            {
                nums1[tail] = nums1[p1];
                p1--;
            }
            else
            {
                nums1[tail] = nums2[p2];
                p2--;
            }
            tail--;
        }

        // 如果nums2还有剩余元素,直接拷贝到nums1前方
        while (p2 >= 0)
        {
            nums1[tail] = nums2[p2];
            p2--;
            tail--;
        }
    }
}

复盘

仅用于交流学习,记录学习心得