Leet Code 刷题记录【1】

20.有效的括号

用到的数据结构:链表实现的栈
参考:C语言—堆栈(链表实现) - changfan - 博客园
舍友看到我这一大堆栈的实现惊了,说你机试的时候真的有这么多时间写C吗?于是去看了看C++的写法——短时间里不能很熟练运用java,就先上C++吧。下面是几个需要学习的Part:

关于map的使用
在C里面,我是对着ascii码表看的,或者直接比较,而在C里面可以用Map做一个一对一的hash映射。参考:
C++中map的使用_u011555996的博客-CSDN博客_c++ map
map与unordered_map的区别
.empty()
判断容器是否为空。
需要注意的是,在stack.top()顶端元素时,stack不可以为空。
.size()
在获取字符串长度时,size()函数与length()函数作用相同。 除此之外,size()函数还可以获取vector类型的长度。

21. 合并两个有序链表

我自己写的方法:

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


struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    struct ListNode *p1 = l1;
    struct ListNode *q1 = l2;
    struct ListNode *cur = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode *result = cur;
    int val1 = 0;
    int val2 = 0;

    while((p1 != NULL)||(q1 != NULL))
    {
        val1 = (p1 != NULL)?p1->val:32767;
        val2 = (q1 != NULL)?q1->val:32767;
        cur -> next = (struct ListNode*)malloc(sizeof(struct ListNode));
        cur = cur->next;
        if(val1 <= val2)
        {
            cur->val = val1;
            p1 = p1->next;
        }
        else
        {
            cur->val = val2;
            q1 = q1->next;
        }
    }
    while(p1 != NULL)
    {
        cur -> next = (struct ListNode*)malloc(sizeof(struct ListNode));
        cur = cur->next;
        cur->val = p1->val;
        p1 = p1->next;
    }
    while(q1 != NULL)
    {
        cur -> next = (struct ListNode*)malloc(sizeof(struct ListNode));
        cur = cur->next;
        cur->val = q1->val;
        q1 = q1->next;
    }
    cur->next = NULL;
    return result->next;
}

看到一种递归的写法:

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
    if (!l1)        //如果l1为空
		return l2;
	if (!l2)        //如果l2为空
		return l1;
	if (l1->val < l2->val){
		l1->next = mergeTwoLists(l1->next, l2);
		return l1;
	}
	else{
		l2->next = mergeTwoLists(l1, l2->next);
		return l2;
	}
}

两种方法的运行时间和内存消耗差不多,递归的不好想但是写起来更简洁。

35. 搜索插入位置

在这里学习一下C++的数组。
C++ vector& nums 用法一_年年のBlog-CSDN博客_vector& nums
线性搜索:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int i;
        for(i=0 ; i<nums.size() ; i++)
        {
            if(nums[i] >= target)
                return i;
        }
        return i;
    }

38. 外观数列

不知道为啥没看到我这种写法
双100%
Leet Code 刷题记录【1】_第1张图片
递归快乐!

char * countAndSay(int n){
    if(n == 1)
        return "1";
    char *str = countAndSay(n-1);
    char *nstr = malloc(5000);		//n<30 最大需要5000长度的字符串
    int i = 0,len = strlen(str);
    int count = 1,j = 0;
    char key,num;
    while(i < len)
    {
        key = str[i];		//下面统计key出现的次数
        i++;
        while(str[i] == key && i<len)
        {
            i++;
            count++;
        }
        num = count+48;		//共出现了count次,转换为字符,'0'的ascii码是48
        nstr[2*j] = num; 
        nstr[2*j+1] = key;
        count = 1;
        j++;
    }
    nstr[2*j] = '\0';		//没有初始化过,堵上,不然会乱码
    return nstr;
}

用C++写的话,是一样的逻辑,但是就慢了很多……没有学过C++的程序优化,所以不太清楚是哪里慢了。不过拼接字符串是很爽的事情。

class Solution {
public:
    string countAndSay(int n) {
        if(n == 1)
            return "1";
        string str = countAndSay(n-1);

        char key;
        int count = 1;
        int i = 0,len = str.length();
        string nstr = "";
        while(i<len)
        {
            key = str[i];
            i++;
            while(i<len && str[i] == key)
            {
                count++;
                i++;
            }        
            nstr += to_string(count) + key;
            count = 1;
        }
        return nstr;
    }
};

53. 最大子序和

