0
点赞
收藏
分享

微信扫一扫

LeetCode刷题笔记

Star英 2022-01-28 阅读 78

LeetCode刷题笔记

简单题

2022/1/25

1688. 比赛中的配对次数

给你一个整数 n ,表示比赛中的队伍数。比赛遵循一种独特的赛制:

  • 如果当前队伍数是 偶数 ,那么每支队伍都会与另一支队伍配对。总共进行 n / 2 场比赛,且产生 n / 2 支队伍进入下一轮。
  • 如果当前队伍数为 奇数 ,那么将会随机轮空并晋级一支队伍,其余的队伍配对。总共进行 (n - 1) / 2 场比赛,且产生 (n - 1) / 2 + 1 支队伍进入下一轮。

返回在比赛中进行的配对次数,直到决出获胜队伍为止。

示例 1:

输入:n = 7
输出:6
解释:比赛详情:
- 第 1 轮:队伍数 = 7 ,配对次数 = 3 ,4 支队伍晋级。
- 第 2 轮:队伍数 = 4 ,配对次数 = 2 ,2 支队伍晋级。
- 第 3 轮:队伍数 = 2 ,配对次数 = 1 ,决出 1 支获胜队伍。
总配对次数 = 3 + 2 + 1 = 6

示例 2:

输入:n = 14
输出:13
解释:比赛详情:
- 第 1 轮:队伍数 = 14 ,配对次数 = 7 ,7 支队伍晋级。
- 第 2 轮:队伍数 = 7 ,配对次数 = 3 ,4 支队伍晋级。 
- 第 3 轮:队伍数 = 4 ,配对次数 = 2 ,2 支队伍晋级。
- 第 4 轮:队伍数 = 2 ,配对次数 = 1 ,决出 1 支获胜队伍。
总配对次数 = 7 + 3 + 2 + 1 = 13

解题思路:

​ 每进行一场比赛都会淘汰一位,要产生冠军,就要淘汰掉 n-1个人。

代码

int numberOfMatches(int n){
    return n-1;
}

1. 两数之和

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

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

解题思路:暴力破解,逐个判断两数相加之和是否等于target。

代码

int* twoSum(int* nums, int numsSize, int target, int* returnSize){
    int i,j;
    int *result = (int *)malloc(sizeof(int)*2);
    *returnSize = 2;
    for(int i = 0; i < numsSize - 1; i++){
        for(int j = i + 1; j < numsSize; j++){
            if(nums[i] + nums[j] == target){
                result[0] = i;
                result[1] = j;
                return result;
            }
        }
    }
    return result;
}

9. 回文数

给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false

回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。

示例 1:

输入:x = 121
输出:true

示例 2:

输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。

示例 4:

输入:x = -101
输出:false

代码:

bool isPalindrome(int x){
    // 特殊情况:
        // 如上所述,当 x < 0 时,x 不是回文数。
        // 同样地,如果数字的最后一位是 0,为了使该数字为回文,
        // 则其第一位数字也应该是 0
        // 只有 0 满足这一属性
        if (x < 0 || (x % 10 == 0 && x != 0)) {
            return false;
        }

        int revertedNumber = 0;
        while (x > revertedNumber) {
            revertedNumber = revertedNumber * 10 + x % 10;
            x /= 10;
        }

        // 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
        // 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,
        // 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
        return x == revertedNumber || x == revertedNumber / 10;
}

2022/1/27

13. 罗马数字转整数

罗马数字包含以下七种字符: IVXLCDM

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。

示例 1:

输入: s = "III"
输出: 3

示例 2:

输入: s = "IV"
输出: 4

示例 3:

输入: s = "IX"
输出: 9

示例 4:

输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:

输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

解题思路

  • 正常情况下,左边的数值 > 右边数值,依次从左到右相加即可。
    • 例如 XXVII = X + X + V + I + I = 10 + 10 + 5 +1 + 1 = 27.
  • 若存在小的数字在大的数字的左边的情况,根据规则需要减去小的数字。对于这种情况,我们也可以将每个字符视作一个单独的值,若一个数字右侧的数字比它大,则将该数字的符号取反。
    • 例如 XIV 可视作 X - I + V =10−1+5=14。

代码

int romanToInt(char * s){
    int symbolValues[26];
    symbolValues['I' - 'A'] = 1;
    symbolValues['V' - 'A'] = 5;
    symbolValues['X' - 'A'] = 10;
    symbolValues['L' - 'A'] = 50;
    symbolValues['C' - 'A'] = 100;
    symbolValues['D' - 'A'] = 500;
    symbolValues['M' - 'A'] = 1000;

    int ans = 0;
    int length = strlen(s);

    for(int i = 0; i < length; ++i){
        int value = symbolValues[s[i] - 'A'];
        if(i < length-1 && value < symbolValues[s[i + 1] - 'A']){
            ans -= value;
        }
        else{
            ans += value;
        }
    }
    return ans;
}

