力扣题解19-22

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

中等题
在这里插入图片描述
算是链表题打基础。
单链表是不能往回走的。如果数据结构里没有存储链表长度,那只能靠遍历来获得。一种想法就是先遍历获得链表长度,再遍历获得删除位置。需要遍历两次。第二种方法是遍历后存储,借助一个栈来把遍历到的节点存储后最后再弹出,不仅花费空间多在时间上也得经过入栈-出栈的过程。第三种是快慢指针,通过相距为n的指针同时运动,当前一个指针到达表尾,后一个指针正好到达需要删除的地方。这样只要进行一趟循环。当然,这相当于对复杂度O(n)前的常数进行修改,本质上并没有很大提升。

#include <iostream>

using namespace std;

struct ListNode {
      int val;
      ListNode *next;
      ListNode() : val(0), next(nullptr) {}
      ListNode(int x) : val(x), next(nullptr) {}
      ListNode(int x, ListNode *next) : val(x), next(next) {}
 };

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* front = head;
        ListNode* dummy = new ListNode(0, head);
        ListNode* back = dummy; //下一个才是head
        while(n--){
            front = front->next;
        }
        while(front){   
            front = front->next;
            back = back->next;
        }
        back->next = back->next->next;
        ListNode* res = dummy->next;
        return res;
    }
};

int main(){
    Solution st;
    ListNode* head = new ListNode(1);
    ListNode* cuv = new ListNode(2);
    head->next = cuv;
    cuv->next = new ListNode(3);
    cuv = cuv->next;
    cuv->next = new ListNode(4);
    cuv = cuv->next;
    cuv->next = new ListNode(5);
    cuv = cuv->next;
    cuv->next = nullptr;

    st.removeNthFromEnd(head,5);

    return 0;
}

这里为了不处理头指针的特殊情况(如果慢指针一开始就指向头指针,快指针会越界),而插入dumpy哑指针。这个dumpy本身的数据并不起作用,只是使得慢指针和快指针起初就相差1个单位。不过按照题解并没有释放删除的元素,难道这样不会内存泄漏吗?

20 有效的括号

简单题
在这里插入图片描述
括号匹配是经典的栈的应用。也许有其他形式很简单的代码写法,但是效率一定是栈是最高的(或者说那些写法也只不过是封装了栈的操作,例如python的某些函数)。
简单题,直接贴代码:

#include <iostream>
#include <stack>

using namespace std;

class Solution {
public:
    bool isValid(string s) {
        stack<char> stk;
        for(int i=0; i<s.size(); i++){
            //以下是字符串仍在匹配的情况讨论
            //情况一:栈不为空
            if(!stk.empty()){
                if((stk.top() == '(' && s[i] == ')') || (stk.top() == '{' && s[i] == '}') || (stk.top() == '[' && s[i] == ']')){
                    //栈顶元素和下一个字符匹配
                    stk.pop();
                }
                else{
                    if(s[i] == '(' || s[i] == '[' || s[i] == '{'){
                        //还能继续匹配
                        stk.push(s[i]);
                    }
                    else return false;
                }
            }
            //情况二:栈为空
            else{
                //还能继续匹配
                if(s[i] == '(' || s[i] == '[' || s[i] == '{') stk.push(s[i]);
                //匹配不了了
                else return false;
            }
        }
        //此时字符串已经匹配完了
        if(!stk.empty()) return false;
        else return true;
    }
};

int main(){
    Solution st;
    string s = "()";
    cout<<st.isValid(s)<<endl;
    return 0;
}

21 合并两个有序链表

简单题
在这里插入图片描述
对于链表的基本操作训练。即使不是链表也能使用迭代的方法,对表项进行逐个比对,插入正确的位置。每次需要对比的位置是<list1和list2未计入输出的下一个位置>,取其中小的节点接入输出。

#include <iostream>

using namespace std;

  struct ListNode {
      int val;
      ListNode *next;
      ListNode() : val(0), next(nullptr) {}
      ListNode(int x) : val(x), next(nullptr) {}
      ListNode(int x, ListNode *next) : val(x), next(next) {}
 };

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(!list1) return list2;
        if(!list2) return list1;
        ListNode* cuv1 = list1; //指向list1中未匹配的部分
        ListNode* cuv2 = list2; //指向list2中未匹配的部分
        ListNode* res = list1->val < list2->val ? list1 : list2; //指向小的一边,其实也确定了第一个输出节点
        ListNode* cur = res;
        if(cuv1->val < cuv2->val) cuv1 = cuv1->next;
        else cuv2 = cuv2->next; //确定了第一个输出节点,相应的要往后移动
        while(cuv1 && cuv2){
           if(cuv1->val < cuv2->val){
           		//cur结果指针指向cuv1,cuv1向后移动一位
                cur->next = cuv1;
                cur = cuv1;
                cuv1 = cuv1->next;
           }
           else{
                cur->next = cuv2;
                cur = cuv2;
                cuv2 = cuv2->next;
           }
        }
        //处理某个链表过剩的情况
        if(!cuv1) cur->next = cuv2;
        else cur->next = cuv1;
        return res;
    }
};

int main(){
    Solution st;
    ListNode* list1 = new ListNode(1,new ListNode(2,new ListNode(4)));
    ListNode* list2 = new ListNode(1,new ListNode(3,new ListNode(4)));

    st.mergeTwoLists(list1,list2);

    return 0;
}

以上是使用迭代的方法,时间复杂度为一次项即O(m+n)。链表操作往往拥有递归解法,因为链表具有递归的特性。按照递归的方式思考,本函数将两个升序链表输入,返回一个升序链表。注意:输出的并非一个全新的链表,而是通过连接已有的节点而形成的链表。这就允许使用递归了,因为递归主要就在于找一个递推。这里的递推条件很简单:

  1. list1的当前节点的值小于list2当前节点的值:说明list1当前节点的值是这两个链表中最小的,因此连接上这个节点作为输出。该节点的next指针指向除去该节点的两个链表进行合并操作得到的节点。
  2. list1的当前节点的值大于list2当前节点的值:连接list2节点作为输出,其余类似。
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if (l1 == nullptr) {
            return l2;
        } else if (l2 == nullptr) {
            return l1;
        } else if (l1->val < l2->val) {
            l1->next = mergeTwoLists(l1->next, l2);
            return l1;
        } else {
            l2->next = mergeTwoLists(l1, l2->next);
            return l2;
        }
    }
}

递归在空间复杂度上略逊于迭代。但是作为一种重要的思想,还是在这里列出了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值