LeetCode

前言

前一段时间在刷leetcode的习题,刷了100道题,但是并没有很深刻的感觉(可能是因为自己陷入了为了刷题而刷题的状态中,忘了思考了)。按照自己一贯做事的方案,如果没有很深刻的感觉,那就适时的进行总结。回顾曾走过的路,让自己走的更踏实些。

我准备对所刷题目的解答进行汇总。对每一个题目我首先提供我自己的一些解法思路,也有有可能加入一些别人精彩的思路。考虑到工程量的浩大,可能并不会事事巨细。这个仅仅是我自己的浅薄的总结,不到之处,还请大家批评指正。

为了阅读方便,我将按照题目编号顺序写出解答。注意:这些解答在leetcode上通过,但并不表明它们是没有bug的,如果大家发现了bug,欢迎大家指正。

对于每一个题目,我将按照如下的模式进行阐述。

  1. 题目编号. 标题
  2. 题目内容
  3. 题意: 中文简单分析
  4. 思路: 1. 2. 3. …
  5. 代码: 简单想法+代码
  6. 注意: 额外的注意事项

1. Two Sum

Given an array of integers, return indices of the two numbers such that they add up to a specific target.

You may assume that each input would have exactly one solution.

Example:
Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

题意:给定一个int型数组以及一个target,得到两个元素的下标使得这两个元素的和等于target。

思路:

  1. n个元素,两两一组,一共有n*(n-1)/2组,可在O(n^2)的时间复杂度上得出结果。
  2. 更优一点的方案,首先进行排序,然后对n个元素,按顺序选择。如果选择的某一个元素array[i],且i为结果,那么target-array[i]也必在数组中。因此将问题转换为查找target-array[i]的问题,由于已排序,那么可采用二分查找。总的时间复杂度为排序时间+逐个尝试并查找时间 = O(nlgn) + O(nlgn) = O(nlgn).
  3. 上面其实给出了另外的hash的思路。首先将数组中的所有元素存入hash表中,然后选择数组的一个元素array[i],判断target-array[i]是否也在数组中,这相当于判断是否在hash表中。总的时间复杂度为O(n) + O(n) = O(n).
  4. 我并没有想到其他更优的方案,因为根据直觉来看,至少需要O(n)的时间复杂度,因为任何一个元素都有可能成为结果,我们至少对每个元素要查看一次。

代码:
只给出hash思想的代码。首先对所有的元素构建hash,然后查找target-array[i]在hash表中是否存在。同时为了返回结果中的索引,在存入hash的时候同时存入元素的索引。

vector<int> twoSum(vector<int>& nums, int target) {
    unordered_map<int, int> umi;
    for(int i = 0; i < nums.size(); ++i){
        umi[nums[i]] = i;
    }
    for(int i = 0; i < nums.size(); ++i){
        auto it = umi.find(target-nums[i]);
        if(it != umi.end() && it->second != i){
            return {i, it->second};
        }
    }
    return {};
}

注意:
上面的代码有一个小的技巧。为什么要判断it->second != i? 考虑array = [2 2 3], target=4: 这样hash表中只有一个元素,当find(target-nums[i])的时候,肯定会找到2,但是这个2可能是nums[i]对应的索引,而不是target-nums[i]对应的索引。

2. Add Two Numbers

You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.

Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8

题意: 两个非负数相加求和。与普通的求和不一样的是,这两个数是由链表存储的。例如上面的342+465=807,并将结果也存入链表中返回。

思路:
1. 遍历链表,分别得到两个int变量,然后相加,并将结果转换为链表
2. 两个链表同时遍历,遍历的过程就是累加的过程。由于链表本身存储就是先低位再高位的形式,因此可以直接相加求和即可,注意进位。

代码: 两个链表同时遍历,相加,进位,同时注意链表长度可能不一样,先到尾部的链表相当于后面补0.也就是说342+65 = 342 + 065 = 407

ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
    ListNode tmp(0);
    ListNode *p = &tmp;

    int high = 0;
    while(l1 || l2){
        int cur = ((l1) ? l1->val : 0) + ((l2) ? l2->val:0) + high;
        if(cur >= 10){
            high = 1;
            cur -= 10;
        }else{
            high = 0;
        }

        ListNode* q = new ListNode(cur);
        p->next = q;
        p = q;

        if(l1)
            l1 = l1->next;
        if(l2)
            l2 = l2->next;
    }
    if(high){
        p->next = new ListNode(high);
    }
    return tmp.next;
}

注意: 上面代码有两个讨巧之处。 第一个是tmp节点的使用,这个是额外的链表头节点,利用这个节点可以减少head==NULL这样的判断; 第二个是(l1) ? l1->val : 0这样的用法,如果节点有效,则使用其val,否则使用0,这样就可以不同长度的两个链表的操作统一化了。当然也可以不这样做,当其中某一个链表为空的时候,就停止while循环,然后将不空的链表直接接到已经求得的链表尾部即可,特别类似于两个有序链表的合并问题。

6. ZigZag Conversion

The string “PAYPALISHIRING” is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)

P   A   H   N
A P L S I I G
Y   I   R

And then read line by line: “PAHNAPLSIIGYIR”
Write the code that will take a string and make this conversion given a number of rows:

string convert(string text, int nRows);

convert(“PAYPALISHIRING”, 3) should return “PAHNAPLSIIGYIR”.

题意: 将一个字符串竖着排成一个之字形,然后按照行序输出。

思路:
1. 此题目很简单,肯定是O(n)时间复杂度,将列序转为行序的即可。关键是分析之字形,发现其行数比斜线的个数多2个。
2.
代码: 代码很简单,就是简单定义numRows个字符串,然后遍历输入字符串,逐个塞入到对应的行字符串中,首先处理一列numRows个字符,再处理斜线的numRows-2个字符。

string convert(string s, int numRows) {
    int i = 0;

    vector<string> vs(numRows);

    while(i < s.size()){
        for(int j = 0; j < numRows && i <s.size(); ++j){
            vs[j].push_back(s[i++]);
        }
        for(int j = numRows-2; j >=1 && i < s.size(); --j){
            vs[j].push_back(s[i++]);
        }
    }

    string res;
    for(auto i : vs){
        //printf("%s\n", i.c_str());
        res += i;
    }
    return res;
}

注意: 没啥可注意的,代码简单清晰。

7. Reverse Integer

Reverse digits of an integer.

Example1: x = 123, return 321
Example2: x = -123, return -321

题意: int变量的反转
思路:
1. 先转为字符串,再字符串反转
2. 逐个得出数,并进行乘10加即可。

代码:简单易懂,res= res*10 + c这个很常用,注意记忆。

int reverse(int x) {
    if(x == 0)
        return 0;
    int minus = 1;
    if(x < 0){
        minus = -1;
        x = -x;
    }

    long xx = x, res = 0;
    char c;
    while(xx){
         c = xx % 10;
         res = res * 10 + c;
         //printf("%d ", c);
         xx /= 10;
    }
    res *= minus;
    if(res > std::numeric_limits<int>::max() || res < std::numeric_limits<int>::min() )
        return 0;
    return res;
}

注意: int反转可能导致导致越界,此处我将x转换为long型的xx,再进行的处理,其实这个地方的做法有些多余,只需要将res定义为long类型,不需要xx就行。

不断追加中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值