LeetCode

来源:互联网 发布:华3交换机端口激活命令 编辑:程序博客网 时间:2024/05/22 06:29

前言

前一段时间在刷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   NA P L S I I GY   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 321Example2: 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就行。

不断追加中

0 0
原创粉丝点击