SamJ's blog

leetcode做题记录(2)

Word count: 3.1kReading time: 14 min
2019/11/10 Share

初级算法


字符串转换整数(atoi)

请你来实现一个 atoi 函数,使其能将字符串转换成整数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。

当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明:

假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例 1:

1
2
输入: "42"
输出: 42

示例 2:

1
2
3
4
输入: "   -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。

示例 3:

1
2
3
输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。

示例 4:

1
2
3
4
输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
因此无法执行有效的转换。

示例 5:

1
2
3
4
输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。

比较重要的操作是对字符串进行处理,获得想要的只含有数字和正负号的字符串。

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
36
37
38
39
40
41
42
43
44
class Solution {
public:
int myAtoi(string str) {
long res = 0;
int i = 0;
int flag = 1;
vector<char> resc;
while(str[i] == ' ')
{
++i;
}
if(str[i] == '-')
{
flag = -1;
++i;
}
else if(str[i] == '+')
{
++i;
}
else if(str[i] >= '0' && str[i] <= '9')
{}
else
{
return 0;
}
for(;i < str.size(); ++i)
{
if(str[i] >= '0' && str[i] <= '9')
resc.push_back(str[i]);
else
break;
}
for(i = 0; i < resc.size(); ++i)
{
res = res * 10 + (resc[i] - '0');
if(res > INT_MAX && flag == 1)
return INT_MAX;
if(flag == -1 && flag * res <= INT_MIN)
return INT_MIN;
}
return flag * res;
}
};

实现strStr()

先挖个坑,是用kmp实现的,想详细看一下然后写kmp的详细内容。

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Solution {
public:
void getNext(vector<int> &next,const string needle)
{
int lp=next.size();
int k=-1;
int j=0;
next[0]=-1;
while(j<lp-1)
{
if(k==-1||needle[j]==needle[k])
{
if(needle[++j]==needle[++k])
{
next[j]=next[k];
}
else
{
next[j]=k;
}
}
else
{
k=next[k];
}
}
}
int strStr(string haystack, string needle) {
int ls=haystack.length();
int lp=needle.length();
if(lp==0)
return 0;
int i=0;
int j=0;
vector<int> next(lp,0);
getNext(next,needle);
while(i<ls && j<lp)
{
if(j==-1 || haystack[i]==needle[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}
if(j>=lp)
return i-lp;
else
return -1;
}
};

报数

报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:

1
2
3
4
5
1.     1
2. 11
3. 21
4. 1211
5. 111221

1 被读作 "one 1" ("一个一") , 即 11
11 被读作 "two 1s" ("两个一"), 即 21
21 被读作 "one 2", “one 1""一个二" , "一个一") , 即 1211

给定一个正整数 n(1 ≤ n ≤ 30),输出报数序列的第 n 项。

注意:整数顺序将表示为一个字符串。

示例 1:

1
2
输入: 1
输出: "1"

示例 2:

1
2
输入: 4
输出: "1211"

主要就是找规律然后按照规律报下去直到达到要求的数字就行了。

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
36
37
38
39
40
41
42
43
class Solution {
public:
string countAndSay(int n) {
string res = "", temp = "1";
if (n == 1)
{
return temp;
}
temp = "11";
if(n == 2)
return temp;
char pre = temp[0];
int cnt = 1;
for (int i = 2; i < n; ++i)
{
for (int j = 1; j < temp.size(); ++j)
{
if (pre == temp[j])
{
++cnt;
}
else
{
res.push_back(char('0' + cnt));
res.push_back(pre);
cnt = 1;
}
pre = temp[j];
}
res.push_back(char('0' + cnt));
res.push_back(pre);
if (i < n - 1)
{
temp.clear();
temp = res;
pre = temp[0];
cnt = 1;
res.clear();
}
}
return res;
}
};

最长公共前缀

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

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

示例 1:

1
2
输入: ["flower","flow","flight"]
输出: "fl"

示例 2:

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

说明:

所有输入只包含小写字母 a-z

我的想法是先求第一个和第二个的最长公共前缀,再拿着这个前缀和后面的求,迭代下去。

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
36
37
class Solution {
public:

void Prefix2(string str1, string str2, string& res)
{
for(int i = 0; i < min(str1.size(), str2.size()); ++i)
{
if(str1[i] == str2[i])
{
res.push_back(str1[i]);
}
else
{
break;
}
}
}
string longestCommonPrefix(vector<string>& strs) {
string res = "";
if(strs.size() == 0||strs[0] == "")
return res;
if(strs.size() == 1)
return strs[0];
Prefix2(strs[0], strs[1], res);
if(res == "")
return res;
for(int i = 2; i < strs.size(); ++i)
{
string tmp = res;
res = "";
Prefix2(tmp, strs[i], res);
if(res == "")
return res;
}
return res;
}
};

删除链表中的节点

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。

现有一个链表 – head = [4,5,1,9],它可以表示为:

1

示例 1:

1
2
3
输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

示例 2:

1
2
3
输入: head = [4,5,1,9], node = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

说明:

  • 链表至少包含两个节点。
  • 链表中所有节点的值都是唯一的。
  • 给定的节点为非末尾节点并且一定是链表中的一个有效节点。
  • 不要从你的函数中返回任何结果。

给出的参数是要删除节点的指针,所以考虑如何删除这一节点。

方法是将下一节点的值覆盖到本节点的值上,并改变本节点的next指向下一个节点的next,最后释放本节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
void deleteNode(ListNode* node) {
ListNode *p = node->next;
node->val = p->val;
node->next = p->next;
delete p;
}
};

删除链表的倒数第N个节点

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

1
2
3
给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

一趟扫描是用双指针,一个指针先走n步,然后后一个指针再和先走的一块走,当先走的指针到达链表尾部时,后走的就到达倒数第n个节点,然后将其删除就ok。

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
36
37
38
39
40
41
42
43
44
45
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
int i = 0, flag = 0;
ListNode* cur = head;
ListNode* res = head;
ListNode* pre;
while(i < n - 1)
{
cur = cur->next;
++i;
}
while(cur->next != NULL)
{
pre = res;
res = res->next;
cur = cur->next;
}
if(res == head && res->next == NULL)
{
delete res;
return NULL;
}
else if(res == head)
{
res = res->next;
delete head;
return res;
}
else
{
pre->next = res->next;
delete res;
}
return head;
}
};

