我的刷题之路--链表

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

示例:

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

解法一:递归法
ListNode *mergeTwoLists(ListNode *l1, ListNode *l2)
{
if (l1 == nullptr)
{
return l2;
}
if (l2 == nullptr)
{
return l1;
}
if (l1->val < l2->val)
{
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
else
{
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}

解法二:迭代法

#include
using namespace std;

struct ListNode
{
int val;
struct ListNode *next;
ListNode(int x) //:val(x), next(NULL){}
{
this->val = x;
this->next = NULL;
}
};

ListNode *mergeTwoLists(ListNode *l1, ListNode *l2)
{
ListNode *head = new ListNode(-1); //哑节点简化代码
ListNode q = head;
while (l1 != nullptr && l2 != nullptr)
{
if (l1->val <= l2->val)
{
q->next = l1;
l1 = l1->next;
}
else
{
q->next = l2;
l2 = l2->next;
}
q = q->next;
}
if (l1)
q->next = l1;
else
{
q->next = l2;
}
Listnode
newHead=head->next;
delete head;
return newHead;

}
void test()
{
ListNode *p1 = new ListNode(1);
ListNode *p2 = new ListNode(2);
ListNode *p3 = new ListNode(4);
p1->next = p2;
p2->next = p3;

ListNode *p4 = new ListNode(1);
ListNode *p5 = new ListNode(3);
ListNode *p6 = new ListNode(4);
p4->next = p5;
p5->next = p6;

ListNode *phead = mergeTwoLists(p1, p4);

while (phead)
{
	cout << phead->val << " ";
	phead = phead->next;
}

}

int main()
{
test();
system(“pause”);
return 0;
}

160.相交链表
编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:

在节点 c1 开始相交。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
方法一: 暴力法
对链表A中的每一个结点 ai,遍历整个链表 B 并检查链表 B 中是否存在结点和 ai 相同。
时间复杂度O(m * n) 空间复杂度O(1)
class Solution {
public:
//返回相交的节点
ListNode *getIntersectionNode(ListNode headA, ListNode headB) {
ListNode cur_a = headA;//为什么需要缓存结点

while (cur_a)
{
    ListNode* cur_b = headB;
    while (cur_b)
    {
        if (cur_a == cur_b)
        {
            return cur_a;
        }
        cur_b = cur_b->next;
    }
    cur_a = cur_a->next;
}

return nullptr;

}
};

方法二:双指针法

A的指针遍历完A 接着从headB开始遍历
B的指针遍历完B 接着从headA开始遍历
两个指针都最多会运行m + n次,当都为空时,表示不相交;当相等时,表示相交
让pA和pB分别指向链表A和B的首地址。同时开始访问,一旦一个指针到达末尾,比如pA到达末尾,则将指针指向链表B的首地址,继续遍历,同理pB到达末尾,则将指针指向链表A的首地址,继续遍历。当pA==pB则返回。

这里相当于大家都一共要走A+B链表长度这么长。而如果有公共节点的话,后面那段肯定是公共的。当两个指针同时到达末尾,则没有公共节点。

例如:A=[1,3,5,7,9] B=[2,4,7,9]

则pB相当于要走[2,4,7,9,1,3,5,7,9] pA相当于要走[1,3,5,7,9,2,4,7,9] 注意有公共节点,7,9,也就是说后面一定会遇到!

class Solution {
public:

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {

if(headA == nullptr || headB == nullptr) 
    return nullptr;

ListNode* cur_a = headA;
ListNode* cur_b = headB;

while(cur_a != cur_b)
{
    cur_a = (cur_a == nullptr ? headB : cur_a->next);
    cur_b = (cur_b == nullptr ? headA : cur_b->next);

两种写法
cur1=cur1?cur1->next:headB;
cur2=cur2?cur2->next:headA;

}

return cur_a;

}
};
206.反转链表
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
1、用栈来解决问题
class Solution {
public:
ListNode* reverseList(ListNode* head) {
stack<ListNode*> stk;
ListNode* pt = head;//可以用auto代替ListNode*
while(pt)
{
stk.push(pt);
pt = pt->next;
}
ListNode* dummy = new ListNode(0);
pt = dummy;//不用pt也行 但需要再定义一个指针来缓存这个dummy 存疑
while(stk.size())//这里pt又指向了新的链表 让栈里元素弹出 赋值给pt
{
pt->next = stk.top();
stk.pop();
pt = pt->next;
}
pt->next = nullptr;//这步必须有
ListNode* newHead = dummy->next;
delete dummy;
return newHead;
}
};
2、头插法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* newHead = new ListNode(-1);
while (head != nullptr) {
ListNode* q = head->next;
head->next = newHead->next;//每次都让旧结点插入新头结点的下一个 这和栈的实现非常的相似 注意不能写q= newHead->next
newHead->next = head;
head = q;//这里不能直接写head=head->next 需要先将head->next缓存一下 因为需要让head指向其他地方
}
return newHead->next;
}
};

