题解7-12

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz
思路:

我们先定义一个虚拟头节点dummy指向真正的头节点head,接下来定义两个指针leftright,我们假设总共 5 5 5个节点,返回倒数第 2 2 2个节点,我们先让right,往右走$n $步,原先为dummy,之后走到了2,接下来两个下标同时右移,当right移到了5的时候,left移到了3,我们接下来就把left->next删除掉,就是直接让left->next=left->next->next;具体看实现

/**
 * Definition for singly-linked list.
 * 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 *dummy = new ListNode(0, head);
        ListNode *left = dummy, *right = dummy;
        while(n --)
            right = right->next;
        //right先走了n步
        //当right走到了最后的时候,left走了m - n步,距离最后n
        while(right->next)
        {
            left = left->next;
            right = right->next;
        }
        left->next = left->next->next;
        return dummy->next;
    }
};

20. 有效的括号 - 力扣(LeetCode)

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

示例 3:

输入:s = "(]"
输出:false

提示:

  • 1 <= s.length <= 104
  • s 仅由括号 '()[]{}' 组成
思路:

看到括号我们就想到可以用栈来实现,当然实际上用栈也是可以用字符串来实现的,这道题目是括号匹配,我们每次碰到左括号就入栈,碰到右括号就看一下是否匹配,匹配就可以接着判断,不匹配就返回false,其中判断括号是否匹配可以使用一个特殊的技巧,我们知道相邻括号的ASCII编码就相差不到2括号的ASCII编码如下:

  • ‘(’ 的ASCII码为 40
  • ‘)’ 的ASCII码为 41
  • ‘[’ 的ASCII码为 91
  • ‘]’ 的ASCII码为 93
  • ‘{’ 的ASCII码为 123
  • ‘}’ 的ASCII码为 125
class Solution {
public:
    bool isValid(string s) {
        string stackStr;
        for(auto c : s)
        {
            if(c == '(' || c == '{' || c == '[')
                stackStr += c;
            else
            {
                if(stackStr.size() && abs(stackStr.back() - c) <= 2)
                    stackStr.pop_back();
                else
                    return false;
            }
        }
        return stackStr.empty();
    }
};
class Solution {
public:
    bool isValid(string s) {
        stack<char> st;
        for(auto c : s)
        {
            if(c == '(' || c == '{' || c == '[')
                st.push(c);
            else
            {
                if(st.size() && abs(st.top() - c) <= 2)
                    st.pop();
                else
                    return false;
            }
        }
        return st.empty();
    }
};

21. 合并两个有序链表 - 力扣(LeetCode)

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

https://assets.leetcode.com/uploads/2020/10/03/merge_ex1.jpg

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1l2 均按 非递减顺序 排列
思路:

这道题最好是不使用额外空间完成,其他的没什么需要注意的了

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        // 检查特殊情况
        if (!l1) return l2;
        if (!l2) return l1;
        
        // 确定新链表的头节点
        ListNode* head = nullptr;
        if (l1->val < l2->val) {
            head = l1;
            l1 = l1->next;
        } else {
            head = l2;
            l2 = l2->next;
        }
        
        // 合并链表
        ListNode* current = head;
        while (l1 && l2) {
            if (l1->val < l2->val) {
                current->next = l1;
                l1 = l1->next;
            } else {
                current->next = l2;
                l2 = l2->next;
            }
            current = current->next;
        }
        
        // 处理剩余部分
        if (l1) {
            current->next = l1;
        } else {
            current->next = l2;
        }
        
        return head;
    }
};

22. 括号生成 - 力扣(LeetCode)

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]

示例 2:

输入:n = 1
输出:["()"]

提示:

  • 1 <= n <= 8
思路:

注意到数据范围,肯定是使用dfs完成的,括号生成问题还有一个注意的点就是严格保证左括号>=右括号,接下来看代码实现

class Solution {
public:
    vector<string> res; // 存储有效括号组合的结果
    vector<string> generateParenthesis(int n) {
        dfs(n, 0, 0, ""); // 从初始状态开始深度优先搜索
        return res; // 返回结果
    }
    void dfs(int n, int l, int r, string seq)
    {
        // 当左右括号数都为n时,说明seq为一个有效的括号组合,存储到结果中
        if(l == n && r == n)
        {
            res.push_back(seq);
        }
        // 如果左括号数小于n,可以加一个左括号
        if(l < n)   dfs(n, l + 1, r, seq + '(');
        // 如果右括号数小于n,可以加一个右括号,但需要保证右括号数小于左括号数
        if(r < n && r < l)  dfs(n, l, r + 1, seq + ')');
    }
};

23. 合并 K 个升序链表 - 力扣(LeetCode)

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i]升序 排列
  • lists[i].length 的总和不超过 10^4
思路:

合并k个链表,我们想到有序元素存放需要堆,所以我们就定义一个堆

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    //定义一个比较函数,用于优先队列排序
    struct Cmp {
        bool operator() (ListNode* a, ListNode* b) {
            return a->val > b->val;
        }
    };

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        //定义一个小根堆,用于存放每个链表的头结点
        priority_queue<ListNode*, vector<ListNode*>, Cmp> heap;
        //定义一个虚拟头结点和尾结点
        auto dummy = new ListNode(-1), tail = dummy;
        //将每个链表的头结点加入小根堆
        for (auto l : lists) if (l) heap.push(l);
        while (heap.size()) {
            //取出堆顶元素
            auto t = heap.top();
            heap.pop();
            //将堆顶元素加入结果链表
            tail = tail->next = t;
            //如果堆顶元素还有下一个结点,则将其加入小根堆中
            if (t->next) heap.push(t->next);
        }
        //返回结果链表的头结点
        return dummy->next;
    }
};

31. 下一个排列 - 力扣(LeetCode)

整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。

  • 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3][1,3,2][3,1,2][2,3,1]

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

  • 例如,arr = [1,2,3] 的下一个排列是 [1,3,2]
  • 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2]
  • arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。

给你一个整数数组 nums ,找出 nums 的下一个排列。

必须** 原地 **修改,只允许使用额外常数空间。

示例 1:

输入:nums = [1,2,3]
输出:[1,3,2]

示例 2:

输入:nums = [3,2,1]
输出:[1,2,3]

示例 3:

输入:nums = [1,1,5]
输出:[1,5,1]

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 100
思路:
  1. 从数组末尾往前找,找到第一个位置j,使得 nums[j] < nums[j + 1]
    • 如果不存在这样的 j,则说明数组是不递增的,直接将数组逆转即可。
    • 如果存在这样的 j,则从末尾找到第一个位置 i > j,使得 nums[i] > nums[j]
  2. 交换 nums[i]nums[j],然后将数组从 j + 1 到末尾部分逆转。

时间复杂度:

  • 线性遍历数组常数次,时间复杂度为 O ( n ) O(n) O(n)

空间复杂度:

  • 没有使用任何额外的数组空间,故空间复杂度为 O ( 1 ) O(1) O(1)
class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int n = nums.size(), j = -1;
        for(int i = n - 2; i >= 0; i --)
        {
            if(nums[i] < nums[i + 1])
            {
                j = i;
                break;
            }
        }
        if(j == -1)
            reverse(nums.begin(), nums.end());
        else
        {
            for (int i = n - 1; i > j; i--)
                if (nums[i] > nums[j]) {
                    swap(nums[i], nums[j]);
                    break;
                }
            reverse(nums.begin() + j + 1, nums.end());
        }

    }
};
  • 33
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值