反转链表

反转一个单链表。

示例:

1
2
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

首先迭代,从第二个节点开始,逐个摘下来,并用头插法将其插到第一个节点前。

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
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == NULL)
return NULL;
if(head->next == NULL)
return head;
ListNode* pre = head->next;
ListNode* cur = pre->next;
head->next = NULL;
while(cur!= NULL)
{
pre->next = head;
head = pre;
pre = cur;
cur = cur->next;
}
pre->next = head;
head = pre;
return head;
}
};

递归实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head)
{
if(head == NULL || head->next ==NULL) //到了不能反转的情况
return head;
ListNode* newhead = reverseList(head->next);
head->next->next = head; //将后一个链表结点指向前一个结点
head->next = nullptr; //将原链表中前一个结点指向后一个结点的指向关系断开
return newhead;
}
};

合并两个有序链表

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

示例:

1
2
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

如果l1值小于l2值时,l1往后走一个,l1的后续节点是mergeTwoLists(l1->next, l2);

l2值小于l1值时同理。

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
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
//递归,很关键哦
if(l1==NULL)
return l2;
if(l2==NULL)
return l1;
if(l1->val < l2->val){
l1->next=mergeTwoLists(l1->next,l2);
return l1;
}
else{
l2->next=mergeTwoLists(l1,l2->next);
return l2;
}

}
};

回文链表

请判断一个链表是否为回文链表。

示例 1:

1
2
输入: 1->2
输出: false

示例 2:

1
2
输入: 1->2->2->1
输出: true

进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

用了最容易弄懂的解法,将后半跳链元素值入栈,最后一个一个比较。

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
36
37
38
39
40
41
42
43
44
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
if(head == NULL || head->next == NULL)
return true;
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
}
stack<int> is;
while(slow != NULL)
{
is.push(slow->val);
slow = slow->next;
}
while(!is.empty())
{
if(is.top() == head->val)
{
is.pop();
head = head->next;
}
else
{
break;
}
}
if(is.empty())
return true;
else
return false;
}
};

环形链表

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。

示例 1:

1
2
3
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

2

示例 2:

1
2
3
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

3

示例 3:

1
2
3
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

4

进阶:

你能用 O(1)(即,常量)内存解决此问题吗?

两个指针,一个一次走两步,一个一次走一步,能碰到就是有环,走到空就是无环。

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
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == NULL || head->next == NULL)
return false;
ListNode *fast = head;
ListNode *slow = head;
while(fast->next && slow && fast->next->next)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
return true;
}
return false;
}
};
CATALOG
  1. 1. 初级算法
    1. 1.1. 字符串转换整数(atoi)
    2. 1.2. 实现strStr()
    3. 1.3. 报数
    4. 1.4. 最长公共前缀
    5. 1.5. 删除链表中的节点
    6. 1.6. 删除链表的倒数第N个节点
    7. 1.7. 反转链表
    8. 1.8. 合并两个有序链表
    9. 1.9. 回文链表
    10. 1.10. 环形链表