14. 最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

示例 1:

输入:strs = ["flower","flow","flight"]
输出:"fl"

示例 2:

输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。

解题思路

  • 进行纵向对比,将数组中字符串的每一位和第一个字符串进行逐一比较,将不相等的那一位置’\0’,输出strs[0]即可。

代码

char * longestCommonPrefix(char ** strs, int strsSize){
    if(strsSize == 0){
        return NULL;
    }
    for(int i = 0; i < strlen(strs[0]); ++i){
        for(int j = 0; j < strsSize; j++ ){
            if(strs[j][i] != strs[0][i]){            //不相等时结束
                strs[0][i] = '\0';
                return strs[0];
            }
        }
    }
    return strs[0];      //第一个字符串是最长公共前缀
}

20. 有效的括号

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

示例 3:

输入:s = "(]"
输出:false

示例 4:

输入:s = "([)]"
输出:false

示例 5:

输入:s = "{[]}"
输出:true

解题思路

  • 判断括号的有效性可以使用「栈」这一数据结构来解决。

    我们遍历给定的字符串 ss。当我们遇到一个左括号时,我们会期望在后续的遍历中,有一个相同类型的右括号将其闭合。由于后遇到的左括号要先闭合,因此我们可以将这个左括号放入栈顶。

    当我们遇到一个右括号时,我们需要将一个相同类型的左括号闭合。此时,我们可以取出栈顶的左括号并判断它们是否是相同类型的括号。如果不是相同的类型,或者栈中并没有左括号,那么字符串 s无效,返回False。为了快速判断括号的类型,我们可以使用哈希表存储每一种括号。哈希表的键为右括号,值为相同类型的左括号。

    在遍历结束后,如果栈中没有左括号,说明我们将字符串 s中的所有左括号闭合,返回True,否则返回False。

    注意到有效字符串的长度一定为偶数,因此如果字符串的长度为奇数,我们可以直接返回False,省去后续的遍历判断过程。

代码

char pairs(char a) {
    if (a == '}') return '{';          //遇到右括号,返回左括号
    if (a == ']') return '[';
    if (a == ')') return '(';
    return 0;
}

bool isValid(char* s) {
    int n = strlen(s);
    if (n % 2 == 1) {                    //奇数一定不能匹配
        return false;
    }
    int stk[n + 1], top = 0;             //初始化栈
    for (int i = 0; i < n; i++) {
        char ch = pairs(s[i]);
        if (ch) {
            if (top == 0 || stk[top - 1] != ch) {    //栈空或者不匹配
                return false;
            }
            top--;
        } else {
            stk[top++] = s[i];       //入栈
        }
    }
    return top == 0;                 //全部出栈,即匹配
}

21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

img
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

解题思路:

  • 建立新的链表头结点
  • p1,p2,p3指向list1,list2,list3中元素
  • 若list1,list2其中之一为NULL,直接将其赋给list3
  • 都不为空则按大小将其依次链接到list3,最后将剩余的元素连接到list3的末尾

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* list3 = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode* p1 = list1;
    struct ListNode* p2 = list2;
    struct ListNode* p3 = list3;

    if(p1 == NULL)
        return list2;
    if(p2 == NULL)
        return list1;

    while(p1 && p2){
        if(p1->val < p2->val){
            p3->next = p1;
            p1 = p1 -> next;
        }
        else{
            p3->next = p2;
            p2 = p2 -> next;
        }
        p3 = p3 -> next;
    }
    if(p1 != NULL)
        p3->next = p1;
    else
        p3->next = p2;
    list3 = list3 -> next;
    return list3;
}

2022/1/28

26. 删除有序数组中的重复项

给你一个有序数组 nums ,请你** 原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [1,2]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

示例 2:

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

解题思路

  • 将数组中不重复的元素依次向前挪动,用delete记录需要删除的元素的个数
image-20220128111243285

代码

int removeDuplicates(int* nums, int numsSize){
    int delete = 0;           //记录删除元素个数
    for(int i = 1; i < numsSize; i++){
        if(nums[i - 1] == nums[i]){           //相邻两个元素相同,删除后者
             delete++;
        }
        else{
            nums[i - delete] = nums[i];       //将不重复元素挪动到正确位置
        }
    }
    return numsSize - delete;                //修改后数组元素的长度
}

27. 移除元素(做法同26题)

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

代码:

