leetcode(C++)链表相关合并两个有序链表+删除中间节点+删除链表的倒数第n个节点+返回倒数第k个节点

1.合并两个有序链表

方法一:创建了一个新的链表

题目要求是将两个升序链表合并成一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。这道题目和合并两个有序数组的那道很像。这个代码在leetcode C++代码中提交无法通过,但是在codeblocks里是可以的,查了下是发生了内存错误,C++中不建议使用malloc。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
if(l1==NULL) return l2;
     if(l2==NULL) return l1;
     struct ListNode* p=NULL;
     struct ListNode* temp=(struct ListNode*)malloc(sizeof(struct ListNode));
     if(l1->val<=l2->val){
        temp->val=l1->val;
        temp->next=NULL;
        l1=l1->next;
     }
     else{
        temp->val=l2->val;
        temp->next=NULL;
        l2=l2->next;
     }
     p=temp;
     while(l1&&l2){
        if(l1->val<=l2->val){
            struct ListNode* a=(struct ListNode*)malloc(sizeof(struct ListNode));
            a->val=l1->val;
            a->next=NULL;
            temp->next=a;
            temp=temp->next;
            l1=l1->next;
        }
        else{
            struct ListNode* a=(struct ListNode*)malloc(sizeof(struct ListNode));
            a->val=l2->val;
            a->next=NULL;
            temp->next=a;
            temp=temp->next;
            l2=l2->next;
        }
     }
     if(l2==NULL) temp->next=l1;
     if(l1==NULL) temp->next=l2;
     return p;
}

方法二:递归

参考了官方题解和详解递归的题解。关于递归:函数在运行时调用自己,这个过程就叫作递归。关于边界情况:比如空链表,像上面的方法一样,两个链表中有一个为空,不需要任何合并操作,直接返回非空链表。否则,要判断哪个链表的头节点的值更小。如果两个链表有一个为空,递归结束。递归函数必须有终止条件否则会出错。此题目的终止条件是:如果两个链表中有一个为空。如何递归:较小节点的next指针指向其余节点的合并的结果。递归解法在执行用时上不如上一种解法。

ListNode* mergeTwoLists1(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(l2->next,l1);
            return l2;
     }
 }

方法三:迭代

其实最开始我想到的就是这种方法,但不知道设置一个哨兵节点,以便于在最后返回合并后的链表。另外还需要维护一个pre指针,pre最开始就是指向这个哨兵节点的。这里懂了之前说的头节点的存在有时是为了实际问题的好处理。但奇怪的是这种方法的执行效率和二差不多,不如一。

ListNode* mergeTwoLists2(ListNode* l1, ListNode* l2){
    ListNode* prehead=new ListNode;
    //struct ListNode* prehead=(struct ListNode*)malloc(sizeof(struct ListNode));
    prehead->val=-1;prehead->next=NULL;
    ListNode* pre=prehead;
    if(l1==NULL) return l2;
    if(l2==NULL) return l1;
    while(l1&&l2){
    if(l1->val<=l2->val){
        pre->next=l1;
        l1=l1->next;
        pre=pre->next;
    }
    else{
        pre->next=l2;
        l2=l2->next;
        pre=pre->next;
    }
    }
    if(l1!=NULL) pre->next=l1;
    if(l2!=NULL) pre->next=l2;
    return prehead->next;
}

2.删除中间节点

要求实现一种算法删除单向链表中间的某个节点,假设你只能访问该节点。原来题目给的并不是表头啊,如果给的是表头就可以使用双指针的方法。似乎只能这样来做,因为是单链表你不可能找的到这个节点的前驱节点。

void deleteNode(struct ListNode* node) {
    node->val=node->next->val;
    node->next=node->next->next;
}

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

懂了双指针这种思维方式以后这道题目就变得简单了。类似于上一题的方法三,需要设置哑结点或者叫哨兵节点,一般需要设置这样的节点是因为返回的节点不确定。这里的哑结点用来简化极端情况,比如链表只有一个节点或者需要删除链表的头部。下面的代码采用一次遍历的方法,两次遍历先确定链表的长度也是可以的。删除时需要单独设置一个指针指向被删除的节点,以防丢失。

struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
struct ListNode* dummy=(struct ListNode*)malloc(sizeof(struct ListNode));
    dummy->val=-1;dummy->next=head;
    struct ListNode* p=dummy,*q=dummy;
    for(int i=0;i<n+1;i++)
        p=p->next;
    while(p!=NULL){
        p=p->next;
        q=q->next;
    }
    struct ListNode* del=q->next;
    q->next=q->next->next;
    free(del);
    return dummy->next;
}

4.返回倒数第k个节点

和上一道题目类似。

int kthToLast(struct ListNode* head, int k){
struct ListNode* p=head,*q=head;
    for(int i=0;i<k;i++)
        p=p->next;
    while(p!=NULL){
        p=p->next;
        q=q->next;
    }
    return q->val;
}

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值