LeetCode刷题
栈
150.逆波兰表达式求值
根据逆波兰表示法,求表达式的值。
有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:整数除法只保留整数部分。给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
#include<stack>
#include<vector>
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> mystack;
for (auto& i : tokens) {
if (i == "*" || i == "+" || i == "-" || i == "/") {
resolve(mystack, i);
}
else {
mystack.push(atoi(i.c_str()));
};
}
return mystack.top();
}
void resolve(stack<int>& mystack, string opt) {
//注意:先入栈的为被减数或被除数。
int temp2 = mystack.top();
mystack.pop();
int temp1 = mystack.top();
mystack.pop();
switch (opt[0]) {
case '-': mystack.push(temp1 - temp2); break;
case '+': mystack.push(temp1 + temp2); break;
case '/':mystack.push(temp1 / temp2); break;
case '*':mystack.push(temp1 * temp2); break;
}
}
};
链表
21.合并2个有序列表(迭代+递归)
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
迭代的思想:
/**
* 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) {
ListNode *head=new ListNode(1);//定义头指针指向一个新创建的列表
ListNode *pt=head;//指向head
while(l1!=NULL &&l2!=NULL){
if(l1->val<l2->val){
pt->next=l1;
l1=l1->next;
}
else{
pt->next=l2;
l2=l2->next;
}
pt=pt->next;
}
if(l1==NULL){
pt->next=l2;
}
else{
pt->next=l1;
}
return head->next;
}
};
递归思想: 有点抽象难以理解,暂时未搞定。
/**
* 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;
}
}
};
203.移除链表元素
删除链表中等于给定值 val 的所有节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode *pt=head;
ListNode *pt1=pt;
while(pt!=NULL){
if(pt->val==val){
//检测删除点是不是在头部
if(pt==head){
head=pt->next;//head指向第一个节点后面的节点pt1指向head
pt1=head;
pt=pt->next;
}
//否则就是在其他部分
else{
pt1->next=pt->next;//pt1的next指向pt的后面一个节点
pt=pt->next;///pt1保持不动
}
}
//若不等则指向下一节点
else{
pt1=pt;
pt=pt->next;
}
}
return head;
}
};
147.对链表进行插入排序
对链表进行插入排序,有空再看看。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* insertionSortList(ListNode* head) {
if(head==NULL){
return head;
}
ListNode *p=head;//输入元素
ListNode *q=head;//找插入位置
ListNode *k=q;//记录q前一个指针,修改指针时使用
while(p->next!=NULL){
if(p->val <p->next->val){
p=p->next;
continue;
}
if(p->next->val <q->val){
ListNode *temp=p->next;//temp指向p后面一个节点
p->next=temp->next;//p节点的next储存temp里面的next信息
temp->next=q;//temp的next指向q节点
//插入值是否在开头
if(q==head){
head=temp;
}
else{
k->next=temp;
}
q=head;//从头检索
}
else{
if(q==p){
p=p->next;
continue;
}
else{
k=q;
q=q->next;
}
}
}
return head;
}
};
160.相交链表
编写一个程序,找到两个单链表相交的起始节点。
方法三:双指针法
创建两个指针 pApA 和 pBpB,分别初始化为链表 A 和 B 的头结点。然后让它们向后逐结点遍历。
当 pApA 到达链表的尾部时,将它重定位到链表 B 的头结点 (你没看错,就是链表 B); 类似的,当 pBpB 到达链表的尾部时,将它重定位到链表 A 的头结点。
若在某一时刻 pApA 和 pBpB 相遇,则 pApA/pBpB 为相交结点。
想弄清楚为什么这样可行, 可以考虑以下两个链表: A={1,3,5,7,9,11} 和 B={2,4,9,11},相交于结点 9。 由于 B.length (=4) < A.length (=6),pBpB 比 pApA 少经过 22 个结点,会先到达尾部。将 pBpB 重定向到 A 的头结点,pApA 重定向到 B 的头结点后,pBpB 要比 pApA 多走 2 个结点。因此,它们会同时到达交点。
如果两个链表存在相交,它们末尾的结点必然相同。因此当 pApA/pBpB 到达链表结尾时,记录下链表 A/B 对应的元素。若最后元素不相同,则两个链表不相交。
复杂度分析
时间复杂度 : O(m+n)O(m+n)。
空间复杂度 : O(1)O(1)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *pA=headA;
ListNode *pB=headB;
while(pA!=pB){
if(pA==NULL){
pA=headB;
}
else{
pA=pA->next;
}
if(pB==NULL){
pB=headA;
}
else{
pB=pB->next;
}
}
return pA;
}
};
方法一:暴力搜索法
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *pA=headA;
while(pA!=NULL){
ListNode *pB=headB;
while(pB!=NULL){
//相交节点值一样 next里面的值也一样
if(pA==pB){
return pA;
}
pB=pB->next;
}
pA=pA->next;
}
return NULL;
}
};
141.环形链表(哈希表未完成)
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
方法二:双指针
思路
想象一下,两名运动员以不同的速度在环形赛道上跑步会发生什么?
算法
通过使用具有 不同速度 的快、慢两个指针遍历链表,空间复杂度可以被降低至 O(1)O(1)。慢指针每次移动一步,而快指针每次移动两步。
如果列表中不存在环,最终快指针将会最先到达尾部,此时我们可以返回 false。
现在考虑一个环形链表,把慢指针和快指针想象成两个在环形赛道上跑步的运动员(分别称之为慢跑者与快跑者)。而快跑者最终一定会追上慢跑者。这是为什么呢?考虑下面这种情况(记作情况 A)- 假如快跑者只落后慢跑者一步,在下一次迭代中,它们就会分别跑了一步或两步并相遇。
其他情况又会怎样呢?例如,我们没有考虑快跑者在慢跑者之后两步或三步的情况。但其实不难想到,因为在下一次或者下下次迭代后,又会变成上面提到的情况 A。
/**
* 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) {
ListNode *pt1=head;
ListNode *pt2=head;
if (head==NULL){
return false;
}
while(pt2!=NULL && pt2->next!=NULL){
pt2=pt2->next->next;
pt1=pt1->next;
if(pt2==pt1){
return true;
}
}
return false;//pt2到达尾部,那就是直线环形没有终点
}
};
206.反转链表
反转一个单链表。
法一:迭代法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *cur=NULL;
ListNode *follow=head;
if(head==NULL){
return head;
}
while(follow!=NULL){
ListNode *temp=follow->next;
follow->next=cur;
cur=follow;
follow=temp;
}
return cur;
}
};
转载自:https://leetcode-cn.com/problems/reverse-linked-list/solution/fan-zhuan-lian-biao-shuang-zhi-zhen-di-gui-yao-mo-/
法二:递归法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//如果列表为空直接返回,如果head——>next为空返回head
if(head==NULL||head->next==NULL){
return head;
}
ListNode *ret=reverseList(head->next);//返回5,head->next=5,head就是4
head->next->next=head;//4->5->4;
head->next=NULL;//4->null ret=5->4
return ret;
}
};
234. 回文链表
请判断一个链表是否为回文链表。
思路:将链表的值放入数组中,然后在数组中采用双指针比较。
class Solution {
public:
bool isPalindrome(ListNode* head) {
if(head==NULL){
return true;
}
if(head->next==NULL){
return true;
}
vector <int > val;
ListNode *pt=head;
while(pt!=NULL){
val.push_back(pt->val);
pt=pt->next;
}
int m=0,n=val.size()-1;
while(m<n){
if(val[m]!=val[n]){
return false;
}
m++;
n--;
}
return true;
}
};
83. 删除排序链表中的重复元素
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
思路:这是一个简单的问题,仅测试你操作列表的结点指针的能力。由于输入的列表已排序,因此我们可以通过将结点的值与它之后的结点进行比较来确定它是否为重复结点。如果它是重复的,我们更改当前结点的 next 指针,以便它跳过下一个结点并直接指向下一个结点之后的结点。
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(head==NULL){
return head;
}
ListNode *pt=head;
while(pt->next!=NULL&&pt!=NULL){
if(pt->next->val==pt->val){
pt->next=pt->next->next;
}//注意if后面接else不是直接pt=pt->next因为还要判断pt后面的节点是否跟p值一样所以返回大循环继续判断下一节点
else{
pt=pt->next;
}
}
return head;
}
};
237.删除链表中的节点
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
现有一个链表 – head = [4,5,1,9],它可以表示为:
说明:
链表至少包含两个节点。
链表中所有节点的值都是唯一的。
给定的节点为非末尾节点并且一定是链表中的一个有效节点。
不要从你的函数中返回任何结果。
class Solution {
public:
void deleteNode(ListNode* node) {
node->val=node->next->val;
node->next=node->next->next;
}
};
876.链表的中间节点
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
方法一:输出到数组
思路和算法
按顺序将每个结点放入数组 A 中。然后中间结点就是 A[A.Length/2],因为我们可以通过索引检索每个结点。
自己的方法:
class Solution {
public:
ListNode* middleNode(ListNode* head) {
vector<int> myvector;
ListNode *pt=head;
while(pt!=NULL){
myvector.push_back(pt->val);
pt=pt->next;
}
pt=head;
int stage=myvector.size()/2;
while(stage){
pt=pt->next;
stage--;
}
return pt;
}
};
官方题解:
class Solution {
public:
ListNode* middleNode(ListNode* head) {
vector<ListNode*> A = {head};//保存head的节点信息至A[0]中,节点信息包括该节点地址和val和next的值(地址)
while (A.back()->next != NULL)//
A.push_back(A.back()->next);//存入每一个节点的地址
return A[A.size() / 2];//返回中间节点的地址
}
};
方法二:快慢指针法
思路和算法
当用慢指针 slow 遍历列表时,让另一个指针 fast 的速度是它的两倍。
当 fast 到达列表的末尾时,slow 必然位于中间。
class Solution {
public:
ListNode* middleNode(ListNode* head) {
ListNode *fast=head;
ListNode *slow=head;
while(fast!=NULL&& fast->next!=NULL){
fast=fast->next->next;//fast->next->next可以为NULL,此时fast->next为最后一个节点
slow=slow->next;
}
return slow;
}
};
1290.二进制链表转整数
给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。
请你返回该链表所表示数字的 十进制值 。
提示:
链表不为空。
链表的结点总数不超过 30。
每个结点的值不是 0 就是 1。
class Solution {
public:
int getDecimalValue(ListNode* head) {
ListNode *pt=head;
int count=0,sum=0;
while(pt!=NULL){
count++;
pt=pt->next;
}//count计算一共多少节点
pt=head;
while(pt!=NULL){
sum+=pt->val*pow(2,count-1);
count--;
pt=pt->next;
}
return sum;
}
};
官方题解:
class Solution {
public:
int getDecimalValue(ListNode* head) {
ListNode* cur = head;
int ans = 0;
while (cur != nullptr) {
ans = ans * 2 + cur->val;
cur = cur->next;
}
return ans;
}
};
143. 重排链表
给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
先把链表元素全部入栈,然后一边从头遍历,一边出栈。遍历一半、出栈一半。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
void reorderList(ListNode* head) {
stack<ListNode *>s;
ListNode *p=head;
while(p){
s.push(p);
p=p->next;
}
if(s.size()<=2){
return ;
}
p=head;
int size=s.size();
for(int i=0;i<size/2;i++){
ListNode *next=p->next;
p->next=s.top();
s.pop();
p=p->next;
p->next=next;
p=next;
}
p->next=NULL;
return ;
}
};
142. 环形链表 II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
方法一:set法
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL){
return NULL;
}
unordered_set<ListNode*> pset;
ListNode *p=head;
while(p){
if(pset.count(p)){
return p;
}
else{
pset.insert(p);
}
p=p->next;
}
return NULL;
}
};
方法二:快慢指针法参考。两次循环,第一次循环是快慢指针,若链表不是环形,则快指针先到表尾NULL,若是环形,快慢指针会相遇。相遇后将快慢指针之一置到表头head,然后开始第二次循环,此时快慢指针同速移动。当快慢指针再次相遇时到达链表开始入环的第一个节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL){
return NULL;
}
ListNode *slow=head;
ListNode *fast=head;
while(fast->next &&fast->next->next){
fast=fast->next->next;
slow=slow->next;
if(fast==slow){break;}
}
if(fast->next==NULL ||fast->next->next==NULL){
return NULL;
}
slow =head;
while(slow!=fast){
fast=fast->next;
slow=slow->next;
}
return slow;
}
};
138. 复制带随机指针的链表
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的 深拷贝。
我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
方法三:空间迭代
思路见:方法3:O(1)空间迭代
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
Node *p=head;
Node *temp;
if(head==NULL){return NULL;}
while(p){
//创建相邻结点
temp=p->next;
p->next=new Node(p->val);
p->next->next=temp;
p=p->next->next;
}
p=head;
while(p){
if(p->random!=NULL){p->next->random=p->random->next;}//修改新节点的random指针
else{p->next->random=NULL;}
p=p->next->next;
}
//修改链表结构解释见下面图片
Node *p_old_list=head;
Node *p_new_list=head->next;
Node *res=head->next;
while(p_new_list->next != NULL)
{
p_old_list->next=p_new_list->next;//
p_old_list=p_old_list->next;
p_new_list->next=p_old_list->next;
p_new_list=p_new_list->next;
}
p_old_list->next=NULL;
return res;
}
};
92. 反转链表 II
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
思路:3个指针,对于链表的问题,根据以往的经验一般都是要建一个dummy node,连上原链表的头结点,这样的话就算头结点变动了,我们还可以通过dummy->next来获得新链表的头结点。这道题的要求是只通过一次遍历完成,就拿题目中的例子来说,变换的是2,3,4这三个点,我们需要找到第一个开始变换结点的前一个结点,只要让pre向后走m-1步即可,为啥要减1呢,因为题目中是从1开始计数的,这里只走了1步,就是结点1,用pre指向它。万一是结点1开始变换的怎么办,这就是我们为啥要用dummy结点了,pre也可以指向dummy结点。然后就要开始交换了,由于一次只能交换两个结点,所以我们按如下的交换顺序:
1 -> 2 -> 3 -> 4 -> 5 -> NULL
1 -> 3 -> 2 -> 4 -> 5 -> NULL
1 -> 4 -> 3 -> 2 -> 5 -> NULL
我们可以看出来,总共需要n-m步即可,第一步是将结点3放到结点1的后面,第二步将结点4放到结点1的后面。这是很有规律的操作,那么我们就说一个就行了,比如刚开始,pre指向结点1,cur指向结点2,然后我们建立一个临时的结点t,指向结点3(注意我们用临时变量保存某个结点就是为了首先断开该结点和前面结点之间的联系,这可以当作一个规律记下来),然后我们断开结点2和结点3,将结点2的next连到结点4上,也就是 cur->next = t->next,再把结点3连到结点1的后面结点(即结点2)的前面,即 t->next = pre->next,最后再将原来的结点1和结点2的连接断开,将结点1连到结点3,即 pre->next = t。这样我们就完成了将结点3取出,加入结点1的后方。第二步将结点4取出,加入结点1的后方,也是同样的操作。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
if(head==NULL)return NULL;
ListNode *dummy= new ListNode (-1);
dummy->next=head;
ListNode *pre=dummy;
for(int i=0;i<m-1;i++){
pre=pre->next;
}
ListNode *cur=pre->next;
for(int i=m;i<n;i++){
ListNode *temp=cur->next;
cur->next=temp->next;
temp->next=pre->next;
pre->next=temp;
}
return dummy->next;
}
};
86. 分隔链表
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
思路: 参考思路
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
// before and after are the two pointers used to create the two list
// before_head and after_head are used to save the heads of the two lists.
// All of these are initialized with the dummy nodes created.
ListNode *before_head= new ListNode (0);
ListNode *before=before_head;
ListNode *after_head=new ListNode(0);
ListNode *after =after_head;
while(head!=NULL){
// If the original list node is lesser than the given x,
// assign it to the before list.
if(head->val<x){
before->next=head;
before=before->next;
}
else{
// If the original list node is greater or equal to the given x,
// assign it to the after list.
after->next=head;
after=after->next;
}
// move ahead in the original list
head=head->next;
}
// Last node of "after" list would also be ending node of the reformed list
after->next=NULL;
// Once all the nodes are correctly assigned to the two lists,
// combine them to form a single list which would be returned.
before->next=after_head->next;
return before_head->next;
}
};