int removeElement(int* nums, int numsSize, int val){
    int delete = 0;
    for(int i = 0; i < numsSize; i++){
        if(nums[i] == val){
            delete++;
        }
        else{
            nums[i - delete] = nums[i]; 
        }
    }
    return numsSize - delete;
}

28. 实现 strStr()

实现 strStr() 函数。

给你两个字符串 haystackneedle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1

说明:

needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。

示例 1:

输入:haystack = "hello", needle = "ll"
输出:2

示例 2:

输入:haystack = "aaaaa", needle = "bba"
输出:-1

示例 3:

输入:haystack = "", needle = ""
输出:0

解法一:BF算法(简单模式匹配算法)

int strStr(char * haystack, char * needle){
    int i = 0, j = 0;

    if(strlen(needle) == 0){
        return 0;
    }
    
    while(i < strlen(haystack) && j < strlen(needle)){
        if(haystack[i] != needle[j]){
            i = i - j + 1;
            j = 0;

        }
        else{
            i++;
            j++;
        }
    }
    if(j >= strlen(needle))
        return i - strlen(needle);
    else
        return -1;
}

解法二:KMP算法

int strStr(char* haystack, char* needle) {
    int n = strlen(haystack), m = strlen(needle);
    if (m == 0) {
        return 0;
    }
    int pi[m];
    pi[0] = 0;
    for (int i = 1, j = 0; i < m; i++) {
        while (j > 0 && needle[i] != needle[j]) {
            j = pi[j - 1];
        }
        if (needle[i] == needle[j]) {
            j++;
        }
        pi[i] = j;
    }
    for (int i = 0, j = 0; i < n; i++) {
        while (j > 0 && haystack[i] != needle[j]) {
            j = pi[j - 1];
        }
        if (haystack[i] == needle[j]) {
            j++;
        }
        if (j == m) {
            return i - m + 1;
        }
    }
    return -1;
}

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4

示例 4:

输入: nums = [1,3,5,6], target = 0
输出: 0

示例 5:

输入: nums = [1], target = 0
输出: 0

解题思路:

  • 要求时间复杂度为 O(log n) ,采用二分查找实现

代码:

int searchInsert(int* nums, int numsSize, int target){
    int low = 0, high = numsSize - 1;
    while(low <= high){
        int mid = (low + high) / 2;
        if(nums[mid] == target)
            return mid;
        if(nums[mid] > target)
            high = mid - 1;
        else
            low = mid + 1;
    }
    return high + 1;
}

53. 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

输入:nums = [1]
输出:1

示例 3:

输入:nums = [5,4,-1,7,8]
输出:23

解法一:贪心算法

int maxSubArray(int* nums, int numsSize){      //贪心算法
    int cur_sum = nums[0], max_sum = nums[0] ;

    if(nums == NULL)
        return 0;
    
    for(int i = 1; i < numsSize; i++){
        if(nums[i] + cur_sum < nums[i]){       //当前位置之前的和小于0,丢弃之前项
            cur_sum = nums[i];
        }else{
            cur_sum += nums[i];                //大于0加上之前的和
        }
        if(cur_sum > max_sum)
            max_sum = cur_sum;
    }

    return max_sum;
}

解法二:动态规划

//若前一个元素大于0,加到当前元素上
int maxSubArray(int* nums, int numsSize){      //动态规划
    for(int i = 1; i < numsSize; i++){         //修改nums数组,前一个元素大于0,则加到当前元素
        if(nums[i - 1] > 0)
            nums[i] += nums[i - 1];
    }

    int max = nums[0];
    for(int j = 1; j < numsSize; j++){         //遍历数组,找出最大值
        if(nums[j] > max)
            max = nums[j];
    }

    return max;
}

58. 最后一个单词的长度

给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中最后一个单词的长度。

单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。

示例 1:

输入:s = "Hello World"
输出:5

示例 2:

输入:s = "   fly me   to   the moon  "
输出:4

示例 3:

输入:s = "luffy is still joyboy"
输出:6

解题思路:

  • 先从后往前逆序找到字符串中最后一个字母,该字母索引记为 pos
  • 从pos处从后往前找,直到第一次遇到空格为止,并依次记录单词中字母的个数,即最后一个单词长度。

代码:

int lengthOfLastWord(char * s){
    
    int pos = strlen(s) - 1;                  //记录从后起第一个不为空格的字母的位置
    for(int i = pos; i >= 0; i--){
        if(s[i] != ' '){
            pos = i;
            break;
        }
    }

    int length = 0;
    for(int i = pos; i >= 0; i--){             //计算最后一个单词长度
        if(s[i] != ' '){
            length++;
        }else{
            break;
        }
    }

    return length;
}
举报

相关推荐

0 条评论