所有的题型目录在下面的链接
LeetCode相关典型题解合集(两百多道题)
链表
一、160找出两个链表的交点
这道题目本质的思路是:走完自己的路,再走别人的路。两人走的总路程相同,遇到则返回,没遇到则返回null
当循环到第二轮的时候,两个链表若是没有相遇的节点,都会指向空节点,满足循环条件退出循环,因此不会形成死循环。
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
//本质上是走过自己的路,再走过对方的路
if(headA==NULL||headB==NULL){
return NULL;
}
ListNode * t1=headA;
ListNode *t2=headB;
while(headA!=headB){
if(headA==NULL){
headA=t2;
}
else{
headA=headA->next;
}
if(headB==NULL){
headB=t1;
}else{
headB=headB->next;
}
}
return headA;
}
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
二、206链表反转
有迭代和递归两种方法
迭代的思路就是三个节点,pre,cur和temp。pre和cur一次向后移,每次移动的时候都有cur.next->pre
递归就要熟悉递归的原理了
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//递归
ListNode *pre=nullptr;
ListNode *cur=head;
while(cur!=nullptr){
ListNode *temp=cur->next;
cur->next=pre;
pre=cur;
cur=temp;
}
return pre;
/**********************/
//递归
if(head==nullptr||head->next==nullptr){
return head;
}
ListNode *cur=reverseList(head->next);
head->next->next=head;
head->next=nullptr;
return cur;
}
};
三、21合并两个有序链表
分为递归和迭代两个方法。
递归自己好好体会
迭代是链表题中最经典的设置哨兵节点的方法,但是需要注意的是设置哨兵节点的时候尽量用哨兵节点->next=node这种表达,而不要用哨兵节点=node这种表达
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
/*********1、递归*************/
//只要有一个链表为空就可以停止,做两次判断是因为不知道那一个链表先为空
/*if(l1==nullptr){
return l2;
}
if(l2==nullptr){
return l1;
}
if(l1->val<l2->val){
l1->next=mergeTwoLists(l1->next,l2);
return l1; //这个return l1或者l2是因为全部都递归遍历了,返回头结点。
}else{
l2->next=mergeTwoLists(l1,l2->next);
return l2;
}*/
/*********2、迭代********************/
//思路:一般链表的题目都要考虑一个前置pre节点作为记录节点,这样会解决很多问题(哨兵节点)
ListNode *preHead=new ListNode(-1);
ListNode *pre=preHead;
while(l1&&l2){
if(l1->val<=l2->val){
pre->next=l1;
l1=l1->next;
}else{
pre->next=l2;
l2=l2->next;
}
pre=pre->next;
}
if(l1==nullptr){
pre->next=l2;
}
if(l2==nullptr){
pre->next=l1;
}
return preHead->next;
}
四、19删除链表的倒数第n个节点
双指针的经典应用
另一个思路是用栈来写
ListNode* removeNthFromEnd(ListNode* head, int n) {
//双指针思想,以后这种倒数的长度类型的题目都可以用双指针
ListNode *p=head;
ListNode *q=head;
int count=0;
while(count<n){
count++;
q=q->next;
}
//当链表中q节点为空时,即删除的元素等于链表的长度
//这时候其实是删除链表的第一个节点,所以返回head->next
if(!q){
return head->next;
}
while(q->next){
p=p->next;
q=q->next;
}
p->next=p->next->next;
return head;
}
五、24两两交换链表中的节点
一般涉及到交换和删除的节点操作,就要考虑设置一个前驱节点了
ListNode* swapPairs(ListNode* head) {
//迭代
ListNode *dummy=new ListNode(-1);
dummy->next=head;
ListNode *p=head;
ListNode *q=new ListNode(-1);
ListNode *temp=dummy;
//之所以不用q来判断是因为若是q本身为空,则q->next会判错
while(p&&p->next){
q=p->next;
p->next=p->next->next;
q->next=p;
temp->next=q;
temp=p;
p=p->next;
}
return dummy->next;
/************************/
//递归
if(!head||!head->next){
return head;
}
ListNode *temp=head->next;
head->next=swapPairs(temp->next);
temp->next=head;
return temp;
六、445两数相加
这个题的思路使用栈做,链表用头插法。
需要注意的点在于一种情况:链表A为[5],链表B为[5]这种情况
因此,下面相加操作循环中的count!=0至关重要,表示还要进位
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
stack<int> s1;
stack<int> s2;
ListNode *head=new ListNode(-1);
while(l1){
s1.push(l1->val);
l1=l1->next;
}
while(l2){
s2.push(l2->val);
l2=l2->next;
}
ListNode *temp1=head;
int count=0;
int sum=0;
int n1,n2;
while(!s1.empty()||!s2.empty()||count!=0){
//头插法
if(s1.empty()){
n1=0;
}else{
n1=s1.top();
s1.pop();
}
if(s2.empty()){
n2=0;
}else{
n2=s2.top();
s2.pop();
}
sum=n1+n2+count;
count=sum/10;
ListNode *temp2=new ListNode(sum%10);
temp2->next=temp1->next;
temp1->next=temp2;
}
return head->next;
}
七、234回文链表
递归有点难以理解,放个链接:递归解法
class Solution {
public:
bool isPalindrome(ListNode* head) {
/************快慢指针+反转列表(效率爆炸)*******************/
if(head==NULL||head->next==NULL){
return true;
}
ListNode *slow=head;
ListNode *fast=head;
while(fast!=NULL&&fast->next!=NULL){
slow=slow->next;
fast=fast->next->next;
}
ListNode *cout=slow;
ListNode *half=reverseNodeList(slow);
bool flag=true;
while(half!=NULL){
if(head->val!=half->val){
flag=false;
}
half=half->next;
head=head->next;
}
return flag;
/**************递归*************************/
}
ListNode *reverseNodeList(ListNode *node){
if(node->next==NULL){
return node;
}
ListNode *dummyHead=NULL;
ListNode *temp=NULL;
while(node){
temp=node->next;
node->next=dummyHead;
dummyHead=node;
node=temp;
}
return dummyHead;
}
};
八、725分隔链表
注意,用vector的时候不用考虑什么二维数组。
思路就是把链表断开
vector<ListNode*> splitListToParts(ListNode* root, int k) {
//1、计算链表的长度
int count=0;
ListNode *temp=root;
while(temp){
count++;
temp=temp->next;
}
//2、计算分割的数量
int length;
length=(count/k)<1?1:count/k;
int *arr=new int[k];
for(int i=0;i<k;i++){
arr[i]=length;
}
if(count>=k){
int remainder=count%k;
for(int i=0;i<remainder;i++){
arr[i]=arr[i]+1;
}
}
//3、添加到vector
vector<ListNode *> split;
ListNode *dummy=NULL;
for(int i=0;i<k;i++){
split.push_back(root);
for(int j=0;j<arr[i];j++){
if(root!=NULL){
dummy=root;
root=root->next;
}
}
if(dummy!=NULL){
dummy->next=NULL;
}
}
return split;
}
九、328奇偶链表
写的时候有个细节需要注意,就是当所有奇数序列链表和偶数序列链表已经完成,需要合并的时候
这个时候如果直接合并,会出现问题。
因为如果是12345这种,135和24
4这个节点后面一定要让其指向空,不然会出问题
ListNode* oddEvenList(ListNode* head) {
//判断head为空,只有一个节点,两个节点的情况
if(head==nullptr||head->next==nullptr){
return head;
}
ListNode *oddNode=head;
ListNode *evenNode=head->next;
ListNode *temp=evenNode->next;
ListNode *temp_evenNode=evenNode;
int count=1;
while(temp){
if((count%2)!=0){
//奇数序列
oddNode->next=temp;
oddNode=oddNode->next;
}else{
//偶数序列
temp_evenNode->next=temp;
temp_evenNode=temp_evenNode->next;
}
temp=temp->next;
count++;
}
/*********最重要的两行************/
temp_evenNode->next=nullptr;
oddNode->next=evenNode;
return head;
}