这一题有大神写了很棒的题解:题解
法1:动态规划
用dp[i]表示以nums[i]结尾的最大子序和。
dp[0] = nums[0];
dp[i] = max{ dp[i-1]+nums[i] , nums[i] };
当然也可以只记录最大dp值,这样就可以不用存整个dp数组,空间上更小。
这里又用到了一个很好用的库.max()

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int numsize = nums.size();
        int dp = nums[0];
        int result = dp;
        int i;
        for(i=1 ; i<numsize ; i++)
        {
            dp = max(dp+nums[i],nums[i]);
            result = max(dp,result);
        }
        return result;
    }
};

法2:贪心法
贪心算法不一定能找到最优解。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int numsize = nums.size();
        int sum = 0;
        int result = INT_MIN;
        int i;
        for(i=0 ; i<numsize ; i++)
        {
            sum += nums[i];
            result = max(result,sum);
            if(sum < 0)
                sum = 0;
        }
        return result;
    }
};

别的方法就有些复杂,对这道题不是很适用了。

66. 加一

一开始是这样写的:

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int len = digits.size();
        int i,carry = 0;
        vector<int> res;
        
        for(i=len-1 ; i>=0 ; i--)
        {
            if(i == len-1)
                digits[i]++;
            digits[i] += carry;
            carry = (digits[i])/10;
            res.insert(res.begin(),digits[i]%10);
        }
        if(carry)
            res.insert(res.begin(),1);
        return res;
    }
};

然后我被自己的内存消耗暴击了……后面没有单独声明数组res,差别不大。
这里注意一个写法:在数组头部插入res.insert(res.begin(),num);
然后发现大家在进位这件事上做文章。从后向前遍历,置所有的9为0,将第一个不为9的数+1.
特殊情况下,9999……99999,则需要在最前面补1.

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int len = digits.size();
        int i;
        
        for(i=len-1 ; i>=0 ; i--)
        {
            if(digits[i] == 9)
                digits[i] = 0;
            else
            {
                digits[i]++;
                return digits;
            }
        }
        if(i < 0)
            digits.insert(digits.begin(),1);
        return digits;
    }
};

然后我仍旧被内存消耗暴击,甚至比之前还多……不知道为什么别人这样写就是双百。

69. x 的平方根

二分法啦

class Solution {
public:
    int mySqrt(int x) {
        if(x == 1 || x == 0)
            return x;
        int min = 0, max = x;
        int res,last = 0;
        while(true)
        {
            res = (min+max)/2;
            if(last == res)
                return res-1;
            if(x/res < res)
                max = res;
            else
                min = res+1;
            last = res;
        }
    }
};

需要注意的是,为了避免乘法溢出,我们使用除法来判断大小。
k = (min+max)/2;
如果k大了,到(min,k)里去找
如果k小了,到(k+1,max)里去找
结果偏大,-1.

70. 爬楼梯

这真的不是在考我小学奥数?
首先要理解这个题目,得到一个动态规划的式子:
f(x) = f(x-1)+f(x-2);
即,爬x阶楼梯的方法数 = 爬x-1阶的方法数+爬x-2阶的方法数
因为倒着想,爬到x阶前,你可以爬1阶,也可以爬2阶。如果是爬1阶,那么你已经爬了x-1阶;如果是爬2阶,那么你已经爬了x-2阶。
写到这里我已经不认识爬字了。
好了,理解了这个之后,我们就可以得到一个斐波那契数列:1,2,3,5,8,13……
所以,这就是求斐波那契数列的一个题,understand?
而在理解之前,我一直尝试用排列组合求解(跪)

解斐波那契数列有两种比较容易想到的方法:1.递归;2.滑动窗口
在上C语言课的时候,我们都在这里学习了递归的思想,然而实际上用递归的话,函数套函数,卷卷卷卷卷,到n = 44就会超时。所以标答给的是滑动窗口的解法。下面是我自己根据滑动窗口写的答案,双百,时间复杂度是O(n).

class Solution {
public:
    int climbStairs(int n) {
        if(n == 1)
            return 1;
        else if(n == 2)
            return 2;
        int a = 1, b = 2, c;
        int i;
        for(i=3;i<n+1;i++)
        {
            switch(i%3){
                case 0:
                    c = a+b;
                    break;
                case 1:
                    a = c+b;
                    break;
                case 2:
                    b = a+c;
                    break;
            }
        }
        i = max(a,b);
        return max(i,c);
    }
};

当然斐波那契数列由来已久,大家闲得发慌,给出了几种时间复杂度O(logn)的解法。至此,这道题已经从小学奥数题变成了考研数学题,需要用到一系列线代知识和高数知识。本来我买考研数学辅导书回来只是想回忆一下这些知识,还被一众小伙伴嘲笑浪费钱,但是事实证明,大学里学的数学真的很重要!!!
官方解答

你可能感兴趣的:(刷题吭吭,c++,leetcode)