这么写错哪了//q->next 的值换了
ListNode* newHead = new ListNode(-1);
ListNode* q=head;
while(q)
{
q->next=newHead->next;//这里
newHead->next=q;
q=q->next;
}
return newHead->next;

  1. 删除排序链表中的重复元素?
    给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
    示例 1:
    输入: 1->1->2
    输出: 1->2
    1、双指针法
    class Solution {
    public:
    ListNode* deleteDuplicates(ListNode* head) {
    if(head){//不判断是否为空 提交会报错
    ListNode* cur = head->next, pre = head;
    while(cur){
    if(cur->val == pre->val){
    pre->next = cur->next;
    delete cur;
    cur = pre->next;
    }else{
    pre = cur;
    cur = cur->next;
    }
    }
    }
    return head;
    }
    };
    不明白返回的是head
    或者前面判断是否为空这么写if(headNULL||head->nextNULL)
    return head;
    2、递归法
    class Solution {
    public:
    ListNode
    deleteDuplicates(ListNode* head) {
    if(!head||!head->next)
    return head;
    head->next=deleteDuplicates(head->next);
    if(head->val==head->next->val) head=head->next;
    return head;
    }
    };
    递归套路解决链表问题:
    找终止条件:当head指向链表只剩一个元素的时候,自然是不可能重复的,因此return
    想想应该返回什么值:应该返回的自然是已经去重的链表的头节点
    每一步要做什么:宏观上考虑,此时head.next已经指向一个去重的链表了,而根据第二步,我应该返回一个去重的链表的头节点。因此这一步应该做的是判断当前的head和head.next是否相等,如果相等则说明重了,返回head.next,否则返回head
    237.删除链表中的节点
    请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。

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

示例 1:
输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
替身攻击
class Solution {
public:
void deleteNode(ListNode* node) {
node->val=node->next->val;
node->next=node->next->next;
}
};
19.删除链表的倒数第 n 个节点?
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
双指针
解题思路与官方所给方法相同,使用两个指针分别指向头节点和第n个节点,当后一个指针到达最后一个节点时,第一个指针正好位于倒数第n+1个节点处,删除第一个指针的下一个结点即可。
我的理解:倒数第n个结点,正数为总结点数-n+1,让一个指针指向第n个结点,另一个结点从头开始遍历,一起出发,第一个走完时,第二个指针指向总数-n即倒数第n+1个结点
代码
/**

  • 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) {
    ListNode* first = new ListNode(NULL);
    first->next = head; //添加头节点,便于操作
    ListNode* ptr = first; //搜索节点
    ListNode* current = first; //中间节点
    for (int i = 0; i < n; i++)//将该指针指第n个节点
    {
    current = current->next;
    }
    while (current->next != NULL) //到达最后一个节点
    {
    ptr = ptr->next;
    current = current->next;
    }
    ListNode* p = ptr->next;
    ptr->next = p->next; //删除ptr指向节点
    delete p; //释放空间
    return first->next;
    }
    两个问题:1、为什么delete的不是first new谁delete谁?
    2、为什么提交return head会报错
    24.两两交换链表中的结点
    给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
    你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
    示例:

给定 1->2->3->4, 你应该返回 2->1->4->3.
双指针
class Solution {
public:
ListNodeswapPairs(ListNodehead){
ListNode*dummHead=newListNode(0);
dummHead->next=head;

ListNodep=dummHead;
while(p->next&&p->next->next){
ListNode
node1=p->next;
ListNodenode2=node1->next;
ListNode
node3=node2->next;

p->next=node2;
node2->next=node1;
node1->next= node3;//顺序可颠倒

p=node1;//p=p->next但是p->next改变了所以用node1
}

ListNode*retNode=dummHead->next;
deletedummHead;//为了deletenew出来的结点所以多写两行

returnretNode;
}
};

445、两数相加
给定两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。

进阶:
如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。
示例:
输入: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出: 7 -> 8 -> 0 -> 7
两栈头插法
或者可以用三栈解决
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
if(!l1)
return l2;
if(!l2)
return l1;
stacks1,s2;
//把l1和l2放进栈里
while(l1)
{
s1.push(l1->val);
l1=l1->next;
}
while(l2)
{
s2.push(l2->val);
l2=l2->next;
}

    int carry = 0,n1 = 0,n2 = 0,sum = 0;
    ListNode* dummy=new ListNode(-1);

    while(!s1.empty() || !s2.empty() || carry)
    {
      if(s1.empty())n1 = 0;//这里必须要判断 因为while中或是全为假才跳出循环 即s1s2都为空 carry也为0
        else {n1 = s1.top();s1.pop();}
        
        if(s2.empty())n2 = 0;
        else{n2 = s2.top();s2.pop();}

        sum=n1+n2+carry;//求和与进位值

        ListNode* tmp=new ListNode(sum%10);//插入取余的值
        tmp->next=dummy->next;
        dummy->next=tmp;

        carry=sum/10;//处理进位 取整不要余数
    }

ListNode* newHead=dummy->next;
delete dummy;
return newHead;
}
};

234、回文链表
Vector数组 双指针
/**

  • 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(headNULL) return true;
    if(head->next
    NULL)return true;

     ListNode *i=head;
     vector<int> val;
     //将链表值放入数组
     while(i!=NULL){
         val.push_back(i->val);
         i=i->next;
     }
     int m=0,n=val.size()-1;
     while(m<n){
         if(val[m]!=val[n])
             return false;
         m++;
         n--;
     }
     return true;
    

    }
    };
    栈 一一出栈比较
    class Solution {
    public:
    bool isPalindrome(ListNode* head) {
    stack s;
    ListNode *p = head;
    while§{
    s.push(p->val);
    p = p->next;
    }
    p = head;
    while§{
    if(p->val != s.top()){
    return 0;
    }
    s.pop();
    p = p->next;
    }
    return 1;
    }
    };

  1. 移除链表元素
    /**
  • 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* newHead=new ListNode(-1);
    newHead->next=head;
    ListNode* p=newHead;
    while(p->next)
    {
    if(p->next->val==val)
    {
    p->next=p->next->next;
    }
    else
    p=p->next;
    }
    return newHead->next;
    }
    };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值